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 | import {formatClock, setSummary, timeRange, type OrderEntry} from '@/lib/gig' type Row = { id: string kind: 'SONG' | 'SET' | 'BREAK' label: string | null minutes: number | null song: {title: string} | null } /** * A gig's running order at a glance: each set as a small heading with its * times, songs numbered within it, breaks as a quiet line between. Pure * markup (server-safe). */ export default function RunningOrder<T extends Row>({ entries, song, }: { entries: OrderEntry<T>[] /** How a song row reads; defaults to its title */ song?: (item: T, n: number) => React.ReactNode }) { return ( <ol className="space-y-1"> {entries.map((e) => { if (e.kind === 'SET') return ( <li key={e.item.id} className="flex flex-wrap items-baseline gap-x-3 pb-1 pt-3 first:pt-0" > <span className="text-xs font-bold uppercase tracking-widest text-amber"> {e.info.label} </span> <span className="font-mono text-sm"> {timeRange(e.info.start, e.info.end)} </span> <span className="text-xs text-faint">{setSummary(e.info)}</span> </li> ) if (e.kind === 'BREAK') return ( <li key={e.item.id} className="my-2 flex items-center gap-3 text-sm text-faint" > <span className="h-px flex-1 bg-line" /> Break {e.info.minutes ? ` · ${e.info.minutes} min` : ''} {e.info.end != null && ` · back ${formatClock(e.info.end)}`} <span className="h-px flex-1 bg-line" /> </li> ) return ( <li key={e.item.id} className="flex gap-3"> <span className="w-5 shrink-0 text-right font-mono text-faint"> {e.n} </span> {song ? song(e.item, e.n) : e.item.song?.title} </li> ) })} </ol> ) } |