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>
This commit is contained in:
80
src/app/admin/bookings/McRegistrationsTab.tsx
Normal file
80
src/app/admin/bookings/McRegistrationsTab.tsx
Normal file
@@ -0,0 +1,80 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect, useMemo } from "react";
|
||||
import { adminFetch } from "@/lib/csrf";
|
||||
import { type BaseBooking, type BookingGroup } from "./types";
|
||||
import { LoadingSpinner } from "./BookingComponents";
|
||||
import { GenericBookingsList } from "./GenericBookingsList";
|
||||
|
||||
interface McRegistration extends BaseBooking {
|
||||
masterClassTitle: string;
|
||||
}
|
||||
|
||||
interface McSlot { date: string; startTime: string }
|
||||
interface McItem { title: string; slots: McSlot[] }
|
||||
|
||||
export function McRegistrationsTab() {
|
||||
const [regs, setRegs] = useState<McRegistration[]>([]);
|
||||
const [mcDates, setMcDates] = useState<Record<string, string>>({});
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
Promise.all([
|
||||
adminFetch("/api/admin/mc-registrations").then((r) => r.json()),
|
||||
adminFetch("/api/admin/sections/masterClasses").then((r) => r.json()),
|
||||
]).then(([regData, mcData]: [McRegistration[], { items?: McItem[] }]) => {
|
||||
setRegs(regData);
|
||||
const dates: Record<string, string> = {};
|
||||
const mcItems = mcData.items || [];
|
||||
for (const mc of mcItems) {
|
||||
const latestSlot = mc.slots?.reduce((latest, s) => s.date > latest ? s.date : latest, "");
|
||||
if (latestSlot) dates[mc.title] = latestSlot;
|
||||
}
|
||||
const regTitles = new Set(regData.map((r) => r.masterClassTitle));
|
||||
for (const regTitle of regTitles) {
|
||||
if (dates[regTitle]) continue;
|
||||
for (const mc of mcItems) {
|
||||
const latestSlot = mc.slots?.reduce((latest, s) => s.date > latest ? s.date : latest, "");
|
||||
if (!latestSlot) continue;
|
||||
if (regTitle.toLowerCase().includes(mc.title.toLowerCase()) || mc.title.toLowerCase().includes(regTitle.toLowerCase())) {
|
||||
dates[regTitle] = latestSlot;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
setMcDates(dates);
|
||||
}).catch(() => {}).finally(() => setLoading(false));
|
||||
}, []);
|
||||
|
||||
const today = new Date().toISOString().split("T")[0];
|
||||
|
||||
const groups = useMemo((): BookingGroup<McRegistration>[] => {
|
||||
const map: Record<string, McRegistration[]> = {};
|
||||
for (const r of regs) {
|
||||
if (!map[r.masterClassTitle]) map[r.masterClassTitle] = [];
|
||||
map[r.masterClassTitle].push(r);
|
||||
}
|
||||
return Object.entries(map).map(([title, items]) => {
|
||||
const date = mcDates[title];
|
||||
const isArchived = !date || date < today;
|
||||
return {
|
||||
key: title,
|
||||
label: title,
|
||||
dateBadge: date ? new Date(date + "T12:00").toLocaleDateString("ru-RU", { day: "numeric", month: "short" }) : undefined,
|
||||
items,
|
||||
isArchived,
|
||||
};
|
||||
});
|
||||
}, [regs, mcDates, today]);
|
||||
|
||||
if (loading) return <LoadingSpinner />;
|
||||
|
||||
return (
|
||||
<GenericBookingsList<McRegistration>
|
||||
items={regs}
|
||||
endpoint="/api/admin/mc-registrations"
|
||||
onItemsChange={setRegs}
|
||||
groups={groups}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user