Commit Graph

95 Commits

Author SHA1 Message Date
b251ee5138 feat: phone input mask +375 (XX) XXX-XX-XX in manual booking modal 2026-03-25 00:18:54 +03:00
5e93c9a746 feat: auto-scroll and highlight newly added items in all array editors 2026-03-25 00:07:54 +03:00
540d3a9297 fix: dashboard cards no longer disappear on refresh — use prop trigger instead of key remount 2026-03-25 00:00:55 +03:00
259b31a722 feat: admin-editable success + waiting list messages for MC and Open Day 2026-03-24 23:52:07 +03:00
0c39bdba5e refactor: compact searchable SelectField for Open Day, removed time field from slot editor 2026-03-24 23:34:16 +03:00
1047b71abe feat: auto-focus and scroll to newly created class in Open Day schedule 2026-03-24 23:11:53 +03:00
d0fad4aae5 feat: discount toggle flag, hide price from success popup
- Admin: "Добавить скидку" toggle — OFF hides discount fields and sets to 0
- Public: discount line hidden when discountPrice=0
- Success popup: removed price/booking count info
2026-03-24 23:07:06 +03:00
eb6ec5aeb6 feat: toast popup for save status — 'Сохранено' or error, no layout jump 2026-03-24 22:55:13 +03:00
8ecebe686c fix: remove all 'Сохранение...' text, show toast popup on save instead 2026-03-24 22:44:22 +03:00
0ae8f57cde fix: empty participant fields show red error, no save until filled 2026-03-24 22:34:12 +03:00
b22e4c5d39 fix: participant limits — red error when max < min, no save on invalid, shows 0 2026-03-24 22:27:38 +03:00
d08905ee93 feat: min/max participants — shared ParticipantLimits component
- New ParticipantLimits component in FormField.tsx (reusable)
- Used in both Open Day settings and MC editor — identical layout
- Open Day: event-level min/max (DB migration 15)
- MC: per-event min/max (JSON fields)
- Public: waiting list when full, spots counter, amber success modal
2026-03-24 22:11:10 +03:00
4acc88c1ab feat: Open Day class duration + max participants, UI fixes
- Class duration: editable end time (was hardcoded +1h), shown as range
- Max participants per class (0 = unlimited), shown as "3/10 чел."
- New DB migration 14: max_participants column on open_day_classes
- Min bookings moved to separate row with hint text
- "Скидка" renamed to "Цена со скидкой" for clarity
- Cancel/recover icon: Ban for cancel, RotateCcw for recover
2026-03-24 19:45:31 +03:00
353484af2e refactor: Open Day schedule — hall selector instead of full grid
Replace wide multi-column hall grid with hall selector tabs + single
column time slots. Each hall tab shows class count badge. Scales
better as more halls are added.
2026-03-24 19:34:02 +03:00
2693491fee refactor: remove booking section from Open Day admin (managed in bookings page) 2026-03-24 19:31:08 +03:00
8343374969 fix: BLACK HEART header links to /admin dashboard, not public site 2026-03-24 19:20:38 +03:00
4dabca52ee feat: BLACK HEART header text links to main site 2026-03-24 19:19:21 +03:00
afe9f7012c fix: reminders card — single line layout matching other cards, cleanup unused prop 2026-03-24 19:12:01 +03:00
87ba4d232a feat: reminder status counts in dashboard card
Напоминания card now shows: не спрош. (gold), придёт (green),
не придёт (red) — same visual style as booking cards. Clickable
to navigate to reminders tab. Skipped "нет ответа" per user request.
2026-03-24 19:08:37 +03:00
a95676ea6a fix: dashboard status counts always set filter (no toggle), hover underline
- Clicking a status count always sets that filter, even when navigating
  to a different tab (previously toggled off if same filter was active)
- Click card background to reset filter to 'all'
- Hover underline on status count links
2026-03-24 19:00:12 +03:00
67d8f6330c refactor: dashboard cards with clickable status counts + tab bar restored
- Dashboard cards show all 4 statuses inline: new (gold), contacted (blue),
  confirmed (green), declined (red) — big numbers with consistent status colors
- Each number+label is clickable to filter the tab by that status
- Tab bar restored below dashboard
- Removed filter chips from SearchBar (dashboard handles filtering)
- Open Day card uses cyan border (distinct from blue contacted status)
2026-03-24 18:56:39 +03:00
745d72f36d fix: date validation, pre-fill on re-edit, MS_PER_DAY constant
- Extract MS_PER_DAY constant to lib/constants.ts
- ConfirmModal date: max=1 year, rejects past dates and malformed years
- ConfirmModal pre-fills existing date + group when re-editing (✎)
- Confirmed date display handles malformed dates gracefully
- Red border + error for invalid dates, submit disabled
2026-03-24 18:26:28 +03:00
aa07b64c80 fix: dashboard counters refresh after status changes
When a booking status is changed, confirmed, or deleted in any tab,
the dashboard summary cards re-fetch to show updated counts. Previously
the dashboard was stale until page reload.
2026-03-24 17:52:21 +03:00
83456c6e9d fix: unique key prefix to avoid React duplicate key warning 2026-03-24 17:42:32 +03:00
42be063c7f fix: auto-refresh bookings silently instead of showing update banner
Polling detects new bookings and silently re-fetches tab data + dashboard
counts. No more "press to update" banner — data appears automatically.
2026-03-24 17:41:08 +03:00
18c11d0611 fix: MC series uses earliest slot date for registration cutoff
Multi-session master classes are a series — once the first session
passes, the group has started and registration closes. Changed all
MC date logic from "latest slot" / "any future slot" to "earliest slot":

- DashboardSummary: upcoming = earliest slot >= today
- McRegistrationsTab: archive = earliest slot < today
- AddBookingModal: only show MCs where earliest slot >= today
- Public MasterClasses: isUpcoming checks earliest slot
2026-03-24 17:35:31 +03:00
b48cc040e1 fix: search and status filter work together consistently
- Status filter chips stay visible during text search
- Search results filtered by selected status (search + filter = AND)
- Shows "Нет записей по фильтру" when search has results but filter excludes all
2026-03-24 17:23:16 +03:00
49d710b2e7 refactor: move status filter from per-tab pills to global search bar
- Remove FilterTabs from inside each booking tab
- Add compact status chips (Все/Новая/Связались/Подтверждено/Отказ)
  below the search bar — one global filter for all tabs
- Filter chips hidden during active text search
- Status filter toggles on click (click again to deselect)
- GenericBookingsList accepts filter as prop instead of managing internally
2026-03-24 17:15:47 +03:00
057f1ff1ee fix: MC tab shows 'all archived' instead of misleading filter counts
When all booking groups are archived (e.g. past MC events), the filter
pills no longer show 'Новая 5' etc. Instead shows 'Все записи в архиве'
with archive auto-expanded. Filter counts now exclude archived items.
2026-03-24 16:54:47 +03:00
2c64951cb3 fix: 4 bugs from regression testing
- BUG-1: Strip HTML tags in sanitizeName (prevent stored XSS)
- BUG-2: Strip HTML tags in notes via sanitizeText across all 3 booking APIs
- BUG-3: Dashboard excludes archived/past MCs and expired Open Day events from counts
- BUG-4: Truncate long names in booking cards to prevent overflow
2026-03-24 16:43:19 +03:00
aa0cfe35c3 fix: comprehensive bookings admin UX improvements
- #1 Delete confirmation dialog before removing bookings
- #2 Error toasts instead of silent .catch(() => {})
- #3 Optimistic rollback — UI reverts on API failure
- #4 Loading indicator on reminder status buttons
- #5 Search results are now actionable (status change + delete)
- #6 New bookings banner instead of full tab remount
- #7 Error states for failed data loads
- #8 InlineNotes only saves on blur when value changed
- #9 AddBookingModal supports Instagram/Telegram fields
- #10 Polling pauses when browser tab is hidden
- #11 Enter key submits ConfirmModal
2026-03-24 15:54:22 +03:00
669c4a3023 fix: reminders include contacted bookings, confirmation details persist after status revert
- Reminders query now includes 'contacted' group bookings with confirmed_date,
  preventing people from being forgotten when admin hasn't clicked "Подтвердить"
- Confirmation details (group, date) remain visible regardless of booking status,
  so "Вернуть" no longer hides previously entered info
2026-03-24 15:26:05 +03:00
c87c63bc4f feat: booking panel upgrade — refactor, notes, search, manual add, polling
Phase 1 — Refactor:
- Split monolith _shared.tsx into types.ts, BookingComponents, InlineNotes,
  GenericBookingsList, AddBookingModal, SearchBar (no more _ prefix)
- All 3 tabs use GenericBookingsList — shared status workflow, filters, archive

Phase 2 — Features:
- DB migration 13: add notes column to all booking tables
- Inline notes with amber highlight, auto-save 800ms debounce
- Confirm modal comment saves to notes field
- Manual add: 2 tabs (Занятие / Мероприятие), filters expired MCs, Open Day support
- Search bar: cross-table search by name/phone
- 10s polling for real-time updates (bookings page + sidebar badge)
- Status change marks booking as seen (fixes unread count on reset)
- Confirm modal stores human-readable group label instead of raw groupId
- Confirmed group bookings appear in Reminders tab

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 13:34:16 +03:00
87f488e2c1 feat: dashboard summary on bookings page, archive expired MC and Open Day
- Dashboard cards show new/pending counts per tab, click to navigate
- MC tab: expired master classes (past date or deleted) move to collapsible archive
- Open Day tab: past events move to archive section
- Date badges on MC group headers (gold active, strikethrough archived)
- Fix MC content API key (masterClasses not master-classes)
- Fuzzy title matching for MC registration → content date lookup

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 19:13:49 +03:00
b906216317 feat: add status workflow to MC and Open Day bookings, refactor into separate files
- DB migration v12: add status column to mc_registrations and open_day_bookings
- MC and Open Day tabs now have full status workflow (new → contacted → confirmed/declined)
- Filter tabs with counts, status badges, action buttons matching group bookings
- Extract shared components (_shared.tsx): FilterTabs, StatusBadge, StatusActions, BookingCard, ContactLinks
- Split monolith into _McRegistrationsTab.tsx, _OpenDayBookingsTab.tsx, _shared.tsx

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 19:05:44 +03:00
c3cbd90fe4 feat: hero video management — diagonal split, mobile single video, admin editor
- Hero: diagonal 3-video split on desktop, single center video on mobile (CSS breakpoint)
- Videos render client-side only to avoid hydration mismatch
- Loading overlay (z-5) hides buffering without blocking text content
- Admin hero editor: 3 fixed slots (left/center/right) with upload, preview, remove
- Center slot marked as main (used on mobile), save blocked until all 3 filled
- Upload API: supports MP4/WebM (50MB) in hero folder alongside existing image uploads
- Added videos field to hero type and fallback data

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 16:24:29 +03:00
650f8dc719 feat: add short description for team carousel cards
- Add shortDescription field to TeamMember type
- DB migration #11: add short_description column to team_members
- Admin editor: separate "Краткое описание (для карточки)" and "Полное описание"
- Carousel shows shortDescription with line-clamp-3, falls back to description
- Full description shown in "Подробнее" profile view

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 14:08:50 +03:00
96e3333e9f feat: floating contact bar, remove pricing contact links, fix admin hooks
- Add FloatingContact bar (Записаться + Instagram) visible while scrolling
- Hides on hero and near contact section, centered at bottom
- Move BackToTop button up to avoid overlap
- Remove redundant ContactHint from Pricing section (floating bar covers it)
- Fix React hooks order violation in AdminLayout (early return before useEffect)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 13:35:36 +03:00
1bfd502930 feat: booking confirm modal with cascading Hall → Trainer → Group, linear workflow
- Add confirmed_date, confirmed_group, confirmed_comment to group_bookings (migration #10)
- Linear booking flow: Новая → Связались → Подтвердить (modal) / Отказ
- Confirm modal with cascading selects: Hall → Trainer → Group → Date → Comment
- Groups merged by groupId — shows all days/times (e.g. "СР 20:00, ПТ 16:15")
- Auto-prefill hall/trainer/group from booking's groupInfo via fuzzy scoring
- Proper SHORT_DAYS constant for weekday abbreviations
- Filter chips with status counts, declined sorted to bottom
- "Вернуть" on confirmed/declined returns to "Связались" (not "Новая")

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 01:03:48 +03:00
8d1e3fb596 fix: reopen booking returns to "Связались" instead of "Новая"
Admin already contacted the person, so reopened bookings skip "Новая" state.
Renamed button from "Сбросить" to "Вернуть".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 19:25:42 +03:00
0ec2361a16 feat: add linear booking workflow — Новая → Связались → Подтверждено/Отказ
- Add status + confirmed_date columns to group_bookings (migration #10)
- Linear flow: Новая shows "Связались →", Связались shows date picker + "Отказ"
- Date picker for confirmation allows only today and future dates
- Confirmed bookings show the scheduled date
- Filter chips: Все / Новая / Связались / Подтверждено / Отказ with counts
- Declined bookings sorted to bottom of list
- "Сбросить" button on confirmed/declined to restart flow

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 17:14:51 +03:00
e4a9b71bfe feat: upgrade reminders tab — group by event, status tags, amber "Нет ответа"
- Group reminders by event within each day (e.g. "Master class · 16:00")
- Stats (придёт/не придёт/нет ответа/не спрошены) shown per event, not per day
- People separated by status with colored tag labels for easy scanning
- "Нет ответа" now amber when active (was neutral gray, confused with unselected)
- Cancelled people faded (opacity-50)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 16:32:16 +03:00
e617660467 fix: clean up admin bookings — add all contact fields, remove redundant NotifyToggle/filters
- Add phone to MC registrations display, instagram/telegram to group bookings
- Remove NotifyToggle from all 3 tabs (handled by Reminders tab)
- Remove FilterChips (Новые/Без напоминания) — redundant with Reminders tab
- Remove unused urgencyMap, mcItems fetch, MasterClassSlot/MasterClassItem types
- Fix delete button layout (pinned right, doesn't wrap)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 16:04:47 +03:00
9e0aa5b5dc fix: LOW priority — GPU hints, CSRF cleanup, redundant query removal, mobile perf
- Add will-change to .hero-glow-orb (filter, transform) and .team-card-glitter::before (background-position)
- Clear CSRF cookie on logout alongside auth cookie
- Add max array length (100) validation on team reorder endpoint
- Remove redundant isOpenDayClassBookedByPhone pre-check (DB UNIQUE constraint handles it)
- Extract Schedule grid layout calculation into useMemo
- Reduce HeroLogo sparkle animations on mobile (15 → 8 via hidden sm:block)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 14:28:35 +03:00
5cd23473c8 fix: MEDIUM — Cache-Control headers on admin GETs, Open Day past date validation
- Add Cache-Control headers to admin GET endpoints (sections 60s, team 60s, bookings 30s, unread 15s, open-day 60s)
- Validate Open Day date is not in the past on both create (POST) and update (PUT)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 14:19:39 +03:00
b1adbbfe3d fix: MEDIUM priority — shared validation, content caching, Schedule useReducer, stable keys
- Extract shared sanitization to src/lib/validation.ts, apply to all 3 registration routes (#2)
- Replace key={index} with stable keys in About and News (#4)
- Add 5-min in-memory content cache in content.ts, invalidate on admin section save (#6)
- Refactor Schedule from 8 useState calls to useReducer — single dispatch, fewer re-renders (#8)
- Remove Hero scroll indicator, add auto-scroll to next section on wheel/swipe

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 14:17:24 +03:00
66dce3f8f5 fix: HIGH priority — scroll debounce, timing-safe auth, a11y, error logging, cleanup dead modals
- Header: throttle scroll handler via requestAnimationFrame (was firing 60+/sec)
- Auth: use crypto.timingSafeEqual for password and token signature comparison
- A11y: add role="dialog", aria-modal, aria-label to all modals (SignupModal, NewsModal, TeamProfile lightbox)
- A11y: add aria-label to close buttons, menu toggle (with aria-expanded), floating CTA
- A11y: add aria-label to MC Instagram buttons
- Error logging: add console.error with route names to all API catch blocks (admin + public)
- Fix open-day-register error leak (was returning raw DB error to client)
- Fix MasterClasses key={index} → key={item.title}
- Delete 3 unused modal components (BookingModal, MasterClassSignupModal, OpenDaySignupModal) — replaced by unified SignupModal

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 14:01:21 +03:00
127990e532 fix: critical perf & security — rate limiting, DB indexes, N+1 query, image lazy loading
- Add in-memory rate limiter (src/lib/rateLimit.ts) to public registration endpoints
- Add DB migration #9 with 8 performance indexes on booking/registration tables
- Fix N+1 query in getUpcomingReminders() — single IN() query instead of per-title
- Add loading="lazy" to all non-hero images (MasterClasses, News, Classes, Team)
- Add sizes attribute to Classes images for better responsive loading

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 13:55:49 +03:00
4e766d6957 feat: add reminders tab with status tracking (coming/pending/cancelled)
Auto-surfaces bookings for today and tomorrow. Admin sets status per
person: coming, no answer, or cancelled. Summary stats per day.
DB migration 8 adds reminder_status column to all booking tables.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 13:07:00 +03:00
b94ee69033 feat: add booking management, Open Day, unified signup modal
- MC registrations: notification toggles (confirm/remind) with urgency
- Group bookings: save to DB from BookingModal, admin CRUD at /admin/bookings
- Open Day: full event system with schedule grid (halls × time), per-class
  booking, discount pricing (30 BYN / 20 BYN from 3+), auto-cancel threshold
- Unified SignupModal replaces 3 separate forms — consistent fields
  (name, phone, instagram, telegram), Instagram DM fallback on network error
- Centralized /admin/bookings page with 3 tabs (classes, MC, Open Day),
  collapsible sections, notification toggles, filter chips
- Unread booking badge on sidebar + dashboard widget with per-type breakdown
- Pricing: contact hint (Instagram/Telegram/phone) on price & rental tabs,
  admin toggle to show/hide
- DB migrations 5-7: group_bookings table, open_day tables, unified fields

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 12:58:04 +03:00