Disable Vite's ANSI output (NO_COLOR/FORCE_COLOR) so the background-task
problem matcher's endsPattern reliably matches, fixing the
'Error exists after running preLaunchTask' failure. Add a
'Launch Debuggable Chrome' task (:9222, persistent ~/.chrome-rw-debug
profile) and an attach compound so you can debug the frontend in a Chrome
you control and stay logged in. Update DEBUGGING.md.
Backend configs run Vite directly (not via npm run dev) so the Node
inspector owns the process that executes SSR code, with source maps
enabled for .ts server files. Frontend configs launch/attach Chrome.
Adds a Node-inspector dev task, a Full Stack compound, and DEBUGGING.md
documenting the Safari limitation and workflows.
Replace the stacked per-day lists with a Google-Calendar-style WeekSchedule:
day columns side by side over a shared, sticky time gutter. Click or drag an
empty slot to create a block at that time; click a block to edit it. The
daily schedule reuses the same grid with a single 'Every day' column so both
modes look consistent. On narrow screens the row scrolls horizontally with
short day labels and the gutter pinned, keeping it usable on phones. Also
change the type switcher and copy dialog to enhanced form submissions (no
full-page reload) so they respond immediately. Removes the now-unused
DaySchedule component.
Adds scripts to seed scenario data, create Proclamations, snapshot schedules,
drive the Kiosk-facing HTTP endpoints, and an opt-in (P5_INTEGRATION=1)
Vitest spec exercising create/type/copy against a scratch database. The
integration spec is skipped by default so the normal suite needs no DB.
Adds a Copy to… action on a system's schedule with a multi-select of the
account's other systems, labelled by System Name (fallback: key), excluding
the source. Targets that already have a schedule are flagged and require an
explicit Overwrite & copy confirmation. Shows a summary of the systems
updated after success.
Adds a session-authenticated copy action that scopes the source and targets
to the caller's License Keys and returns a summary of the systems updated.
The loader now reports hasSchedule per system so the Copy UI can warn before
overwriting. Adds action tests (auth, cross-account, summary, error).
copySchedule copies one License Key's Schedule to one or more other keys by
value in a single Prisma transaction (all targets or none), assigning fresh
Block IDs and preserving each block's dynamicAudio and Proclamation order.
Source/targets are scoped to the caller's account; self-copy and empty
target lists are rejected. The resulting schedule is run through the shared
validator before commit. Adds tests: daily/weekly copy, overwrite, multiple
targets, rollback, cross-account rejection, new IDs, changed version/ETag,
copy independence, and Kiosk-payload parity with the shared fixtures.
System picker lists the account's License Keys labelled by System Name
(fallback: key). Schedule type selector (Daily|Weekly) with a confirmation
before switching a populated schedule (daily->weekly prefills all seven
days; otherwise clears). Weekly shows a per-day editor. Blocks use
TimeInput, allow adjacent/midnight-crossing ranges (labelled 'ends next
day'), block empty blocks client-side, default dynamic audio on, and show
title/category/group only. Adds gap helper text and an informational
kiosk-timezone note (browser fallback; no external/paid dependency).
Implements the shared validateSchedule validator (daily + weekly, midnight
spillover, Sunday->Monday wrap, adjacency, empty-block rejection), a
per-License-Key schedule service (get/set type, CRUD blocks, weekly
prefill with confirmation), the content-hash version/ETag serializer, the
new GET /api/v2/licenses/[licenseKey]/schedule (200/304/404, type null),
and additive evolution of the legacy GET /api/v2/schedule. Adds validator,
serializer (against docs/fixtures), endpoint ETag/304, and service tests.
Adds Schedule.licenseKeyId (unique, nullable) and Schedule.type, makes the
legacy userId optional/non-unique, adds Block.day and flips Block
dynamicAudio default to true. The migration backfills each existing
per-user schedule onto that account's oldest License Key as a daily
schedule, preserves unmappable schedules (licenseKeyId NULL) with a
NOTICE, and is reversible. Tested up/down against a scratch schema.
Add PUT /api/v2/licenses/[licenseKey]/system-name (200/400 invalid_name/
404 unknown_license/409 name_taken, idempotent) and extend POST
/api/v2/license with an optional systemName and additive systemNameStatus
(ok/unchanged/conflict/invalid). Validation always succeeds; an omitted
field never changes or clears a name and empty/whitespace is invalid.
Adds server and helper tests covering trimming, bounds, case-insensitive
uniqueness, cross-account reuse, 404, and idempotency.
Adds systemName (as typed, trimmed) and systemNameLower (case-folded) to
LicenseKey plus a unique index on (userId, systemNameLower) so names are
case-insensitively unique per account when non-null. Additive and
reversible; migration tested up/down. Regenerates the Prisma client.
Use TimeInput for Block start/end and show the day timeline axis with
AM/PM, so no 24-hour time is displayed in the schedule UI. New Blocks now
default dynamicAudio to true in the modal and server-side when the field
is absent; existing stored Blocks are untouched.
TimeInput displays and accepts AM/PM times, offers the 5-minute options as
suggestions, and allows typing any minute 00-59; its value model is a 24h
HH:MM string. Adds isomorphic parseTimeInput/hmToMinutes/fiveMinuteOptions
helpers and unit + component tests for them.
Add docs/schedule-api-contract.md (current state, time semantics, schedule
data model, validation rules, endpoints, sync behavior, System Name rules,
dynamic audio, backward compatibility, per-phase touchpoints, open items,
changelog) and docs/fixtures/*.json (daily, weekly, none, midnight-crossing,
adjacent, dynamic-audio, and legacy v2 payloads). Kept byte-identical with
the radiantwave repo.