import { useState, useEffect, useRef, useCallback } from "react"; import Image from "next/image"; import { ArrowLeft, Instagram, Trophy, GraduationCap, ExternalLink, X, Award, Scale, Clock, MapPin } from "lucide-react"; import type { TeamMember, RichListItem, VictoryItem, ScheduleLocation } from "@/types/content"; import { SignupModal } from "@/components/ui/SignupModal"; interface TeamProfileProps { member: TeamMember; onBack: () => void; schedule?: ScheduleLocation[]; } export function TeamProfile({ member, onBack, schedule }: TeamProfileProps) { const [lightbox, setLightbox] = useState(null); const [bookingGroup, setBookingGroup] = useState(null); useEffect(() => { function handleKeyDown(e: KeyboardEvent) { if (e.key === "Escape") { if (lightbox) setLightbox(null); else onBack(); } } window.addEventListener("keydown", handleKeyDown); return () => window.removeEventListener("keydown", handleKeyDown); }, [lightbox, onBack]); const places = member.victories?.filter(v => !v.type || v.type === 'place') ?? []; const nominations = member.victories?.filter(v => v.type === 'nomination') ?? []; const judging = member.victories?.filter(v => v.type === 'judge') ?? []; const victoryTabs = [ ...(places.length > 0 ? [{ key: 'place' as const, label: 'Достижения', icon: Trophy, items: places }] : []), ...(nominations.length > 0 ? [{ key: 'nomination' as const, label: 'Номинации', icon: Award, items: nominations }] : []), ...(judging.length > 0 ? [{ key: 'judge' as const, label: 'Судейство', icon: Scale, items: judging }] : []), ]; const hasVictories = victoryTabs.length > 0; const [activeTab, setActiveTab] = useState(victoryTabs[0]?.key ?? 'place'); const hasExperience = member.experience && member.experience.length > 0; const hasEducation = member.education && member.education.length > 0; // Extract trainer's groups from schedule using groupId const groupMap = new Map(); schedule?.forEach(location => { location.days.forEach(day => { day.classes .filter(c => c.trainer === member.name) .forEach(c => { const key = c.groupId ? `${c.groupId}||${location.name}` : `${c.trainer}||${c.type}||${location.name}`; const existing = groupMap.get(key); if (existing) { existing.slots.push({ day: day.day, dayShort: day.dayShort, time: c.time }); if (c.level && !existing.level) existing.level = c.level; if (c.recruiting) existing.recruiting = true; } else { groupMap.set(key, { type: c.type, location: location.name, address: location.address, slots: [{ day: day.day, dayShort: day.dayShort, time: c.time }], level: c.level, recruiting: c.recruiting, }); } }); }); }); const uniqueGroups = Array.from(groupMap.values()).map(g => { // Merge slots by day, then merge days with identical time sets const dayMap = new Map(); const dayOrder: string[] = []; for (const s of g.slots) { const existing = dayMap.get(s.day); if (existing) { if (!existing.times.includes(s.time)) existing.times.push(s.time); } else { dayMap.set(s.day, { dayShort: s.dayShort, times: [s.time] }); dayOrder.push(s.day); } } for (const entry of dayMap.values()) entry.times.sort(); const merged: { days: string[]; times: string[] }[] = []; const used = new Set(); for (const day of dayOrder) { if (used.has(day)) continue; const entry = dayMap.get(day)!; const timeKey = entry.times.join("|"); const days = [entry.dayShort]; used.add(day); for (const other of dayOrder) { if (used.has(other)) continue; const o = dayMap.get(other)!; if (o.times.join("|") === timeKey) { days.push(o.dayShort); used.add(other); } } merged.push({ days, times: entry.times }); } return { ...g, merged }; }); const hasGroups = uniqueGroups.length > 0; const hasBio = hasVictories || hasExperience || hasEducation || hasGroups; return (
{/* Magazine editorial layout */}
{/* Photo — left column, sticky */}
{member.name} {/* Top gradient for name */}
{/* Bottom gradient for mobile bio peek */}
{/* Name + role overlay at top */}

{member.name}

{member.role}

{member.instagram && ( {member.instagram.split("/").filter(Boolean).pop()} )}
{/* Bio panel — overlaps photo edge on desktop */}
{/* Ambient photo background */}
{/* Victory tabs */} {hasVictories && (
{victoryTabs.map(tab => ( ))}
{victoryTabs.map(tab => (
{tab.items.map((item, i) => ( ))}
))}
)} {/* Groups */} {hasGroups && (
Группы {uniqueGroups.map((g, i) => (

{g.type}

{g.merged.map((m, mi) => (
{m.days.join(", ")} {m.times.join(", ")}
))}
{g.location} · {g.address.replace(/^г\.\s*\S+,\s*/, "")}
{g.level && (

{g.level}

)} {g.recruiting && ( Набор открыт )}
))}
)} {/* Education */} {hasEducation && (
Образование {member.education!.map((item, i) => ( ))}
)} {/* Experience */} {hasExperience && (
Опыт {member.experience!.map((item, i) => (

{item}

))}
)} {/* Description */} {member.description && (

{member.description}

)} {/* Empty state */} {!hasBio && !member.description && (

Информация скоро появится

)}
{/* Image lightbox */} {lightbox && (
setLightbox(null)} >
Достижение
)} setBookingGroup(null)} subtitle={bookingGroup ?? undefined} endpoint="/api/group-booking" extraBody={{ groupInfo: bookingGroup }} />
); } function ScrollRow({ children }: { children: React.ReactNode }) { const scrollRef = useRef(null); const dragState = useRef<{ startX: number; scrollLeft: number } | null>(null); const wasDragged = useRef(false); const onPointerDown = useCallback((e: React.PointerEvent) => { const el = scrollRef.current; if (!el) return; (e.target as HTMLElement).setPointerCapture(e.pointerId); dragState.current = { startX: e.clientX, scrollLeft: el.scrollLeft }; wasDragged.current = false; }, []); const onPointerMove = useCallback((e: React.PointerEvent) => { if (!dragState.current || !scrollRef.current) return; const dx = e.clientX - dragState.current.startX; if (Math.abs(dx) > 4) wasDragged.current = true; scrollRef.current.scrollLeft = dragState.current.scrollLeft - dx; }, []); const onPointerUp = useCallback(() => { dragState.current = null; }, []); return (
{children}
); } function VictoryCard({ victory }: { victory: VictoryItem }) { const hasLink = !!victory.link; return (
{victory.place && (
{victory.place}
)}
{victory.category && (

{victory.category}

)}

{victory.competition}

{hasLink && ( Подробнее )}
); } function RichCard({ item, onImageClick }: { item: RichListItem; onImageClick: (src: string) => void }) { const hasImage = !!item.image; const hasLink = !!item.link; if (hasImage) { return (

{item.text}

{hasLink && ( Подробнее )}
); } return (

{item.text}

{hasLink && ( Подробнее )}
); }