import type { Dispatch, SetStateAction } from "react"; import { Calendar, ChevronDown, MapPin } from "lucide-react"; import type { VisitorSessionPreview } from "../lib/api"; import { formatDateLong, formatDurationMinutes, formatTimeShort, sessionTypeDisplay, } from "../lib/sessionDisplay"; import type { AgendaRowTrackV2Bookmark, AgendaRowTrackV2Data } from "./AgendaRowTrackV2"; import { AgendaRowTrackV2 } from "./AgendaRowTrackV2"; export function landingSpeakerName(s: { firstName?: string; lastName?: string }): string { return [s.firstName, s.lastName].filter(Boolean).join(" ").trim() || "Speaker"; } export type LandingAgendaDay = { dateKey: string; dateLabel: string; trackGroups: Array<{ trackId: string; trackName: string; trackHall?: string; sessions: VisitorSessionPreview[]; }>; }; export function groupSessionsIntoAgendaDays(sessions: VisitorSessionPreview[]): LandingAgendaDay[] { if (!sessions.length) return []; const localDateKey = (iso: string) => { const d = new Date(iso); if (Number.isNaN(d.getTime())) return ""; const y = d.getFullYear(); const m = String(d.getMonth() + 1).padStart(2, "0"); const day = String(d.getDate()).padStart(2, "0"); return `${y}-${m}-${day}`; }; const dayLabel = (iso: string) => { const d = new Date(iso); if (Number.isNaN(d.getTime())) return ""; return d.toLocaleDateString(undefined, { weekday: "long", month: "long", day: "numeric", year: "numeric", }); }; const byDay = new Map(); for (const s of sessions) { const key = localDateKey(s.startTime); if (!key) continue; const arr = byDay.get(key) ?? []; arr.push(s); byDay.set(key, arr); } const keys = [...byDay.keys()].sort(); return keys.map((dateKey) => { const daySessions = byDay.get(dateKey)!; const labelIso = daySessions[0]?.startTime ?? ""; const byTrack = new Map< string, { trackId: string; trackName: string; trackHall?: string; sessions: VisitorSessionPreview[]; } >(); for (const s of daySessions) { const tid = s.track?._id ?? "__none__"; const tname = s.track?.name?.trim() || "General"; let g = byTrack.get(tid); if (!g) { g = { trackId: tid, trackName: tname, trackHall: s.track?.hall?.trim() || undefined, sessions: [], }; byTrack.set(tid, g); } else { if (!g.trackHall && s.track?.hall?.trim()) g.trackHall = s.track.hall.trim(); } g.sessions.push(s); } const trackGroups = [...byTrack.values()].sort((a, b) => { const ta = new Date(a.sessions[0].startTime).getTime(); const tb = new Date(b.sessions[0].startTime).getTime(); return ta - tb; }); for (const tg of trackGroups) { tg.sessions.sort((a, b) => new Date(a.startTime).getTime() - new Date(b.startTime).getTime()); } return { dateKey, dateLabel: dayLabel(labelIso), trackGroups }; }); } function sessionToAgendaRowV2( s: VisitorSessionPreview, eventId: string, bookmark: AgendaRowTrackV2Bookmark, ): AgendaRowTrackV2Data { const names = (s.speakers || []).map(landingSpeakerName); if (s.moderator) names.push(`${landingSpeakerName(s.moderator)} (Moderator)`); const joinHref = `/session?${new URLSearchParams({ event_id: eventId, session_id: s._id }).toString()}`; return { time: formatTimeShort(s.startTime), duration: formatDurationMinutes(s.startTime, s.endTime) || "—", trackTitle: s.title, sessionType: sessionTypeDisplay(s.type), description: s.description?.trim() || "Agenda details will appear here when available.", speakerNames: names.length ? names : ["—"], status: s.status === "ongoing" ? { kind: "live" } : null, showJoin: true, joinHref, bookmark, }; } function sessionToMySessionsRowV2( s: VisitorSessionPreview, eventId: string, bookmark: AgendaRowTrackV2Bookmark, ): AgendaRowTrackV2Data { const base = sessionToAgendaRowV2(s, eventId, bookmark); const hall = s.track?.hall?.trim(); const dateLbl = formatDateLong(s.startTime); return { ...base, ...(dateLbl ? { sessionDateLabel: dateLbl } : {}), ...(hall ? { trackLocation: hall } : {}), }; } type LandingAgendaCardsProps = { days: LandingAgendaDay[]; dayOpen: Record; setDayOpen: Dispatch>>; eventId: string; bookmarkedIds: Set; toggleBookmark: (sessionId: string) => void | Promise; }; export function LandingAgendaCards({ days, dayOpen, setDayOpen, eventId, bookmarkedIds, toggleBookmark, }: LandingAgendaCardsProps) { return (
{days.map((day) => { const isOpen = dayOpen[day.dateKey] ?? false; const toggleDay = () => { setDayOpen((p) => ({ ...p, [day.dateKey]: !(p[day.dateKey] ?? false) })); }; return (
{isOpen ? (
{day.trackGroups.map((tg, tgIdx) => (
{tgIdx > 0 ? (
) : null}

{tg.trackName}

{tg.trackHall ? (
{tg.trackHall}
) : null}
{tg.sessions.map((s) => ( void toggleBookmark(s._id), })} /> ))}
))}
) : null}
); })}
); } export function LandingMySessionsList({ sessions, eventId, bookmarkedIds, toggleBookmark, }: { sessions: VisitorSessionPreview[]; eventId: string; bookmarkedIds: Set; toggleBookmark: (sessionId: string) => void | Promise; }) { return (
{sessions.map((s) => ( void toggleBookmark(s._id), })} /> ))}
); }