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 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | import Link from 'next/link' import DemoBanner from '@/components/tour/DemoBanner' import {pageSession} from '@/lib/guard' import { formatClock, parseClock, runningOrder, setSummary, timeRange, } from '@/lib/gig' import {DEMO_SETLIST as set} from '@/lib/tour-demo' export const metadata = {title: 'Sample setlist'} const inert = 'pointer-events-none' /** * The tour's setlist: laid out like a real one (setlists/[id]), the same * for every band, saved nowhere. Its buttons are for show. */ export default async function TourSetlist() { await pageSession() const order = runningOrder(set.startTime, set.items) const songs = order.entries.filter((e) => e.kind === 'SONG').length const start = parseClock(set.startTime) return ( <> <DemoBanner what="setlist" /> <main className="mx-auto max-w-3xl px-4 pb-12 pt-4"> <Link href="/setlists" className="text-sm text-muted no-underline hover:text-text" > ‹ Setlists </Link> <h1 className="mt-1 text-3xl font-extrabold">{set.name}</h1> <p className="mt-1 text-muted"> {[ set.date, start != null && formatClock(start, true), set.venue, `${songs} songs`, ] .filter(Boolean) .join(' · ')} </p> <div className="mt-4 flex flex-wrap gap-2"> <span data-tour="perform" className="inline-flex min-h-11 items-center rounded-lg bg-accent px-4 font-extrabold text-on-accent" > ▶ Perform </span> <span className={`${inert} inline-flex min-h-11 items-center rounded-lg border border-line-2 px-4`} > PDF </span> <span className={`${inert} inline-flex min-h-11 items-center rounded-lg border border-line-2 px-4`} > Save for offline </span> </div> <p className="mt-5 whitespace-pre-wrap rounded-xl bg-panel px-4 py-3 text-[15px]"> {set.notes} </p> <ol className="mt-5"> {order.entries.map((e) => { if (e.kind === 'SET') return ( <li key={e.item.id} className="mt-6 flex flex-wrap items-baseline gap-x-3 border-b-2 border-amber pb-1 first:mt-0" > <h2 className="text-lg font-extrabold">{e.info.label}</h2> <span className="font-mono"> {timeRange(e.info.start, e.info.end)} </span> <span className="text-sm text-muted"> {setSummary(e.info)} </span> </li> ) if (e.kind === 'BREAK') return ( <li key={e.item.id} className="my-4 flex items-center gap-3 text-sm text-muted" > <span className="h-px flex-1 bg-line-2" /> Break · {e.info.minutes} min ·{' '} {timeRange(e.info.start, e.info.end)} <span className="h-px flex-1 bg-line-2" /> </li> ) const i = e.item return ( <li key={i.id} className="border-t border-line py-3"> <div className="flex items-baseline gap-3"> <span className="w-6 shrink-0 font-mono text-sm text-faint"> {e.n} </span> <span className="min-w-0 flex-1 text-lg font-semibold"> {i.song?.title} </span> <span className="shrink-0 font-mono font-bold text-amber"> {i.key} </span> </div> {i.note && ( <p className="mt-1 pl-9 text-[15px] text-warn-fg">{i.note}</p> )} </li> ) })} </ol> </main> </> ) } |