feat: show trainer's groups on profile from schedule data
- Extract classes from schedule matching trainer name - Group by type+time+location, combine days (e.g. ПН, СР) - Display as horizontal scroll cards with time, location, level - Show recruiting badge and address (without city prefix) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -27,7 +27,7 @@ export default function HomePage() {
|
||||
locations: content.schedule.locations.length,
|
||||
}}
|
||||
/>
|
||||
<Team data={content.team} />
|
||||
<Team data={content.team} schedule={content.schedule.locations} />
|
||||
<Classes data={content.classes} />
|
||||
<Schedule data={content.schedule} classItems={content.classes.items} />
|
||||
<Pricing data={content.pricing} />
|
||||
|
||||
@@ -6,13 +6,14 @@ import { Reveal } from "@/components/ui/Reveal";
|
||||
import { TeamCarousel } from "@/components/sections/team/TeamCarousel";
|
||||
import { TeamMemberInfo } from "@/components/sections/team/TeamMemberInfo";
|
||||
import { TeamProfile } from "@/components/sections/team/TeamProfile";
|
||||
import type { SiteContent } from "@/types/content";
|
||||
import type { SiteContent, ScheduleLocation } from "@/types/content";
|
||||
|
||||
interface TeamProps {
|
||||
data: SiteContent["team"];
|
||||
schedule?: ScheduleLocation[];
|
||||
}
|
||||
|
||||
export function Team({ data: team }: TeamProps) {
|
||||
export function Team({ data: team, schedule }: TeamProps) {
|
||||
const [activeIndex, setActiveIndex] = useState(0);
|
||||
const [showProfile, setShowProfile] = useState(false);
|
||||
|
||||
@@ -61,6 +62,7 @@ export function Team({ data: team }: TeamProps) {
|
||||
<TeamProfile
|
||||
member={team.members[activeIndex]}
|
||||
onBack={() => setShowProfile(false)}
|
||||
schedule={schedule}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import { useState, useEffect, useRef, useCallback } from "react";
|
||||
import Image from "next/image";
|
||||
import { ArrowLeft, Instagram, Trophy, GraduationCap, ExternalLink, X, Award, Scale } from "lucide-react";
|
||||
import type { TeamMember, RichListItem, VictoryItem } from "@/types/content";
|
||||
import { ArrowLeft, Instagram, Trophy, GraduationCap, ExternalLink, X, Award, Scale, Clock, MapPin } from "lucide-react";
|
||||
import type { TeamMember, RichListItem, VictoryItem, ScheduleLocation } from "@/types/content";
|
||||
|
||||
interface TeamProfileProps {
|
||||
member: TeamMember;
|
||||
onBack: () => void;
|
||||
schedule?: ScheduleLocation[];
|
||||
}
|
||||
|
||||
export function TeamProfile({ member, onBack }: TeamProfileProps) {
|
||||
export function TeamProfile({ member, onBack, schedule }: TeamProfileProps) {
|
||||
const [lightbox, setLightbox] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -33,7 +34,28 @@ export function TeamProfile({ member, onBack }: TeamProfileProps) {
|
||||
const [activeTab, setActiveTab] = useState(victoryTabs[0]?.key ?? 'place');
|
||||
const hasExperience = member.experience && member.experience.length > 0;
|
||||
const hasEducation = member.education && member.education.length > 0;
|
||||
const hasBio = hasVictories || hasExperience || hasEducation;
|
||||
|
||||
// Extract trainer's groups from schedule, grouped by type + time + location
|
||||
const groupMap = new Map<string, { type: string; time: string; location: string; address: string; days: string[]; level?: string; recruiting?: boolean }>();
|
||||
schedule?.forEach(location => {
|
||||
location.days.forEach(day => {
|
||||
day.classes
|
||||
.filter(c => c.trainer === member.name)
|
||||
.forEach(c => {
|
||||
const key = `${c.type}|${c.time}|${location.name}`;
|
||||
const existing = groupMap.get(key);
|
||||
if (existing) {
|
||||
if (!existing.days.includes(day.dayShort)) existing.days.push(day.dayShort);
|
||||
} else {
|
||||
groupMap.set(key, { type: c.type, time: c.time, location: location.name, address: location.address, days: [day.dayShort], level: c.level, recruiting: c.recruiting });
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
const uniqueGroups = Array.from(groupMap.values());
|
||||
const hasGroups = uniqueGroups.length > 0;
|
||||
|
||||
const hasBio = hasVictories || hasExperience || hasEducation || hasGroups;
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -146,9 +168,42 @@ export function TeamProfile({ member, onBack }: TeamProfileProps) {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Groups */}
|
||||
{hasGroups && (
|
||||
<div className={hasVictories ? "mt-8" : ""}>
|
||||
<span className="inline-flex items-center gap-1.5 rounded-full border border-gold/20 bg-gold/5 px-4 py-1.5 text-sm font-medium text-gold">
|
||||
<Clock size={14} />
|
||||
Группы
|
||||
</span>
|
||||
<ScrollRow>
|
||||
{uniqueGroups.map((g, i) => (
|
||||
<div key={i} className="w-44 shrink-0 rounded-xl border border-white/[0.08] bg-white/[0.03] p-3 space-y-1.5">
|
||||
<p className="text-xs font-semibold uppercase tracking-wider text-white/80">{g.type}</p>
|
||||
<div className="flex items-center gap-1.5 text-xs text-white/50">
|
||||
<Clock size={11} />
|
||||
{g.days.join(", ")} · {g.time}
|
||||
</div>
|
||||
<div className="flex items-start gap-1.5 text-xs text-white/40">
|
||||
<MapPin size={11} className="mt-0.5 shrink-0" />
|
||||
<span>{g.location} · {g.address.replace(/^г\.\s*\S+,\s*/, "")}</span>
|
||||
</div>
|
||||
{g.level && (
|
||||
<p className="text-[10px] text-gold/60">{g.level}</p>
|
||||
)}
|
||||
{g.recruiting && (
|
||||
<span className="inline-block rounded-full bg-green-500/15 border border-green-500/30 px-2 py-0.5 text-[10px] text-green-400">
|
||||
Набор открыт
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</ScrollRow>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Education */}
|
||||
{hasEducation && (
|
||||
<div className={hasVictories ? "mt-8" : ""}>
|
||||
<div className={hasVictories || hasGroups ? "mt-8" : ""}>
|
||||
<span className="inline-flex items-center gap-1.5 rounded-full border border-gold/20 bg-gold/5 px-4 py-1.5 text-sm font-medium text-gold">
|
||||
<GraduationCap size={14} />
|
||||
Образование
|
||||
@@ -163,7 +218,7 @@ export function TeamProfile({ member, onBack }: TeamProfileProps) {
|
||||
|
||||
{/* Experience */}
|
||||
{hasExperience && (
|
||||
<div className={hasVictories || hasEducation ? "mt-8" : ""}>
|
||||
<div className={hasVictories || hasGroups || hasEducation ? "mt-8" : ""}>
|
||||
<span className="inline-flex items-center gap-1.5 rounded-full border border-gold/20 bg-gold/5 px-4 py-1.5 text-sm font-medium text-gold">
|
||||
<Trophy size={15} />
|
||||
Опыт
|
||||
|
||||
Reference in New Issue
Block a user