Move schedule data to JSON file
This commit is contained in:
parent
cb2ad42915
commit
99d04f4b43
5 changed files with 136 additions and 122 deletions
|
@ -1,109 +0,0 @@
|
||||||
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"],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
|
@ -1,6 +1,7 @@
|
||||||
import Timetable from "@/ui/timetable"
|
import Timetable from "@/ui/timetable"
|
||||||
import styles from "./page.module.css"
|
import styles from "./page.module.css"
|
||||||
import { ScheduleEvent, events, locations } from "./events"
|
import { Schedule, ScheduleEvent } from "./types"
|
||||||
|
import { readFile } from "fs/promises"
|
||||||
|
|
||||||
function EventInfo(props: { event: ScheduleEvent }) {
|
function EventInfo(props: { event: ScheduleEvent }) {
|
||||||
return <section className={styles.event}>
|
return <section className={styles.event}>
|
||||||
|
@ -15,19 +16,20 @@ function EventInfo(props: { event: ScheduleEvent }) {
|
||||||
</section>
|
</section>
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function schedule() {
|
export default async function schedule() {
|
||||||
|
const schedule: Schedule = JSON.parse(await readFile("schedule.json", "utf-8"));
|
||||||
return <main className={styles.schedule}>
|
return <main className={styles.schedule}>
|
||||||
<h1>Schedule & Events</h1>
|
<h1>Schedule & Events</h1>
|
||||||
<p>
|
<p>
|
||||||
Study carefully, we only hold these events once a year.
|
Study carefully, we only hold these events once a year.
|
||||||
</p>
|
</p>
|
||||||
<h2>Schedule</h2>
|
<h2>Schedule</h2>
|
||||||
<Timetable events={events} />
|
<Timetable schedule={schedule} />
|
||||||
<h2>Events</h2>
|
<h2>Events</h2>
|
||||||
{events.map(event => <EventInfo event={event} key={event.id}/>)}
|
{schedule.events.map(event => <EventInfo event={event} key={event.id}/>)}
|
||||||
<h2>Locations</h2>
|
<h2>Locations</h2>
|
||||||
<ul>
|
<ul>
|
||||||
{locations.map(location => <li key={location.id}>
|
{schedule.locations.map(location => <li key={location.id}>
|
||||||
<h3>{location.name}</h3>
|
<h3>{location.name}</h3>
|
||||||
{location.description ?? "No description provided"}
|
{location.description ?? "No description provided"}
|
||||||
</li>)}
|
</li>)}
|
||||||
|
|
26
app/schedule/types.ts
Normal file
26
app/schedule/types.ts
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
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 interface Schedule {
|
||||||
|
locations: ScheduleLocation[],
|
||||||
|
events: ScheduleEvent[],
|
||||||
|
}
|
90
schedule.json
Normal file
90
schedule.json
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
{
|
||||||
|
"locations": [
|
||||||
|
{
|
||||||
|
"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",
|
||||||
|
"description": "Found at the entrance"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Camping Fireplace",
|
||||||
|
"id": "camping-fireplace",
|
||||||
|
"description": "Next to the big tree"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"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"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
import { ScheduleEvent, TimeSlot, locations } from "@/app/schedule/events";
|
import { Schedule, ScheduleEvent, ScheduleLocation, TimeSlot } from "@/app/schedule/types";
|
||||||
import styles from "./timetable.module.css";
|
import styles from "./timetable.module.css";
|
||||||
|
|
||||||
const oneDayMs = 24 * 60 * 60 * 1000;
|
const oneDayMs = 24 * 60 * 60 * 1000;
|
||||||
|
@ -100,7 +100,9 @@ function junctionsFromEdges(edges: Iterable<Edge>) {
|
||||||
return keys.map(key => junctions.get(key)!);
|
return keys.map(key => junctions.get(key)!);
|
||||||
}
|
}
|
||||||
|
|
||||||
function* spansFromJunctions(junctions: Iterable<Junction>): Generator<Span> {
|
function* spansFromJunctions(
|
||||||
|
junctions: Iterable<Junction>, locations: ScheduleLocation[]
|
||||||
|
): Generator<Span> {
|
||||||
const activeLocations = new Map(
|
const activeLocations = new Map(
|
||||||
locations.map(location => [location.id, new Set<TimeSlot>()])
|
locations.map(location => [location.id, new Set<TimeSlot>()])
|
||||||
);
|
);
|
||||||
|
@ -211,7 +213,9 @@ function* cutSpansByHours(span: Span): Generator<Span> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function tableElementsFromStretches(stretches: Iterable<Stretch>) {
|
function tableElementsFromStretches(
|
||||||
|
stretches: Iterable<Stretch>, locations: ScheduleLocation[]
|
||||||
|
) {
|
||||||
type Col = { minutes?: number };
|
type Col = { minutes?: number };
|
||||||
type DayHead = { span: number, content?: string }
|
type DayHead = { span: number, content?: string }
|
||||||
type HourHead = { span: number, content?: string }
|
type HourHead = { span: number, content?: string }
|
||||||
|
@ -308,17 +312,18 @@ function tableElementsFromStretches(stretches: Iterable<Stretch>) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Timetable(props: { events: ScheduleEvent[] }) {
|
export default function Timetable(props: { schedule: Schedule }) {
|
||||||
const junctions = junctionsFromEdges(edgesFromEvents(props.events));
|
const { locations, events } = props.schedule;
|
||||||
const stretches = [...stretchesFromSpans(spansFromJunctions(junctions), oneHourMs * 5)];
|
const junctions = junctionsFromEdges(edgesFromEvents(events));
|
||||||
|
const stretches = [...stretchesFromSpans(spansFromJunctions(junctions, locations), oneHourMs * 5)];
|
||||||
const {
|
const {
|
||||||
columnGroups,
|
columnGroups,
|
||||||
dayHeaders,
|
dayHeaders,
|
||||||
hourHeaders,
|
hourHeaders,
|
||||||
locationRows,
|
locationRows,
|
||||||
} = tableElementsFromStretches(stretches);
|
} = tableElementsFromStretches(stretches, locations);
|
||||||
const eventBySlotId = new Map(
|
const eventBySlotId = new Map(
|
||||||
props.events.flatMap(
|
events.flatMap(
|
||||||
event => event.slots.map(slot => [slot.id, event])
|
event => event.slots.map(slot => [slot.id, event])
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue