"use client"; import { useState, useMemo, useEffect } from "react"; import Image from "next/image"; import { Calendar, Clock, User, MapPin, Instagram, X } from "lucide-react"; import { SectionHeading } from "@/components/ui/SectionHeading"; import { Reveal } from "@/components/ui/Reveal"; import { SignupModal } from "@/components/ui/SignupModal"; import { useFocusTrap } from "@/hooks/useFocusTrap"; import type { SiteContent, MasterClassItem, MasterClassSlot, ScheduleLocation } from "@/types"; import { formatMarkup } from "@/lib/markup"; interface MasterClassesProps { data: SiteContent["masterClasses"]; regCounts?: Record; popups?: SiteContent["popups"]; locations?: ScheduleLocation[]; } const MONTHS_RU = [ "января", "февраля", "марта", "апреля", "мая", "июня", "июля", "августа", "сентября", "октября", "ноября", "декабря", ]; const WEEKDAYS_RU = [ "воскресенье", "понедельник", "вторник", "среда", "четверг", "пятница", "суббота", ]; function parseDate(iso: string) { return new Date(iso + "T00:00:00"); } function formatSlots(slots: MasterClassSlot[]): string { if (slots.length === 0) return ""; const sorted = [...slots].sort( (a, b) => parseDate(a.date).getTime() - parseDate(b.date).getTime() ); const dates = sorted.map((s) => parseDate(s.date)).filter((d) => !isNaN(d.getTime())); if (dates.length === 0) return ""; const timePart = sorted[0].startTime ? `, ${sorted[0].startTime}–${sorted[0].endTime}` : ""; if (dates.length === 1) { const d = dates[0]; return `${d.getDate()} ${MONTHS_RU[d.getMonth()]} (${WEEKDAYS_RU[d.getDay()]})${timePart}`; } const sameMonth = dates.every((d) => d.getMonth() === dates[0].getMonth()); const sameWeekday = dates.every((d) => d.getDay() === dates[0].getDay()); if (sameMonth) { const days = dates.map((d) => d.getDate()).join(" и "); const weekdayHint = sameWeekday ? ` (${WEEKDAYS_RU[dates[0].getDay()]})` : ""; return `${days} ${MONTHS_RU[dates[0].getMonth()]}${weekdayHint}${timePart}`; } const parts = dates.map((d) => `${d.getDate()} ${MONTHS_RU[d.getMonth()]}`); return parts.join(", ") + timePart; } function calcDuration(slot: MasterClassSlot): string { if (!slot.startTime || !slot.endTime) return ""; const [sh, sm] = slot.startTime.split(":").map(Number); const [eh, em] = slot.endTime.split(":").map(Number); const mins = (eh * 60 + em) - (sh * 60 + sm); if (mins <= 0) return ""; const h = Math.floor(mins / 60); const m = mins % 60; if (h > 0 && m > 0) return `${h} ч ${m} мин`; if (h > 0) return h === 1 ? "1 час" : h < 5 ? `${h} часа` : `${h} часов`; return `${m} мин`; } function isUpcoming(item: MasterClassItem): boolean { const now = new Date(); const slots = item.slots ?? []; // No dates = "coming soon", still show it if (slots.length === 0) return true; // Has dates with actual values const validSlots = slots.filter(s => s.date); if (validSlots.length === 0) return true; const earliestSlot = validSlots.reduce((min, s) => s.date < min.date ? s : min, validSlots[0]); const d = parseDate(earliestSlot.date); if (earliestSlot.startTime) { const [h, m] = earliestSlot.startTime.split(":").map(Number); d.setHours(h, m, 0, 0); } else { d.setHours(23, 59, 59, 999); } return d > now; } function formatFirstDate(slots: MasterClassSlot[]): string { const validSlots = (slots ?? []).filter(s => s.date); if (validSlots.length === 0) return "Скоро"; const first = validSlots.sort((a, b) => a.date.localeCompare(b.date))[0]; const d = parseDate(first.date); const day = d.getDate(); const month = MONTHS_RU[d.getMonth()]; const weekday = WEEKDAYS_RU[d.getDay()]; const time = first.startTime ? `, ${first.startTime}` : ""; return `${day} ${month} (${weekday})${time}`; } function MasterClassDetail({ item, locations, onClose, onSignup, }: { item: MasterClassItem; locations?: ScheduleLocation[]; onClose: () => void; onSignup: () => void; }) { const slots = item.slots ?? []; const duration = slots[0] ? calcDuration(slots[0]) : ""; const locAddress = locations?.find(l => l.name === item.location)?.address; const focusTrapRef = useFocusTrap(true); useEffect(() => { document.body.style.overflow = "hidden"; function handleKey(e: KeyboardEvent) { if (e.key === "Escape") onClose(); } window.addEventListener("keydown", handleKey); return () => { document.body.style.overflow = ""; window.removeEventListener("keydown", handleKey); }; }, [onClose]); return (
e.stopPropagation()} > {/* Content */}
{/* Top row: tags + price + close aligned */}
{item.style} {duration && ( {duration} )} {item.cost && ( {item.cost}{!item.cost.includes("BYN") ? " BYN" : ""} )}
{/* Title */}

{item.title}

{/* Trainer */} {/* Description */} {item.description && (
{formatMarkup(item.description)}
)} {/* All dates */}

Даты

{slots.length === 0 ? (

Скоро — дата уточняется

) : (
{slots.filter(s => s.date).sort((a, b) => a.date.localeCompare(b.date)).map((slot, i) => { const d = parseDate(slot.date); return (
{d.getDate()} {MONTHS_RU[d.getMonth()]} ({WEEKDAYS_RU[d.getDay()]}) {slot.startTime && ( {slot.startTime}–{slot.endTime} )}
); })}
)}
{/* Location */} {item.location && (
{item.location}{locAddress ? ` · ${locAddress}` : ""}
)} {/* Instagram */} {item.instagramUrl && ( Подробнее в Instagram )} {/* Signup button */}
); } function MasterClassCard({ item, currentRegs, onSignup, onDetail, locations, }: { item: MasterClassItem; currentRegs: number; onSignup: () => void; onDetail: () => void; locations?: ScheduleLocation[]; }) { const slots = item.slots ?? []; const duration = slots[0] ? calcDuration(slots[0]) : ""; const firstDate = formatFirstDate(slots); const maxP = item.maxParticipants ?? 0; const isFull = maxP > 0 && currentRegs >= maxP; return (
{ if (e.key === "Enter" || e.key === " ") { e.preventDefault(); onDetail(); } }} role="button" tabIndex={0} > {/* Full-bleed image or placeholder */}
{item.image ? ( <> {item.title} 1 ? `scale(${item.imageZoom})` : undefined, }} /> {/* No overlay — text shadow handles readability */} ) : (
)}
{/* Content overlay at bottom */}
{/* Tags row */}
{item.style} {duration && ( {duration} )}
{/* Title */}

{item.title}

{/* Trainer */} {/* Divider */}
{/* Date + Location */}
{firstDate}{slots.length > 1 && +{slots.length - 1}}
{item.location && (
{item.location} {(() => { const loc = locations?.find(l => l.name === item.location); return loc?.address ? ` · ${loc.address}` : ""; })()}
)}
{/* Price + Actions */}
{item.instagramUrl && ( )}
{/* Price tag — top right corner */} {item.cost && (
{item.cost}{!item.cost.includes("BYN") ? " BYN" : ""}
)}
); } export function MasterClasses({ data, regCounts = {}, popups, locations }: MasterClassesProps) { if (!data?.items?.length) return null; const [signupTitle, setSignupTitle] = useState(null); const [detailItem, setDetailItem] = useState(null); const upcoming = useMemo(() => { return data.items .filter((item) => item.title && isUpcoming(item)) .sort((a, b) => { const aFirst = parseDate((a.slots ?? [])[0]?.date ?? "9999"); const bFirst = parseDate((b.slots ?? [])[0]?.date ?? "9999"); return aFirst.getTime() - bFirst.getTime(); }); }, [data.items]); return (
{data.title} {upcoming.length === 0 ? (

Следите за анонсами мастер-классов в нашем{" "} Instagram

) : (
{upcoming.map((item, idx) => ( setSignupTitle(item.title)} onDetail={() => setDetailItem(item)} locations={locations} /> ))}
)}
{/* Detail popup */} {detailItem && ( setDetailItem(null)} onSignup={() => { setDetailItem(null); setSignupTitle(detailItem.title); }} /> )} setSignupTitle(null)} subtitle={signupTitle ?? ""} endpoint="/api/master-class-register" extraBody={{ masterClassTitle: signupTitle }} successMessage={popups?.successMessage} waitingMessage={popups?.waitingListText} errorMessage={popups?.errorMessage} instagramHint={popups?.instagramHint} />
); }