Commit Graph

105 Commits

Author SHA1 Message Date
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
575c684cc5 fix: mobile menu — expand to fit all nav links, center align, shrink CTA button
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 16:55:11 +03:00
4721043530 feat: redesign floating contact as vertical icon stack, reposition back-to-top
- Floating contact: bottom-left expandable stack (phone, Instagram, signup modal)
- Mail icon toggle button, closes on outside click
- Back-to-top: bottom-right, same size (h-14 w-14) for visual balance

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 16:38:36 +03:00
8b5ed3c627 feat: add Главная and День открытых дверей nav links, match page section order
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 16:28:56 +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
1c6462d340 feat: hero diagonal split with 3 dancer videos
- Three video panels with diagonal clip-paths and gold separator lines
- Each video centered in its own column for clear visibility
- Replaced nastya.mp4 with nastya-2.mp4

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 15:37:00 +03:00
b0c9a77474 fix: filter past MC events by start time, fix no-image card collapse, center cards
- isUpcoming() now checks startTime, not just date — past events hide after they start
- Cards without images get a gradient placeholder instead of collapsing to 0 height
- Merge two Reveal wrappers into one to prevent empty Reveal stuck at opacity 0
- Use flex layout with justify-center so single cards are centered

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 18:07:41 +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
1acda847b7 fix: clamp team member bio to 3 lines in carousel to prevent layout shift
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 13:49:04 +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
3458f88367 fix: remove floating booking button overlapping mobile menu, center Open Day heading
- Remove floating "Записаться" button that covered hamburger menu on mobile scroll
- Booking still accessible via mobile menu dropdown and Hero CTA
- Center Open Day section heading to match all other sections

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 14:35:29 +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
e63b902081 feat: remove scroll indicator, add auto-scroll from hero to next section
- Remove SCROLL chevron button from hero (not needed)
- Add wheel/swipe listener that smoothly scrolls to the first section below hero
- Works on desktop (wheel) and mobile (touch swipe)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 14:08:30 +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
7497ede2fd fix: auto-issue CSRF cookie for existing sessions
Sessions from before CSRF was added lack the bh-csrf-token cookie,
causing 403 on first save. Middleware now auto-generates the cookie
if the user is authenticated but missing it.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 17:57:49 +03:00
6cbdba2197 feat: add CSRF protection for admin API routes
Double-submit cookie pattern: login sets bh-csrf-token cookie,
proxy.ts validates X-CSRF-Token header on POST/PUT/DELETE to /api/admin/*.
New adminFetch() helper in src/lib/csrf.ts auto-includes the header.
All admin pages migrated from fetch() to adminFetch().

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 17:53:02 +03:00
3ac6a4d840 fix: security hardening, UI fixes, and validation improvements
- Fix header nav overflow by switching to lg: breakpoint with tighter gaps
- Fix file upload path traversal by whitelisting allowed folders and extensions
- Fix BookingModal using hardcoded content instead of DB-backed data
- Add input length validation on public master-class registration API
- Add ID validation on team member and reorder API routes
- Fix BookingModal useCallback missing groupInfo/contact dependencies
- Improve admin news date field to use native date picker
- Add missing Мастер-классы and Новости cards to admin dashboard

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 17:37:29 +03:00
26cb9a9772 feat: redesign news & master classes sections, migrate middleware to proxy
- News: magazine layout with featured hero article + compact list, click-to-open modal
- Master classes: fashion lookbook portrait cards with full-bleed images and overlay content
- Rename middleware.ts to proxy.ts (Next.js 16 convention)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 18:49:13 +03:00
4a1a2d7512 docs: update CLAUDE.md to reflect Next.js 16 upgrade
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 18:25:46 +03:00
b9800c1cc2 feat: add news section with admin editor and public display
- NewsItem type with title, text, date, optional image and link
- Admin page at /admin/news with image upload and auto-date
- Public section between Pricing and FAQ, hidden when empty
- Nav link auto-hides when no news items exist

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 23:19:03 +03:00
f29dbe0c9f fix: use groupId for trainer bio schedule groups
Group extraction now uses groupId (from admin panel) instead of
type+time heuristic, with mergeSlotsByDay for proper day/time display.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 22:54:15 +03:00
340a1d2f7f feat: multi-popular toggle for pricing, BYN price field for master classes
- Replace single popular dropdown with per-item toggle switch in pricing admin
- Add PriceField component to master classes admin (strips/adds BYN suffix)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 20:03:41 +03:00
f5e80c792a feat: add groupId support and redesign schedule GroupView hierarchy
- Add groupId field to ScheduleClass for admin-defined group identity
- Add versioned DB migration system (replaces initTables) to prevent data loss
- Redesign GroupView: Trainer → Class Type → Group → Datetimes hierarchy
- Group datetimes by day, merge days with identical time sets
- Auto-assign groupIds to legacy schedule entries in admin
- Add mc_registrations CRUD to db.ts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 19:55:34 +03:00
84b0bc4d60 feat: add master classes section with registration system
- New master classes section on landing page with upcoming events grid
- Admin CRUD for master classes (image, slots, trainer, style, cost, location)
- User signup modal (name + Instagram required, Telegram optional)
- Admin registration management: view, add, edit, delete with quick-contact links
- Customizable success message for signup confirmation
- Auto-filter past events, Russian date formatting, duration auto-calculation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 18:29:06 +03:00
6981376171 feat: add Записаться button to group cards with pre-filled Instagram DM
- BookingModal now accepts optional groupInfo for pre-filled message
- Trainer profile: each group card has Записаться button
- Schedule group view: each group card has Записаться button
- Message includes class type, trainer, days, and time

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 16:45:27 +03:00
4f92057411 feat: show trainer's groups on profile from schedule data
- Extract classes from schedule matching trainer name
- Group by type+time+location, combine days (e.g. ПН, СР)
- Display as horizontal scroll cards with time, location, level
- Show recruiting badge and address (without city prefix)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 16:35:17 +03:00
ce033074cd feat: horizontal drag-scroll for all bio sections, fix tab resize
- Replace wrapping grid with horizontal ScrollRow (drag to scroll)
- Apply to victories, education, and experience sections
- Grid overlay for victory tabs so height stays stable across tabs
- Fixed-width cards (w-44/w-48) with items-stretch for uniform height
- Remove scrollbar, add grab cursor for drag interaction

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 16:19:12 +03:00
d4751975d2 feat: upgrade bio panel design, clickable carousel cards, Escape navigation
- Widen layout (max-w-5xl), larger photo column
- Fix place badge: clean pill instead of clipped diamond
- Increase victory card text sizes for readability
- Cards fill available width instead of fixed size
- Move back button above photo
- Add Escape key: closes lightbox or returns to gallery
- Clicking inactive carousel photos scrolls to them

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 15:40:06 +03:00
1b391cdde6 feat: split victories into tabbed sections (place/nomination/judge)
Add type field to VictoryItem, tabbed UI in trainer profile,
and type dropdown in admin form.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 16:31:00 +03:00
d3bb43af80 feat: replace free-text place input with dropdown select in victories
Predefined options: 1-6 место, Финалист, Полуфиналист, Лауреат,
Номинант, Участник, Победитель, Гран-при, Best Show, Vice Champion,
Champion — with emoji indicators for top places and nominations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 15:58:27 +03:00
5030edd0d6 fix: compact victory cards, fix date picker overflow, improve city autocomplete
- Merge place/category/competition into single row for compact layout
- Inline date range picker (no wrapper div causing overflow)
- Remove restrictive Nominatim filter — show all location results
- Reduce padding/gaps across all bio fields for denser layout

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 15:47:52 +03:00
627781027b feat: upgrade team admin with click-to-edit, Instagram validation, date picker, city autocomplete
- Team list: click card to open editor (remove pencil button), keep drag-to-reorder
- Instagram field: username-only input with @ prefix, async account validation via HEAD request
- Victory dates: date range picker replacing text input, auto-formats to DD.MM.YYYY / DD-DD.MM.YYYY
- Victory location: city autocomplete via Nominatim API with suggestions dropdown
- Links: real-time URL validation with error indicators on all link fields
- Save button blocked when any validation errors exist

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 15:34:55 +03:00