--- title: Artist Timetable Prototype Audit status: complete audited-at: 2026-05-08 audited-by: Claude Code prototype-source: ./resources/Crewli - Artist Timetable Management/ follows-up: dev-docs/RFC-TIMETABLE-Artist-Timetable-Module.md (v0.1) feeds-into: dev-docs/RFC-TIMETABLE-Artist-Timetable-Module.md (v0.2 — to be written) --- # Artist Timetable Prototype Audit > Read-only audit of the standalone React+Babel prototype shipped in > `./resources/Crewli - Artist Timetable Management/`. The prototype is a > single-page demo (no build, no backend, in-memory state) that explores the > UX/UI for the Artist Timetable module. This document captures **what is** > in the prototype so RFC-TIMETABLE v0.2 can adopt, adapt, or reject each > piece deliberately. --- ## §1 File inventory The prototype is loaded by a single HTML page that pulls React 18, ReactDOM, and Babel-standalone from unpkg, then loads five JSX/JS scripts in order. | Group | File | LOC | One-line description | |---|---|---|---| | App entry | `Crewli Timetable.html` | 28 | HTML shell — loads React 18 / Babel-standalone via unpkg, then `data.js`, `helpers.js`, and the four JSX files. | | Data / fixtures | `data.js` | 159 | `window.CrewliData` IIFE — `EVENT`, `STAGES`, `STAGE_DAYS`, `ADVANCE_SECTIONS`, `ARTISTS`, `GENRES`, `PERFORMANCES`, `PARKED`, `PENDING`, `TIME` constants. | | Pure logic / lib | `helpers.js` | 144 | `window.CrewliHelpers` — `STATUS` palette, `fmtTime`, `snap`, `findConflicts`, `findB2B`, `isCapacityWarn`, `assignLanes`, `advanceCount`. | | Components | `timetable.jsx` | 1133 | `Block`, `TimeAxis`, `GridBg`, `StatusMultiSelect`, `ParkingColumn`, `Timetable`. Owns lane assignment, cascade-bump, drag/resize/create logic. | | Components | `popover.jsx` | 264 | `Popover` (timetable block), `QueuePopover` (parking item), `StatusDropdown`, `PopoverBody`. Off-screen-safe `pickPos`. | | Components | `modals.jsx` | 379 | `PerformanceModal` (add/edit), `StageEditor` (create/edit/delete stage), `LineupMatrix` (stages×days bulk toggle), `Backdrop`, `Field`. | | Components | `app.jsx` | 517 | `App` root — owns all state via `React.useState`, dispatches reducer-style actions, renders `Sidebar`, `Header`, `Timetable`, popovers, modals, `FooterToolbar`, `TweaksPanel`. | | Components (dev) | `tweaks-panel.jsx` | 759 | Generic vendor-style live-tweak panel: zoom slider, density radio, advancing-% toggle. **Not part of the module surface** — design-time only. | | Styles | `styles.css` | ~1000 | Light-mode shell, navy sidebar, teal accent. CSS custom properties on `:root`. Status colours live in `helpers.js`, not CSS. | | Docs | `docs/RFC-TIMETABLE - Artist Timetable Module.md` | 529 | A copy of v0.1 RFC checked into the prototype folder. Identical to (intended) `dev-docs/RFC-TIMETABLE-...` v0.1. | | Docs | `docs/timetable-module.md` | 470 | Companion narrative doc — overlap with RFC, less authoritative. | | Other | `.DS_Store`, `uploads/pasted-...png` | — | Mac filesystem cruft + a screenshot embedded for design context. Ignore. | The prototype has **no node_modules, no package.json, no bundler config, no tests**. State is in-memory and resets on reload. --- ## §2 Data model The prototype's authoritative data shapes live in `data.js`. Field names match Crewli `SCHEMA.md` §3.5.7 conventions where possible (snake_case, ULID-shaped string IDs prefixed by entity letter — `s_`, `a_`, `p_`, `pk_`, `pa_`, `d_`). ### §2.1 `EVENT` | Field | Type | Sample | Nullable | Notes | |---|---|---|---|---| | `id` | string | `"ezf_2026"` | no | Free-form string in prototype; production uses ULID. | | `name` | string | `"Echt Zomer Feesten"` | no | | | `edition` | string | `"2026"` | no | Prototype-only convenience; not in SCHEMA. | | `sub_event_label` | string | `"dag"` | no | i18n knob — header reads "Stages per {sub_event_label}". | | `days` | array | — | no | Inline pivot (no `event_id` FK in fixture; single-event PoC). | ### §2.2 `Day` (inline in `EVENT.days`) | Field | Type | Sample | Nullable | Notes | |---|---|---|---|---| | `id` | string | `"d_fr"` | no | | | `date` | ISO date | `"2026-07-10"` | no | | | `label` | string | `"Vrijdag"` | no | Long form. | | `short` | string | `"Vr"` | no | Two-char form (unused in current UI; carried for future tabs). | ### §2.3 `STAGES` | Field | Type | Sample | Nullable | Notes | |---|---|---|---|---| | `id` | string | `"s_hardstyle"` | no | | | `name` | string | `"Hardstyle District"` | no | | | `color` | string (hex) | `"#e85d75"` | no | Drives left-edge swatch on stage row + popover header tint. | | `capacity` | int | `4500` | no | Drives `isCapacityWarn(artist, stage) = artist.draw > stage.capacity * 1.1`. | | `__draft` | bool | `true` | yes | Transient flag set by `handleAddStage` so `StageEditor` knows it is committing a brand-new row vs editing an existing one. Stripped in `app.jsx:323` before dispatch. | ### §2.4 `STAGE_DAYS` (pivot) | Field | Type | Sample | Notes | |---|---|---|---| | `stage_id` | string FK | `"s_hardstyle"` | | | `day_id` | string FK | `"d_fr"` | Prototype joins to `EVENT.days[].id`, not `date`. | No PK column — pure pivot. Production schema (SCHEMA §3.5.7) uses `(stage_id, day_date)` with an int AI PK and unique constraint. ### §2.5 `ADVANCE_SECTIONS` | Field | Type | Sample | Notes | |---|---|---|---| | `key` | string | `"tour"` | Map key, not ULID. | | `label` | string | `"Tourmanager"` | Displayed in popover checklist. | Closed list of 5 in prototype: `tour, hosp, travel, flight, rider`. In production these are `advance_sections` rows per artist (not a global list). ### §2.6 `GENRES` ```js ["Hardstyle", "Techno", "House", "Hollands", "Pop", "Urban", "Disco", "Aprés"] ``` A flat string array, used to (a) populate the genre `