"use client"; import { useState, useEffect } from "react"; import { createPortal } from "react-dom"; import { X } from "lucide-react"; import { adminFetch } from "@/lib/csrf"; type Tab = "classes" | "events"; type EventType = "master-class" | "open-day"; interface McOption { title: string; date: string } interface OdClass { id: number; style: string; time: string; hall: string } interface OdEvent { id: number; date: string; title?: string } export function AddBookingModal({ open, onClose, onAdded, }: { open: boolean; onClose: () => void; onAdded: () => void; }) { const [tab, setTab] = useState("classes"); const [eventType, setEventType] = useState("master-class"); const [name, setName] = useState(""); const [phone, setPhone] = useState(""); const [instagram, setInstagram] = useState(""); const [telegram, setTelegram] = useState(""); const [mcTitle, setMcTitle] = useState(""); const [mcOptions, setMcOptions] = useState([]); const [odClasses, setOdClasses] = useState([]); const [odEventId, setOdEventId] = useState(null); const [odClassId, setOdClassId] = useState(""); const [saving, setSaving] = useState(false); useEffect(() => { if (!open) return; setName(""); setPhone(""); setInstagram(""); setTelegram(""); setMcTitle(""); setOdClassId(""); // Fetch upcoming MCs (filter out expired) adminFetch("/api/admin/sections/masterClasses").then((r) => r.json()).then((data: { items?: { title: string; slots: { date: string }[] }[] }) => { const today = new Date().toISOString().split("T")[0]; const upcoming = (data.items || []) .filter((mc) => { const earliest = mc.slots?.reduce((min, s) => s.date < min ? s.date : min, mc.slots[0]?.date ?? ""); return earliest && earliest >= today; }) .map((mc) => ({ title: mc.title, date: mc.slots.reduce((min, s) => s.date < min ? s.date : min, mc.slots[0]?.date ?? ""), })); setMcOptions(upcoming); if (upcoming.length === 0 && tab === "events") setEventType("open-day"); }).catch(() => {}); // Fetch active Open Day event + classes adminFetch("/api/admin/open-day").then((r) => r.json()).then(async (events: OdEvent[]) => { const today = new Date().toISOString().split("T")[0]; const active = events.find((e) => e.date >= today); if (!active) { setOdEventId(null); setOdClasses([]); return; } setOdEventId(active.id); const classes = await adminFetch(`/api/admin/open-day/classes?eventId=${active.id}`).then((r) => r.json()); setOdClasses(classes); }).catch(() => {}); // eslint-disable-next-line react-hooks/exhaustive-deps }, [open]); useEffect(() => { if (!open) return; function onKey(e: KeyboardEvent) { if (e.key === "Escape") onClose(); } document.addEventListener("keydown", onKey); return () => document.removeEventListener("keydown", onKey); }, [open, onClose]); const hasUpcomingMc = mcOptions.length > 0; const hasOpenDay = odEventId !== null && odClasses.length > 0; const hasEvents = hasUpcomingMc || hasOpenDay; async function handleSubmit() { if (!name.trim() || !phone.trim()) return; setSaving(true); try { if (tab === "classes") { await adminFetch("/api/admin/group-bookings", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: name.trim(), phone: phone.trim(), ...(instagram.trim() && { instagram: instagram.trim() }), ...(telegram.trim() && { telegram: telegram.trim() }), }), }); } else if (eventType === "master-class") { const title = mcTitle || mcOptions[0]?.title || "Мастер-класс"; await adminFetch("/api/admin/mc-registrations", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ masterClassTitle: title, name: name.trim(), instagram: "-", phone: phone.trim() }), }); } else if (eventType === "open-day" && odClassId && odEventId) { await adminFetch("/api/open-day-register", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ classId: Number(odClassId), eventId: odEventId, name: name.trim(), phone: phone.trim() }), }); } onAdded(); onClose(); } finally { setSaving(false); } } if (!open) return null; const inputClass = "w-full rounded-lg border border-white/[0.08] bg-white/[0.04] px-3 py-2 text-sm text-white outline-none focus:border-gold/40 placeholder-neutral-500"; const tabBtn = (key: Tab, label: string, disabled?: boolean) => ( ); const canSubmit = name.trim() && phone.trim() && !saving && (tab === "classes" || (tab === "events" && eventType === "master-class" && hasUpcomingMc) || (tab === "events" && eventType === "open-day" && odClassId)); return createPortal(
e.stopPropagation()}>

Добавить запись

Ручная запись (Instagram, звонок, лично)

{/* Tab: Classes vs Events */}
{tabBtn("classes", "Занятие")} {tabBtn("events", "Мероприятие", !hasEvents)}
{/* Events sub-selector */} {tab === "events" && (
{hasUpcomingMc && ( )} {hasOpenDay && ( )}
)} {/* MC selector */} {tab === "events" && eventType === "master-class" && mcOptions.length > 0 && ( )} {/* Open Day class selector */} {tab === "events" && eventType === "open-day" && odClasses.length > 0 && ( )} setName(e.target.value)} placeholder="Имя" className={inputClass} /> setPhone(e.target.value)} placeholder="Телефон" className={inputClass} />
setInstagram(e.target.value)} placeholder="Instagram" className={inputClass} /> setTelegram(e.target.value)} placeholder="Telegram" className={inputClass} />
, document.body ); }