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
This commit is contained in:
179
docs/booking-status-flow-plan.md
Normal file
179
docs/booking-status-flow-plan.md
Normal file
@@ -0,0 +1,179 @@
|
||||
# Booking Status Flow — Planned Changes
|
||||
|
||||
## Current Flow (What We Have Now)
|
||||
|
||||
```
|
||||
new ──["Связались →"]──→ contacted ──["Подтвердить"]──→ confirmed
|
||||
──["Отказ"]──→ declined
|
||||
|
||||
confirmed ──["Вернуть"]──→ contacted
|
||||
declined ──["Вернуть"]──→ contacted
|
||||
```
|
||||
|
||||
### What buttons appear at each status:
|
||||
|
||||
| Status | Buttons shown |
|
||||
|-----------|------------------------------------|
|
||||
| new | "Связались →" |
|
||||
| contacted | "Подтвердить", "Отказ" |
|
||||
| confirmed | "Вернуть" |
|
||||
| declined | "Вернуть" |
|
||||
|
||||
---
|
||||
|
||||
## Problems Found
|
||||
|
||||
### 1. Can't confirm directly from `new`
|
||||
Someone calls, books, and confirms in one conversation.
|
||||
Admin must click "Связались" → wait → click "Подтвердить".
|
||||
Two clicks for one real-world action.
|
||||
|
||||
### 2. Can't decline from `new`
|
||||
Spam or invalid booking arrives.
|
||||
Admin must first mark "Связались" (lie) → then "Отказ".
|
||||
|
||||
### 3. Reminders miss non-confirmed group bookings
|
||||
SQL query in `db.ts` line 870:
|
||||
```sql
|
||||
WHERE status = 'confirmed' AND confirmed_date IN (?, ?)
|
||||
```
|
||||
If admin contacted someone and set a date but forgot to click "Подтвердить",
|
||||
that person **never shows up** in reminders tab.
|
||||
|
||||
MC registrations don't have this problem — they show by event date regardless of status.
|
||||
|
||||
### 4. Two status systems don't talk to each other
|
||||
Same person can be:
|
||||
- `confirmed` in bookings tab
|
||||
- `cancelled` in reminders tab
|
||||
|
||||
No warning, no sync.
|
||||
|
||||
### 5. "Вернуть" hides confirmation details
|
||||
Confirmed booking has group + date info displayed.
|
||||
"Вернуть" → status becomes `contacted` → info becomes invisible
|
||||
(still in DB, but UI only shows it when `status === "confirmed"`).
|
||||
Re-confirming requires filling everything again from scratch.
|
||||
|
||||
---
|
||||
|
||||
## Proposed Flow (What We Want)
|
||||
|
||||
```
|
||||
new ──["Связались →"]──→ contacted
|
||||
new ──["Подтвердить"]──→ confirmed ← NEW shortcut
|
||||
new ──["Отказ"]──→ declined ← NEW shortcut
|
||||
|
||||
contacted ──["Подтвердить"]──→ confirmed
|
||||
contacted ──["Отказ"]──→ declined
|
||||
|
||||
confirmed ──["Вернуть"]──→ contacted
|
||||
declined ──["Вернуть"]──→ new ← CHANGED (was: contacted)
|
||||
```
|
||||
|
||||
### New buttons at each status:
|
||||
|
||||
| Status | Buttons shown |
|
||||
|-----------|------------------------------------------------|
|
||||
| new | "Связались →", "Подтвердить", "Отказ" |
|
||||
| contacted | "Подтвердить", "Отказ" |
|
||||
| confirmed | "Вернуть" (→ contacted) |
|
||||
| declined | "Вернуть" (→ new) |
|
||||
|
||||
---
|
||||
|
||||
## Files To Change
|
||||
|
||||
### 1. `src/app/admin/bookings/BookingComponents.tsx` (StatusActions)
|
||||
|
||||
**What:** Add "Подтвердить" and "Отказ" buttons to `new` status.
|
||||
Change "Вернуть" from `declined` to go to `new` instead of `contacted`.
|
||||
|
||||
**Before:**
|
||||
```tsx
|
||||
{status === "new" && actionBtn("Связались →", ...)}
|
||||
{status === "contacted" && (
|
||||
actionBtn("Подтвердить", ...) + actionBtn("Отказ", ...)
|
||||
)}
|
||||
{(status === "confirmed" || status === "declined") && actionBtn("Вернуть", → "contacted")}
|
||||
```
|
||||
|
||||
**After:**
|
||||
```tsx
|
||||
{status === "new" && (
|
||||
actionBtn("Связались →", → "contacted")
|
||||
+ actionBtn("Подтвердить", → "confirmed")
|
||||
+ actionBtn("Отказ", → "declined")
|
||||
)}
|
||||
{status === "contacted" && (
|
||||
actionBtn("Подтвердить", → "confirmed")
|
||||
+ actionBtn("Отказ", → "declined")
|
||||
)}
|
||||
{status === "confirmed" && actionBtn("Вернуть", → "contacted")}
|
||||
{status === "declined" && actionBtn("Вернуть", → "new")}
|
||||
```
|
||||
|
||||
### 2. `src/app/admin/bookings/GenericBookingsList.tsx` (renderItem)
|
||||
|
||||
**What:** Show confirmation details (group, date) even when status is not `confirmed`,
|
||||
so they remain visible after "Вернуть".
|
||||
|
||||
**Before:** `renderExtra` only shows confirmed details when `b.status === "confirmed"`
|
||||
**After:** Show confirmed details whenever they exist, regardless of status
|
||||
(this is actually in `page.tsx` GroupBookingsTab `renderExtra` — line 291)
|
||||
|
||||
### 3. `src/app/admin/bookings/page.tsx` (GroupBookingsTab renderExtra)
|
||||
|
||||
**What:** Change condition from `b.status === "confirmed" && (b.confirmedGroup || ...)` to just `(b.confirmedGroup || b.confirmedDate)`.
|
||||
|
||||
**Before:**
|
||||
```tsx
|
||||
{b.status === "confirmed" && (b.confirmedGroup || b.confirmedDate) && (
|
||||
<span className="text-[10px] text-emerald-400/70">...</span>
|
||||
)}
|
||||
```
|
||||
|
||||
**After:**
|
||||
```tsx
|
||||
{(b.confirmedGroup || b.confirmedDate) && (
|
||||
<span className="text-[10px] text-emerald-400/70">...</span>
|
||||
)}
|
||||
```
|
||||
|
||||
### 4. `src/lib/db.ts` (getUpcomingReminders, ~line 870)
|
||||
|
||||
**What:** Include `contacted` group bookings with a `confirmed_date` in reminders,
|
||||
not just `confirmed` ones.
|
||||
|
||||
**Before:**
|
||||
```sql
|
||||
SELECT * FROM group_bookings WHERE status = 'confirmed' AND confirmed_date IN (?, ?)
|
||||
```
|
||||
|
||||
**After:**
|
||||
```sql
|
||||
SELECT * FROM group_bookings WHERE status IN ('confirmed', 'contacted') AND confirmed_date IN (?, ?)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Files NOT Changed
|
||||
|
||||
- `types.ts` — BookingStatus type stays the same (`new | contacted | confirmed | declined`)
|
||||
- `SearchBar.tsx` — No changes needed
|
||||
- `AddBookingModal.tsx` — No changes needed
|
||||
- `InlineNotes.tsx` — No changes needed
|
||||
- `McRegistrationsTab.tsx` — No changes needed (MC doesn't use ConfirmModal)
|
||||
- `OpenDayBookingsTab.tsx` — No changes needed
|
||||
- API routes — No changes needed (they already accept any valid status)
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
| Change | File | Risk |
|
||||
|--------|------|------|
|
||||
| Add confirm/decline buttons to `new` status | BookingComponents.tsx | Low — additive |
|
||||
| "Вернуть" from declined → `new` instead of `contacted` | BookingComponents.tsx | Low — minor behavior change |
|
||||
| Show confirmation details at any status | page.tsx (renderExtra) | Low — visual only |
|
||||
| Include `contacted` bookings in reminders | db.ts | Low — shows more data, not less |
|
||||
Reference in New Issue
Block a user