15 — BoxOffice: Streaming UI & the Moderator Console
UI project 2 of 5 · IOS/TheOne/BoxOffice, app/workers/boxoffice-api, app/web-platform /support/staff
Stack: SwiftUI, Cloudflare Workers (Hono) + D1, Next.js + TanStack Query, Cloudflare Stream
Scope: iOS 45 files / 20,407 lines · Worker with OpenAPI + vitest · four-route staff console
Commit: 5e3bbbf6c — "a moderator console for the 27 endpoints that had no UI"
Where this sits. Two UIs, one product: the consumer video surface, and the internal tool that keeps it legal. The internal tool is the more interesting interview artifact, because operator UX under legal deadlines is a design problem most candidates have never touched.
The consumer side — iOS BoxOffice
Container / Models / Network / Service / ViewModels / Views — catalog
browsing, video detail, My List, playback via Cloudflare Stream, access
expiry, and a creator/subscription tier. Backed by boxoffice-api (a Worker
over D1 with a checked-in prod_schema.sql, an openapi.yaml, migrations, and
a vitest suite).
Presentation work landed alongside it: portrait cards, hero backdrop, poster / backdrop / credits / ratings on the API, and a real title page.
The staff console — the part worth showing
"The staff backend was finished and deployed; nothing could reach it. Moderators had 27 endpoints and curl. This is the other half."
Four routes at /support/staff, nested inside the existing support area so it
inherits identity bootstrap and the query client — but with its own shell.
The user-facing support layout is a 448px mobile column with a bottom tab bar,
which is right for filing one ticket and wrong for working a queue.
SupportNavbar hides itself on these routes for the same reason.
| Route | What it does |
|---|---|
| Overview | The observability page. Every number already existed in the API and had nowhere to be seen. |
| Queue | Filterable review with approve / reject / takedown, the incomplete-takedown sweep, one-click revocation retry. |
| DMCA | Notices with their sworn elements shown; counter-notices with the restore window rendered as a countdown. Restore is disabled until the window opens, with the reason on screen. |
| Creators | §512(i) strike record and termination, with a standing warning that no threshold terminates on its own. |
Three design decisions to bring to an interview
1. Every number is paired with what it means. "3 incomplete takedowns" tells a person nothing unless it also says that an incomplete takedown is content the platform believes it removed and is still serving. Moderator headcount reads as a warning at 0 (nothing is ever approved) and at 1 (one person is the entire review capacity).
2. A 502 on takedown is a result, not a transport failure. The decision applied and the Stream asset survived: the content is out of the catalog and still playing for anyone holding a token issued in the last ten minutes. The service resolves that state and the UI says exactly that. Letting the error propagate would show "request failed" for a takedown that half-succeeded — sending a moderator in precisely the wrong direction.
3. Legal deadlines are rendered as clocks, and controls are disabled until they are legally available. The counter-notice restore window is a countdown, not a date string, and the restore button cannot be pressed early.
This is the difference between an admin CRUD screen and an operator console: the interface encodes the consequences of the action, not just the action.
Interview surface this opens
- Designing for operators under statutory deadlines (DMCA §512 timelines)
- Representing partial failure in a UI without lying in either direction
- Why an internal tool needs a different shell than the consumer app it lives in
- Observability as a product surface: pairing every metric with its meaning