"use client"; import { useState, useEffect, useMemo } from "react"; import { ChevronDown, ChevronRight } from "lucide-react"; import { adminFetch } from "@/lib/csrf"; import { type BookingStatus, type BookingFilter, LoadingSpinner, EmptyState, DeleteBtn, ContactLinks, FilterTabs, StatusBadge, StatusActions, BookingCard, fmtDate, countStatuses, sortByStatus, } from "./_shared"; interface OpenDayBooking { id: number; classId: number; eventId: number; name: string; phone: string; instagram?: string; telegram?: string; status: BookingStatus; createdAt: string; classStyle?: string; classTrainer?: string; classTime?: string; classHall?: string; } export function OpenDayBookingsTab() { const [bookings, setBookings] = useState([]); const [loading, setLoading] = useState(true); const [filter, setFilter] = useState("all"); useEffect(() => { adminFetch("/api/admin/open-day") .then((r) => r.json()) .then((events: { id: number; date: string }[]) => { if (events.length === 0) { setLoading(false); return; } const ev = events[0]; return adminFetch(`/api/admin/open-day/bookings?eventId=${ev.id}`) .then((r) => r.json()) .then((data: OpenDayBooking[]) => setBookings(data)); }) .catch(() => {}) .finally(() => setLoading(false)); }, []); const counts = useMemo(() => countStatuses(bookings), [bookings]); const grouped = useMemo(() => { const filtered = filter === "all" ? bookings : bookings.filter((b) => b.status === filter); const map: Record = {}; for (const b of filtered) { const key = `${b.classHall}|${b.classTime}|${b.classStyle}`; if (!map[key]) map[key] = { hall: b.classHall || "—", time: b.classTime || "—", style: b.classStyle || "—", trainer: b.classTrainer || "—", items: [] }; map[key].items.push(b); } for (const g of Object.values(map)) { const sorted = sortByStatus(g.items); g.items.length = 0; g.items.push(...sorted); } return Object.entries(map).sort(([, a], [, b]) => { const hallCmp = a.hall.localeCompare(b.hall); return hallCmp !== 0 ? hallCmp : a.time.localeCompare(b.time); }); }, [bookings, filter]); const [expanded, setExpanded] = useState>({}); async function handleStatus(id: number, status: BookingStatus) { setBookings((prev) => prev.map((b) => b.id === id ? { ...b, status } : b)); await adminFetch("/api/admin/open-day/bookings", { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ action: "set-status", id, status }), }); } async function handleDelete(id: number) { await adminFetch(`/api/admin/open-day/bookings?id=${id}`, { method: "DELETE" }); setBookings((prev) => prev.filter((b) => b.id !== id)); } if (loading) return ; return (
{grouped.length === 0 && } {grouped.map(([key, group]) => { const isOpen = expanded[key] ?? true; const groupCounts = countStatuses(group.items); return (
{isOpen && (
{group.items.map((b) => (
{b.name}
{fmtDate(b.createdAt)} handleDelete(b.id)} />
handleStatus(b.id, s)} />
))}
)}
); })}
); }