Carousel: cycle upcoming schemes instead of one day's (#48) #103

Merged
claude-bot merged 1 commit from feat/carousel-upcoming into main 2026-09-01 15:54:00 +00:00
Contributor

The last item on #48.

The problem

The carousel ran over allSchemeIds = scheme ? [scheme.id] : [] — the selected day's single scheme. A one-element carousel isn't a carousel, and it duplicated the Preview button sitting directly beside it. The stop control (the harder half) was already done.

Where it moved, and why

Starting a carousel moved to the toolbar. "Upcoming" isn't a property of whichever day happens to be selected, so a day-scoped panel is the wrong home for it. In the toolbar it can offer a real window — next 7 / 30 / 90 days — and report how many schemes that covers before you commit to cycling them.

The side panel keeps the stop control (it's where you look after starting one), and the header keeps the global one.

Reads the resolved day map, not the scheme list

upcomingSchemeIds(dayMap, from, days) walks the resolved days, so the carousel previews what will actually run:

  • a no-event day contributes nothing
  • a multi-day event contributes its scheme once, not once per day
  • each day's winner is the one the server resolved, not a second divergent resolution in the client (the trap #93 fixed in buildDayMap)

It's a pure function in calendar/utils.ts with 6 tests covering date ordering, multi-day collapsing, skipped days, the window edge, a month boundary, and the empty case.

Permissions

The control sits outside the isAdmin block: both carousel endpoints are require_viewer, and the spec says "bounded WLED preview endpoints, which viewer may call because Iris automatically restores the prior controller state."

useStartCarousel now takes an interval — the API already accepted interval_secs but the hook never passed it, so the 3-second default was the only reachable value.

Verification

Frontend in a node:22 container: tsc -b, eslint, vitest (76 passed, 6 new) and npm run build all clean.

🤖 Generated with Claude Code

The last item on #48. ## The problem The carousel ran over `allSchemeIds = scheme ? [scheme.id] : []` — the selected day's **single** scheme. A one-element carousel isn't a carousel, and it duplicated the Preview button sitting directly beside it. The stop control (the harder half) was already done. ## Where it moved, and why Starting a carousel moved to the **toolbar**. "Upcoming" isn't a property of whichever day happens to be selected, so a day-scoped panel is the wrong home for it. In the toolbar it can offer a real window — next 7 / 30 / 90 days — and report how many schemes that covers *before* you commit to cycling them. The side panel keeps the **stop** control (it's where you look after starting one), and the header keeps the global one. ## Reads the resolved day map, not the scheme list `upcomingSchemeIds(dayMap, from, days)` walks the resolved days, so the carousel previews **what will actually run**: - a no-event day contributes nothing - a multi-day event contributes its scheme **once**, not once per day - each day's winner is the one the server resolved, not a second divergent resolution in the client (the trap #93 fixed in `buildDayMap`) It's a pure function in `calendar/utils.ts` with 6 tests covering date ordering, multi-day collapsing, skipped days, the window edge, a month boundary, and the empty case. ## Permissions The control sits **outside** the `isAdmin` block: both carousel endpoints are `require_viewer`, and the spec says *"bounded WLED preview endpoints, which `viewer` may call because Iris automatically restores the prior controller state."* `useStartCarousel` now takes an interval — the API already accepted `interval_secs` but the hook never passed it, so the 3-second default was the only reachable value. ## Verification Frontend in a `node:22` container: `tsc -b`, `eslint`, `vitest` (**76 passed**, 6 new) and `npm run build` all clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Carousel: cycle upcoming schemes instead of one day's (#48)
All checks were successful
CI / Alembic migration check (pull_request) Successful in 1m4s
CI / Python lint & type-check (pull_request) Successful in 1m30s
CI / Frontend lint, test & build (pull_request) Successful in 1m38s
CI / Python tests (pull_request) Successful in 2m42s
CI / Docker build, health smoke & E2E (pull_request) Successful in 1m48s
88c5236435
The carousel ran over `allSchemeIds = scheme ? [scheme.id] : []` -- the
selected day's single scheme. A one-element carousel is not a carousel, and
it duplicated the Preview button sitting next to it.

Starting one moves to the toolbar, because "upcoming" is not a property of
whichever day happens to be selected; there it can offer a real window (next
7 / 30 / 90 days) and report how many schemes that covers before you commit.

The scheme list comes from the resolved day map rather than the raw scheme
list, so the carousel previews what will actually run: no-event days
contribute nothing, and a multi-day event contributes its scheme once rather
than once per day. That logic is a pure function in calendar/utils.ts with
six tests, including the month-boundary and window-edge cases.

The control sits outside the admin block -- both carousel endpoints are
require_viewer, and the spec allows viewers to trigger bounded previews. The
side panel keeps the stop control, which is where you look after starting
one, and the header keeps the global one.

useStartCarousel now takes an interval; the API already accepted one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
claude-bot deleted branch feat/carousel-upcoming 2026-09-01 15:54:00 +00:00
Sign in to join this conversation.
No description provided.