Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | import React from 'react';
import { cn } from '@/lib/utils';
import type { components } from '@/types/generated/event-watch-api.types';
type WatchSessionDetail = components['schemas']['WatchSessionDetail'];
interface AgendaListProps {
sessions: WatchSessionDetail[];
}
const BREAK_TYPES = new Set(['break', 'lunch', 'networking', 'aperitif']);
function formatTime(iso: string | null | undefined): string {
if (!iso) return '—';
return new Date(iso).toLocaleTimeString('de-CH', { hour: '2-digit', minute: '2-digit' });
}
function speakerNames(session: WatchSessionDetail): string {
if (!session.speakers?.length) return '';
return session.speakers
.map((s) => [s.firstName, s.lastName].filter(Boolean).join(' '))
.filter(Boolean)
.join(', ');
}
function SessionRow({ session }: { session: WatchSessionDetail }) {
const isActive = session.status === 'ACTIVE';
const isDone = session.status === 'COMPLETED';
const isBreak = BREAK_TYPES.has(session.sessionType ?? '');
const speakers = speakerNames(session);
return (
<div
className={cn(
'flex items-start gap-3 py-3 px-2 rounded-lg mb-1 border-l-2 transition-colors',
isActive ? 'bg-primary/10 border-primary' : 'border-transparent'
)}
>
{/* Status icon */}
<div className="mt-0.5 shrink-0">
{isDone ? (
<span className="text-green-500 text-lg">✓</span>
) : isActive ? (
<span className="text-primary text-lg">▶</span>
) : isBreak ? (
<span className="text-muted-foreground text-lg">☕</span>
) : (
<span className="text-muted-foreground/40 text-lg">○</span>
)}
</div>
{/* Content */}
<div className="flex-1 min-w-0">
<p
className={cn(
'text-base font-medium leading-snug',
isDone
? 'line-through text-muted-foreground/50'
: isActive
? 'text-foreground font-bold'
: 'text-foreground/80'
)}
>
{session.title}
</p>
{speakers && !isDone && <p className="text-sm text-muted-foreground mt-0.5">{speakers}</p>}
<p className="text-sm text-muted-foreground/60 font-mono mt-0.5">
{formatTime(session.scheduledStartTime)}
{session.durationMinutes != null ? ` · ${session.durationMinutes} min` : ''}
</p>
</div>
</div>
);
}
export const AgendaList: React.FC<AgendaListProps> = ({ sessions }) => {
if (sessions.length === 0) {
return <p className="text-center text-muted-foreground py-6">Keine Sessions verfügbar</p>;
}
return (
<div>
<p className="text-xs font-semibold text-muted-foreground uppercase tracking-[0.2em] mb-2">
Programm
</p>
<div className="border-t border-border mb-2" />
<div>
{sessions.map((session) => (
<SessionRow key={session.sessionSlug} session={session} />
))}
</div>
</div>
);
};
|