Minimally functional schedule rendering

Add timetable and event listing based on transforming a simple input
data structure.
This commit is contained in:
Hornwitser 2025-02-26 22:53:56 +01:00
parent 7d822e4934
commit 484c27ece2
10 changed files with 610 additions and 7 deletions

109
app/schedule/events.ts Normal file
View file

@ -0,0 +1,109 @@
export interface ScheduleEvent {
name: string,
id: string,
host?: string,
cancelled?: boolean,
description?: string,
slots: TimeSlot[],
}
export interface ScheduleLocation {
name: string,
id: string,
description?: string,
}
export interface TimeSlot {
id: string,
start: string,
end: string,
locations: string[],
}
export const locations: ScheduleLocation[] = [
{
name: "House",
id: "house",
description: "Blue building east of the camping",
},
{
name: "Common House",
id: "common-house",
description: "That big red building in the middle",
},
{
name: "Info Desk",
id: "info-desk",
},
{
name: "Camping Fireplace",
id: "camping-fireplace",
},
];
export const events: ScheduleEvent[] = [
{
name: "Arcade",
id: "arcade",
description: "Play retro games!",
slots: [
{
id: "arcade-1",
start: "2025-07-18T10:00Z",
end: "2025-07-19T01:30Z",
locations: ["house"],
},
{
id: "arcade-2",
start: "2025-07-19T10:00Z",
end: "2025-07-20T01:00Z",
locations: ["house"],
},
{
id: "arcade-3",
start: "2025-07-20T10:00Z",
end: "2025-07-20T18:00Z",
locations: ["house"],
},
],
},
{
name: "Bonfire Stories",
description: "Share your stories as we sit cosily around the bonfire.",
id: "bonfire",
slots: [
{
id: "bonfire-1",
start: "2025-07-19T20:00Z",
end: "2025-07-20T01:00Z",
locations: ["camping-fireplace"],
},
],
},
{
name: "Fursuit Games",
description: "Playful time for the suiters.",
id: "fursuit-games",
slots: [
{
id: "fursuit-games-1",
start: "2025-07-19T19:00Z",
end: "2025-07-19T20:00Z",
locations: ["common-house"],
},
],
},
{
name: "Late Stragglers",
description: "Wait a minute, why are you still here?.",
id: "too-late",
slots: [
{
id: "too-late-1",
start: "2025-07-22T20:00Z",
end: "2025-07-23T01:00Z",
locations: ["camping-fireplace"],
},
],
},
];

View file

@ -0,0 +1,18 @@
.schedule {
padding-inline: 1rem;
}
.schedule :is(h1, h2, h3, h4) {
margin-block: 0.75em 0.25em;
}
.event {
background: color-mix(in oklab, var(--background), grey 20%);
padding: 0.5rem;
border-radius: 0.5rem;
}
.event h3 {
margin: 0;
}
.event + .event {
margin-block-start: 0.5rem;
}

36
app/schedule/page.tsx Normal file
View file

@ -0,0 +1,36 @@
import Timetable from "@/ui/timetable"
import styles from "./page.module.css"
import { ScheduleEvent, events, locations } from "./events"
function EventInfo(props: { event: ScheduleEvent }) {
return <section className={styles.event}>
<h3>{props.event.name}</h3>
<p>{props.event.description ?? "No description provided"}</p>
<h4>Timeslots</h4>
<ul>
{props.event.slots.map(slot => <li key={slot.id}>
{slot.start} - {slot.end}
</li>)}
</ul>
</section>
}
export default function schedule() {
return <main className={styles.schedule}>
<h1>Schedule & Events</h1>
<p>
Study carefully, we only hold these events once a year.
</p>
<h2>Schedule</h2>
<Timetable events={events} />
<h2>Events</h2>
{events.map(event => <EventInfo event={event} key={event.id}/>)}
<h2>Locations</h2>
<ul>
{locations.map(location => <li key={location.id}>
<h3>{location.name}</h3>
{location.description ?? "No description provided"}
</li>)}
</ul>
</main>
}