diff --git a/src/app/page.tsx b/src/app/page.tsx index 2c1734d..15c7fb9 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -42,7 +42,7 @@ export default function HomePage() { /> - {openDayData && } + {openDayData && } diff --git a/src/components/sections/OpenDay.tsx b/src/components/sections/OpenDay.tsx index 4763932..dbe23cb 100644 --- a/src/components/sections/OpenDay.tsx +++ b/src/components/sections/OpenDay.tsx @@ -1,7 +1,8 @@ "use client"; import { useState, useMemo } from "react"; -import { Calendar, Sparkles } from "lucide-react"; +import Image from "next/image"; +import { Calendar, Sparkles, User } from "lucide-react"; import { SectionHeading } from "@/components/ui/SectionHeading"; import { Reveal } from "@/components/ui/Reveal"; import { SignupModal } from "@/components/ui/SignupModal"; @@ -14,6 +15,7 @@ interface OpenDayProps { classes: OpenDayClass[]; }; popups?: SiteContent["popups"]; + teamMembers?: { name: string; image: string }[]; } function formatDateRu(dateStr: string): string { @@ -25,10 +27,20 @@ function formatDateRu(dateStr: string): string { }); } -export function OpenDay({ data, popups }: OpenDayProps) { +export function OpenDay({ data, popups, teamMembers }: OpenDayProps) { const { event, classes } = data; const [signup, setSignup] = useState<{ classId: number; label: string } | null>(null); + const trainerPhotos = useMemo(() => { + const map: Record = {}; + if (teamMembers) { + for (const m of teamMembers) { + if (m.image) map[m.name] = m.image; + } + } + return map; + }, [teamMembers]); + // Group classes by hall const hallGroups = useMemo(() => { const groups: Record = {}; @@ -100,6 +112,7 @@ export function OpenDay({ data, popups }: OpenDayProps) { cls={cls} maxParticipants={event.maxParticipants} onSignup={setSignup} + trainerPhoto={trainerPhotos[cls.trainer]} /> ))} @@ -117,6 +130,7 @@ export function OpenDay({ data, popups }: OpenDayProps) { key={cls.id} cls={cls} onSignup={setSignup} + trainerPhoto={trainerPhotos[cls.trainer]} /> ))} @@ -149,23 +163,26 @@ function ClassCard({ cls, maxParticipants = 0, onSignup, + trainerPhoto, }: { cls: OpenDayClass; maxParticipants?: number; onSignup: (info: { classId: number; label: string }) => void; + trainerPhoto?: string; }) { const label = `${cls.style} · ${cls.trainer} · ${cls.startTime}–${cls.endTime}`; if (cls.cancelled) { return ( - - - - {cls.startTime}–{cls.endTime} - {cls.style} - {cls.trainer} + + + + + {cls.startTime}–{cls.endTime} + + {cls.trainer} · {cls.style} - + Отменено @@ -176,24 +193,71 @@ function ClassCard({ const isFull = maxParticipants > 0 && cls.bookingCount >= maxParticipants; return ( - - - - {cls.startTime}–{cls.endTime} - {cls.trainer} - {cls.style} - {maxParticipants > 0 && ( - - {cls.bookingCount}/{maxParticipants} мест - + + + {/* Trainer photo */} + { + window.dispatchEvent(new CustomEvent("openTrainerProfile", { detail: cls.trainer })); + }} + className="relative flex items-center justify-center h-10 w-10 rounded-full overflow-hidden shrink-0 ring-1 ring-white/10 hover:ring-gold/30 transition-all cursor-pointer mt-0.5" + title={`Подробнее о ${cls.trainer}`} + > + {trainerPhoto ? ( + + ) : ( + + + )} + + + + {/* Trainer name — clickable to bio */} + { + window.dispatchEvent(new CustomEvent("openTrainerProfile", { detail: cls.trainer })); + }} + className="text-sm font-semibold text-white/90 hover:text-gold transition-colors cursor-pointer" + > + {cls.trainer} + + + {/* Time + style */} + + + + {cls.startTime}–{cls.endTime} + + {cls.style} + + + + {/* Badges */} + + {maxParticipants > 0 && ( + + {cls.bookingCount}/{maxParticipants} мест + + )} + + + {/* Book button */} onSignup({ classId: cls.id, label })} - className={`shrink-0 rounded-full px-4 py-2 text-xs font-medium transition-colors cursor-pointer ${ + className={`shrink-0 self-center rounded-xl px-4 py-2 text-xs font-semibold transition-all cursor-pointer ${ isFull - ? "bg-amber-500/10 border border-amber-500/20 text-amber-400 hover:bg-amber-500/20" - : "bg-gold/10 border border-gold/20 text-gold hover:bg-gold/20" + ? "bg-amber-500/10 border border-amber-500/25 text-amber-400 hover:bg-amber-500/20 hover:border-amber-500/40" + : "bg-gold/10 border border-gold/25 text-gold hover:bg-gold/20 hover:border-gold/40" }`} > {isFull ? "Лист ожидания" : "Записаться"} diff --git a/src/components/sections/schedule/DayCard.tsx b/src/components/sections/schedule/DayCard.tsx index 9f650ca..d228bbb 100644 --- a/src/components/sections/schedule/DayCard.tsx +++ b/src/components/sections/schedule/DayCard.tsx @@ -47,11 +47,7 @@ function ClassRow({ setFilterTrainer(filterTrainer === cls.trainer ? null : cls.trainer)} - className={`mt-1.5 flex items-center gap-2 text-sm font-medium cursor-pointer active:opacity-60 ${ - filterTrainer === cls.trainer - ? "text-gold underline underline-offset-2" - : "text-neutral-800 dark:text-white/80" - }`} + className="mt-1.5 flex items-center gap-2 text-sm font-medium cursor-pointer active:opacity-60 text-neutral-800 dark:text-white/80" > {cls.trainer} @@ -62,11 +58,7 @@ function ClassRow({ className="flex items-center gap-2 cursor-pointer active:opacity-60" > - {cls.type} + {cls.type} {cls.level && ( diff --git a/src/components/sections/schedule/GroupView.tsx b/src/components/sections/schedule/GroupView.tsx index 9824af5..f3705b5 100644 --- a/src/components/sections/schedule/GroupView.tsx +++ b/src/components/sections/schedule/GroupView.tsx @@ -230,7 +230,7 @@ export function GroupView({ className="flex items-center gap-2 cursor-pointer" > - {type} + {type} {group.level && ( diff --git a/src/components/sections/schedule/MobileSchedule.tsx b/src/components/sections/schedule/MobileSchedule.tsx index 62359aa..b4e8512 100644 --- a/src/components/sections/schedule/MobileSchedule.tsx +++ b/src/components/sections/schedule/MobileSchedule.tsx @@ -47,7 +47,7 @@ function ClassRow({ setFilterTrainer(filterTrainer === cls.trainer ? null : cls.trainer)} - className={`truncate text-sm font-medium text-left active:opacity-60 ${filterTrainer === cls.trainer ? "text-gold underline underline-offset-2" : "text-neutral-800 dark:text-white/80"}`} + className="truncate text-sm font-medium text-left active:opacity-60 text-neutral-800 dark:text-white/80" > {cls.trainer} @@ -73,7 +73,7 @@ function ClassRow({ className={`flex items-center gap-1.5 active:opacity-60 ${filterTypes.has(cls.type) ? "opacity-100" : ""}`} > - {cls.type} + {cls.type} {showLocation && cls.locationName && (
{cls.style}
{cls.trainer}
{cls.trainer} · {cls.style}
- {cls.bookingCount}/{maxParticipants} мест -