fix: use groupId for trainer bio schedule groups
Group extraction now uses groupId (from admin panel) instead of type+time heuristic, with mergeSlotsByDay for proper day/time display. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -37,24 +37,65 @@ export function TeamProfile({ member, onBack, schedule }: TeamProfileProps) {
|
||||
const hasExperience = member.experience && member.experience.length > 0;
|
||||
const hasEducation = member.education && member.education.length > 0;
|
||||
|
||||
// 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 }>();
|
||||
// Extract trainer's groups from schedule using groupId
|
||||
const groupMap = new Map<string, { type: string; location: string; address: string; slots: { day: string; dayShort: string; time: 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 key = c.groupId
|
||||
? `${c.groupId}||${location.name}`
|
||||
: `${c.trainer}||${c.type}||${location.name}`;
|
||||
const existing = groupMap.get(key);
|
||||
if (existing) {
|
||||
if (!existing.days.includes(day.dayShort)) existing.days.push(day.dayShort);
|
||||
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, time: c.time, location: location.name, address: location.address, days: [day.dayShort], level: c.level, recruiting: c.recruiting });
|
||||
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());
|
||||
const uniqueGroups = Array.from(groupMap.values()).map(g => {
|
||||
// Merge slots by day, then merge days with identical time sets
|
||||
const dayMap = new Map<string, { dayShort: string; times: string[] }>();
|
||||
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<string>();
|
||||
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;
|
||||
@@ -181,9 +222,14 @@ export function TeamProfile({ member, onBack, schedule }: TeamProfileProps) {
|
||||
{uniqueGroups.map((g, i) => (
|
||||
<div key={i} className="w-48 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 className="space-y-0.5">
|
||||
{g.merged.map((m, mi) => (
|
||||
<div key={mi} className="flex items-center gap-1.5 text-xs text-white/50">
|
||||
<Clock size={11} className="shrink-0" />
|
||||
<span className="font-medium text-white/70">{m.days.join(", ")}</span>
|
||||
<span>{m.times.join(", ")}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex items-start gap-1.5 text-xs text-white/40">
|
||||
<MapPin size={11} className="mt-0.5 shrink-0" />
|
||||
@@ -198,7 +244,7 @@ export function TeamProfile({ member, onBack, schedule }: TeamProfileProps) {
|
||||
</span>
|
||||
)}
|
||||
<button
|
||||
onClick={() => setBookingGroup(`${g.type}, ${g.days.join("/")} ${g.time}`)}
|
||||
onClick={() => setBookingGroup(`${g.type}, ${g.merged.map(m => m.days.join("/")).join(", ")} ${g.merged[0]?.times[0] ?? ""}`)}
|
||||
className="w-full mt-1 rounded-lg bg-gold/15 border border-gold/25 py-1.5 text-[11px] font-semibold text-gold hover:bg-gold/25 transition-colors cursor-pointer"
|
||||
>
|
||||
Записаться
|
||||
|
||||
Reference in New Issue
Block a user