feat(flutter): admin parity slice — requests, quarantine, users, invites #33
Reference in New Issue
Block a user
Delete Branch "dev"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Mirrors the operator-actionable subset of the web admin surface to the Flutter client. Closes the admin chunk of Fable umbrella #356 for v1; Integrations + Overview tabs are an explicit carve-out (mobile-low-value, deferred).
What ships
Three new admin surfaces under
/admin/admin/requests) — pending Lidarr requests with Approve / Reject. Approve fires immediately; Reject confirms via dialog. Both optimistic with rollback. Joinsuser_id→usernameclient-side viaadminUsersProvider(server doesn't denormalize the requester username)./admin/quarantine) —ExpansionTileper aggregated track row (collapsed: title + report-count + top reason; expanded: per-user reports). Three-dot menu offers Resolve / Delete file / Delete via Lidarr; both deletes route through aTypedConfirmSheetthat requires typing "DELETE"./admin/users) — Users + Invites in one scroll. User rows tap into an edit sheet (admin / auto-approve toggles, admin-supplied password reset, typed-confirm delete). Invites section shows token (monospace, copy button), optional note, redeemed badge; "Generate" opens a small note-input dialog (server hardcodes 24h TTL).Admin landing + entry points
AdminLandingScreen— list of three section cards with at-a-glance counts driven byadminCountsProvider(parallel fan-out to the three list endpoints).isAdmin. Reach is 2 taps.Home AppBar simplification
HomeScreenand the other top-level screens (Library, Search, Discover, Playlists, Settings) now adopt a sharedMainAppBarActionswidget:[Admin if isAdmin]This drops Home from a crowded 5-icon AppBar and gives every screen a consistent shape.
Routing
buildRouterredirect closure now refuses any/admin/*path whenuser.isAdmin == false(serverRequireAdminmiddleware remains the actual authority — this is UX).Server contract — verified, not assumed
Spent the diagnostic time up-front against
internal/api/admin_*.goto lock down JSON shapes. Notable client-affecting bits:GET /admin/usersandGET /admin/invitesare enveloped ({"users": [...]},{"invites": [...]}) — not flat lists.PUT /admin/users/{id}/auto-approvebody field isauto_approve(different from theauto_approve_requestsresponse field).POST /admin/users/{id}/reset-passwordtakes admin-supplied{password: "..."}and returns 204 — there is no server-generated-password mode, so the UI is a typed-input form.POST /admin/invitestakes optional{note: "..."}— server hardcodes 24h TTL, so the UI offers a note input rather than an expiry input.report_count,reason_counts, nestedreports[]) — UI is anExpansionTileshowing the rollup with per-user reports underneath.adminUsersProvider(free since the Users screen warms it).Spec + plan in
docs/superpowers/specs/2026-05-08-flutter-admin-parity-slice-design.mdanddocs/superpowers/plans/2026-05-08-flutter-admin-parity-slice.md(gitignored, local-only).Test plan
CI runs
flutter analyze+flutter test+ APK build on theflutter-cirunner./homeshows Admin (only when logged in as admin)/admin→ tap each section card → verify counts and actions/register, then promote them in/admin/usersNotes for next slices
AdminUsersControllerbuild-only landed in slice 2 to feed the Requests-screen username join; mutations layered on in slice 3.