Skip to content

Dashboard redesign: React + shadcn/ui, live charts, history API, DLQ inspector - #10

Merged
emoss08 merged 4 commits into
mainfrom
claude/custom-cdc-review-zth3ax
Aug 11, 2026
Merged

emoss08 merged 4 commits into
mainfrom
claude/custom-cdc-review-zth3ax

Conversation

@emoss08

@emoss08 emoss08 commented Aug 11, 2026

Copy link
Copy Markdown
Owner

What does this PR do?

Rebuilds the embedded dashboard from scratch and enriches the server APIs that feed it.

UI rewrite (React 19 + TypeScript + Vite + Tailwind v4 + shadcn/ui):

  • zod validates every API response at the fetch boundary, react-hook-form + zodResolver drives the backfill form, and TanStack Table v9 powers all tables (sortable, filterable, shared feature config).
  • Live charts with recharts (throughput events/s and WAL-lag area charts over a 30-minute window) and nivo (operation-mix donut, busiest-tables stacked bars), theme-aware in light and dark.
  • Overview page shows stat cards (throughput, WAL lag, in-flight, dead letters), an instance panel (version, slot, publication, current LSN, uptime), and per-sink health including breaker state, retries, and an error-type breakdown.
  • DLQ triage gets a payload inspector dialog: the parked (already-transformed) event as pretty-printed JSON plus sink/table/LSN/attempts metadata, alongside the existing retry/discard/retry-all actions.
  • Resilience touches: an offline banner when the gateway stops responding (instead of silently frozen numbers), a responsive sidebar that collapses to an icon rail on narrow screens (DLQ count becomes a red dot), and per-page document titles.

Server-side:

  • New /api/history endpoint backed by a 5-second sampler with a 30-minute ring buffer, so charts have real data the moment the dashboard opens and survive page reloads.
  • /api/stats now reports version/slot/publication, an aggregated per-operation counts map, per-sink errors_by_type, and DLQ parked/retried totals.
  • --version flag and version stamping via goreleaser ldflags.

The built ui/dist is committed and embedded per the existing PocketBase-style setup, so go build still needs no Node.

Type of change

  • New feature / sink
  • Refactor / chore

Checklist

  • go test ./... passes (including -race)
  • gofmt / go vet are clean
  • Tests added or updated for changed behavior (/api/stats new fields, /api/history shape, sampler ring-buffer cap)
  • README.md / CLAUDE.md updated if env vars or behavior changed (dashboard section, screenshot at docs/dashboard.png, UI build instructions)
  • If this touches delivery semantics (LSN acknowledgment, sink error handling, TOAST handling): not touched — this PR is observability/UI only

Notes for the reviewer

  • The dashboard was verified with headless-Chromium screenshots against a mock API in both themes and at narrow viewports, with zero console errors; docs/dashboard.png in the README is one of those captures.
  • The history sampler reads from the process's own Prometheus registry, so it adds no new dependencies and costs one gather every 5 seconds.
  • Chart libraries are split into their own Vite chunk (~189 KB gzipped) so the core app stays small; everything is served from the embedded FS as before.

🤖 Generated with Claude Code

https://claude.ai/code/session_019dEj24Ta2o9QxzW9ZrSPtb


Generated by Claude Code

claude added 4 commits August 11, 2026 16:47
…HF + zod

Full rewrite of the embedded dashboard from the minimal Svelte page to a
proper product UI, still compiled into the binary PocketBase-style
(ui/dist committed, go:embed, zero deployment):

- App shell: slim sidebar navigation (Overview, Tables, Backfill, Dead
  letters with a live count badge), brand mark, streaming status with
  uptime in the footer, sticky page header with LSN and readiness
  badges, dark/light theme toggle (system default, no flash on load).
- shadcn/ui component system (zinc theme, new-york style): button,
  badge, card, input, label, table, dialog, form, and sonner toasts,
  with Radix primitives underneath.
- TanStack Table v9 (the new tree-shakable useTable/tableFeatures API)
  powers every table — sinks, table activity, backfill status, and the
  dead-letter queue — with click-to-sort headers and global filtering.
- react-hook-form + zod drive the backfill trigger dialog (validated
  schema.table identifier or empty for all); zod also validates every
  API response so a Go/UI contract drift fails loudly instead of
  rendering NaNs.
- TanStack Query polls /api/stats and /dlq every 2s with mutations and
  cache invalidation for retry/discard/retry-all/backfill actions, each
  confirmed by a toast; retry-all sits behind a confirm dialog that
  explains the stale-state caveat.
- Overview: stat cards with a live emerald throughput sparkline
  (client-computed from counter deltas), WAL lag, in-flight, DLQ depth;
  per-sink delivery table with health dots and breaker badges. Tables
  page adds share-of-traffic bars; every page has a designed empty
  state.
- Toolchain: Vite 7 + TypeScript strict + Tailwind v4; the CI dashboard
  job (npm ci && npm run build) now type-checks the UI too via tsc -b.
  Docs updated (README, CLAUDE.md, CONTRIBUTING.md, embed.go).

Verified by driving all four pages in headless Chromium against a mock
API (light + dark) with zero console errors.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019dEj24Ta2o9QxzW9ZrSPtb
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019dEj24Ta2o9QxzW9ZrSPtb
…cher stats

The dashboard now tells the full pipeline story with real visualizations:

Backend:
- New /api/history endpoint backed by a server-side sampler: a ring
  buffer of throughput/WAL-lag/in-flight/DLQ samples (5s interval, 30min
  window) collected from the process's own metrics, so charts have data
  the moment the page opens and survive reloads. Started/stopped with
  the HTTP server.
- /api/stats grew: build version, slot and publication names, a global
  operation mix (INSERT/UPDATE/DELETE/READ/TRUNCATE totals), per-sink
  error breakdown by type (timeout/circuit_open/other), and DLQ
  parked/retried all-time counters.

Frontend:
- recharts: throughput area chart (events/s derived from history
  deltas) and a replication-lag chart with an explanation of what
  sustained growth means, both themed for light/dark via a MutationObserver
  on the root class.
- nivo: operation-mix donut with center total on the overview, and
  horizontal stacked bars of the busiest tables by operation on the
  Tables page, with a shared operation color legend.
- New Instance panel: version, streaming/ready badges, uptime, LSN,
  slot, publication, tables observed, DLQ state. Dead-letters stat card
  now shows all-time parked/retried; sink failure counts carry an
  error-type breakdown on hover.
- zod schemas extended for the new fields (with tolerant defaults so
  older gateways still render); vendor chunk splitting (react/charts/
  data) keeps initial parse reasonable.

Verified by driving every page in headless Chromium against a mock API
in both themes with zero console errors; Go suite passes with -race.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019dEj24Ta2o9QxzW9ZrSPtb
…idebar

- DLQ page gains an event inspector dialog showing the parked
  (already-transformed) payload plus sink/table/LSN/attempt metadata,
  so poison events can be diagnosed without redis-cli.
- The shell shows a "Cannot reach GTC" banner when /api/stats polling
  fails, instead of silently freezing stale numbers.
- Sidebar collapses to an icon rail below lg breakpoints (DLQ count
  becomes a red dot), and the document title follows the active page.
- Remove the dead sparkline component and hook left over from the
  recharts migration; drop the empty manual "react" chunk from the
  Vite build.
- Extend server tests: instance info + operations map + errors_by_type
  in /api/stats, /api/history sample shape, and the sampler ring-buffer
  cap.
- README: add a dashboard screenshot (docs/dashboard.png).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019dEj24Ta2o9QxzW9ZrSPtb
@emoss08
emoss08 merged commit 3ec4693 into main Aug 11, 2026
6 checks passed
@emoss08
emoss08 deleted the claude/custom-cdc-review-zth3ax branch August 11, 2026 18:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants