# 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