- 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
- 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
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.
Напоминания card now shows: не спрош. (gold), придёт (green),
не придёт (red) — same visual style as booking cards. Clickable
to navigate to reminders tab. Skipped "нет ответа" per user request.
- 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
- 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)
- 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
Confirming a booking for today or tomorrow auto-sets reminder_status
to 'coming'. Confirming for later dates leaves it unset — admin will
need to check closer to the event.
When a group booking is confirmed with a date, the reminder status
is automatically set to 'coming' — no need to manually mark it again
in the Reminders tab.
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.
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
- 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
- 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
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.
- 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
- #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
- 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
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>
- 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>
- 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>
- 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>
- 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>
- 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>
- 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>
- 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>
- 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>
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>
- 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>
- 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>
- 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>
- 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>
- 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>
- 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>