feat(admin-ui): Next.js admin UI for task monitoring#19
Merged
Conversation
Standalone Next.js app that points at a Firestore database, lists tasks from the onceTasks collection, and exposes Reset / Cancel / Delete that mirror the library's Manager semantics. - Server-side @google-cloud/firestore reads + writes; browser only talks to local API routes (no Firebase SDK). - Task type and environment dropdowns populated by skip-scan distinct-value discovery on the collection - no consumer code or env vars required. - Outbound links (View Logs, Gmail thread, conversation deep-link, etc.) configured via YAML at ONCETASK_LINKS_CONFIG; mustache-style placeholders with auto URL-encoding and a 'requires' field for conditional rendering. - React Query handles polling and mutation invalidation across list and detail panes. - No built-in auth - deploy behind oauth2-proxy / Caddy forward_auth / Cloudflare Access / similar.
- Label each filter dropdown with its name (Status, Task type, Environment) above the Select trigger; "all" option is just "All" now that the label provides context. - Replace package-lock.json with pnpm-lock.yaml; update README.
Client owns the orderBy policy per status; server accepts orderBy+orderDir params and applies them. Drops the per-status default orderBys and the JS re-sort so the displayed order matches what the server actually returned. Also aligns the doneAt zero-value with the Go production code ("" not "0001-01-01T00:00:00Z").
New top-level tab structure: Browse (existing UI, unchanged) and Index-aware (Active / Done / Resource sub-tabs). Index-aware mode only issues queries that the production Go code's Firestore indexes already serve — no new composite indexes are prompted. Sub-state filtering for Active (Ready/Leased/Waiting) is done client-side over the env+type result set.
Client-side filters (status derivation, resourceKey substring, indexed view sub-state) apply over what the server returns. Bumping the per-query cap to 2000 widens the window the user can filter through. The new FetchInfo banner makes the cap explicit — shown/fetched counts, and an amber warning when the cap was the constraint so operators know more data may exist. Adds DoneTasksView client-side status filter (Completed/Failed/Cancelled) as a four-button toggle over the fetched rows.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The oncetask library is a Go module — no HTTP API, no UI. Every consumer that wants to inspect or operate on the
onceTaskscollection (View Logs, Reset a failed task, Cancel a runaway recurrence) has to build their own admin tooling against Firestore directly. Donna's Den is the existing example of this duplication.Solution
A standalone Next.js app in
admin-ui/that points at a Firestore project, lists tasks, and exposes the library's mutation surface. Deploy alongside your workers, share with whoever needs ops access.Architecture
No Firebase SDK on the client. The Next.js server holds the credential and proxies every read/write through local API routes. The browser only talks to the local app.
Generic by default
SELECT type LIMIT 1thenNOT IN [...]until empty). No consumer code, no hardcoded enums.ONCETASK_LINKS_CONFIG. Mustache-style{path.to.field}placeholders with auto URL-encoding, plus arequires:field for conditional rendering. Seelinks.example.yaml.Mutations
resetTask,cancelTask,deleteTaskinsrc/lib/oncetask/mutations.tsmirror the field updates inpkg/oncetask/reset.goandpkg/oncetask/cancellation.go. React Query handles invalidation so list + detail refresh after every action.Auth
Nothing built in. README points operators at oauth2-proxy / Caddy
forward_auth/ Cloudflare Access / Tailscale. The server validates that mutations target tasks belonging to the task's stampedenv— accidentally pointing at the wrong project still requires the task ID to exist there.Stack
Next.js 16 (App Router) · React 19 · TypeScript · Tailwind v4 · shadcn (base-ui) · @google-cloud/firestore · @tanstack/react-query · js-yaml · lodash.get.
Quick start