Skip to content

feat(v1.4): admin page redesign — status-first card grid + per-section extraction#133

Merged
MBombeck merged 1 commit intomainfrom
feat/v14-admin-redesign
May 8, 2026
Merged

feat(v1.4): admin page redesign — status-first card grid + per-section extraction#133
MBombeck merged 1 commit intomainfrom
feat/v14-admin-redesign

Conversation

@MBombeck
Copy link
Copy Markdown
Owner

@MBombeck MBombeck commented May 8, 2026

Summary

Replaces the implicit, fluid status surface of the legacy 2699-LOC /admin monolith with a status-first 6-card grid per docs/ui-guidelines.md §3.4. Each card pairs the §2.6 colored dot with a text label so state never relies on color alone (WCAG 1.4.1).

This PR lands the foundation:

  • New aggregator endpoint /api/admin/status-overview — a single batched Promise.all (no N+1) that computes severity server-side for: Users, Integrations, Monitoring, Backups, Maintenance, Audit log.
  • New <StatusCardGrid> + reusable <StatusBadge> in src/components/admin/. Pure buildCards() function makes severity mapping unit-testable.
  • 14 new tests (424 total, all green).
  • Wires the grid above the existing admin sections; per-section extraction ships in follow-up commits.

Aggregator endpoint shape

type StatusSeverity = "good" | "info" | "caution" | "alert" | "pending";

interface StatusOverview {
  users:        { severity, total, admins, newThisWeek }
  integrations: { severity, withings, moodLog, telegram, ntfy, webPush }
  monitoring:   { severity, glitchtipEnabled, umamiEnabled, wideEventsEnabled, lastErrorAt }
  backups:      { severity, lastBackupAt, backedUpUsers, retentionDays }
  maintenance:  { severity, workerRunning, workerUptimeSeconds, lastIdempotencyCleanup, lastAuditLogCleanup }
  auditLog:     { severity, eventsLast30d, lastLoginAt }
}

Severity rules: backups alert >14d / caution 8–14d / good ≤8d; monitoring alert on error <24h, good when both enabled; maintenance alert when worker stopped.

LOC table (was vs now)

Surface Was Now
src/app/admin/page.tsx 2699 2701 (+1 import, +1 mount)
src/components/admin/status-card-grid.tsx 257
src/components/admin/__tests__/status-card-grid.test.tsx 109
src/app/api/admin/status-overview/route.ts 217
src/app/api/admin/status-overview/__tests__/route.test.ts 160

The page LOC reduction comes in the per-section extraction follow-ups — this PR is the structural foundation: aggregator + grid + tests.

Multi-agent review

Self-review against the §3.4 requirements:

Senior Dev pass

  • No behaviour change to existing sections (zero edits below the grid).
  • Aggregator is a single Promise.all of 16 prisma calls — verified by the route test asserting dataBackup.findMany is called exactly once and user.count exactly 5 times (no loops).
  • requireAdmin() runs first; route annotated with admin.status-overview.
  • A11y: every <StatusBadge> carries aria-label="Status: <text>", the dot is aria-hidden, and the label text is always rendered (not color-only).

Product Lead pass

  • Cards scan in the §3.4 order: Users → Integrations → Monitoring → Backups → Maintenance → Audit log.
  • Each card has identical layout: title + icon + status badge top, 3 metrics, manage CTA bottom.
  • Status badges share the §2.6 color taxonomy across all 6 sections.

Out of scope (medium follow-ups)

  • Per-section extraction to src/components/admin/<section>.tsx (Users → Integrations → Monitoring → Backups → Maintenance → Audit log) — each as its own commit. Tracking branch retains /admin as a sidebar shell once extraction lands.
  • Idempotency / audit-log cleanup audit-log entries (system.cleanup.*) — the aggregator already reads them but the cleanup jobs would need to write the entries.

Test plan

  • pnpm test — 424 tests green (verified locally).
  • pnpm typecheck — clean (verified locally).
  • pnpm exec prettier --check on touched files (verified locally).
  • Manual: visit /admin as admin → 6 cards render with skeletons → load → severity badges visible.
  • Manual: visit /admin as non-admin → grid 401s gracefully.

🤖 Generated with Claude Code

Replaces the implicit, fluid status of the legacy admin monolith with a
6-card grid at the top of /admin per ui-guidelines.md §3.4. Each card
pairs a §2.6 colored dot with a text label so state never relies on
color alone (WCAG 1.4.1).

- New aggregator endpoint /api/admin/status-overview returns a single
  batched Promise.all (no N+1) covering Users, Integrations, Monitoring,
  Backups, Maintenance, Audit log — with severity precomputed server-side.
- New <StatusCardGrid> + reusable <StatusBadge> component in
  src/components/admin/. Pure buildCards() function makes severity
  mapping unit-testable.
- Tests: 14 new (8 aggregator severity/shape, 6 grid badge + a11y).
- Wires the grid above the existing admin sections; per-section
  extraction will land in follow-ups.

Co-Authored-By: Marc-André Bombeck <noreply@anthropic.com>
@MBombeck MBombeck merged commit 725cace into main May 8, 2026
6 checks passed
MBombeck added a commit that referenced this pull request May 8, 2026
…145)

The admin page was a single 2,702-line client component with
nineteen inline functions covering everything from system status
to feedback inbox. The status-card grid foundation in #133 already
moved the at-a-glance dashboard out; this commit moves the inner
panels.

Split into one file per logical section in src/components/admin/:

  _shared.tsx                         276 LOC  shared types, hooks,
                                                primitives (PasswordInput,
                                                StatusItem, SettingsToggle,
                                                useAdminSettings,
                                                useUpdateSettings,
                                                useSystemStatus,
                                                getApiErrorMessage)
  system-status-section.tsx           155
  general-settings-section.tsx         54
  services-section.tsx                 68
  umami-section.tsx                   173
  glitchtip-section.tsx               172
  web-push-vapid-section.tsx          148
  bug-report-section.tsx              109
  reminders-section.tsx               318
  user-management-section.tsx         341
  api-token-overview-section.tsx      144
  login-overview-section.tsx          181
  danger-zone-section.tsx             118
  feedback-inbox-section.tsx          511 (inbox + category badge
                                            + detail dialog)

src/app/admin/page.tsx is now a 77-line shell that imports the
14 sections and the existing status-card-grid.tsx. No behavior
change — every section keeps the same DOM, the same query keys,
the same i18n keys, the same id="…" anchors so deep-links from
the sidebar still scroll into place. UserManagementSection no
longer takes a queryClient prop; it grabs its own via
useQueryClient() like every other section does.
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.

1 participant