From 265095b774083138393c9c9f45585eb44c67d5c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sat, 9 May 2026 17:02:59 +0200 Subject: [PATCH] chore(planning): mark Phase 4b admin refactor complete Co-Authored-By: Claude Opus 4.7 (1M context) --- .planning/STATE.md | 18 +++--- .planning/phase-4b-report.md | 110 +++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+), 7 deletions(-) create mode 100644 .planning/phase-4b-report.md diff --git a/.planning/STATE.md b/.planning/STATE.md index 2dd33f8e1..376a84258 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -64,13 +64,17 @@ Last update: 2026-05-09T16:10:00+02:00 ## Phase 4b — Admin Panel refactor (Settings-style dynamic routes) -- [ ] /admin/[section]/page.tsx + admin-shell sidebar -- [ ] Sections moved to /admin/ routes (system-status, services, integrations, feedback, reminders, users, api-tokens, login-overview, backups, danger-zone) -- [ ] Status-card CTAs use real routes (no #anchor) -- [ ] i18n keys reorganised under admin.section..\* -- [ ] Sidebar nav expandable Admin group -- [ ] Legacy #anchor → new route redirect in proxy.ts -- [ ] Bundle-size win on /admin overview verified +- [x] /admin/[section]/page.tsx + admin-shell sidebar — commit `d8b71b3` +- [x] Sections moved to /admin/ routes (system-status, general, services, integrations, feedback, reminders, users, api-tokens, login-overview, backups, danger-zone) — commit `12b280b` +- [x] T2.6 Backups dedicated admin section + manual run — commit `69c5225` +- [x] T2.7 /admin/users with role filters + force-logout — commit `5957e18` +- [x] i18n keys reorganised under admin.section..\* (EN + DE parity) — commit `a34cd8c` +- [x] Sidebar nav expandable Admin group — commit `73965cd` +- [x] Status-card CTAs use real routes (no #anchor) — commit `961e2a9` +- [x] Legacy section-anchor → new route redirect in proxy.ts (path-style only; pure `#fragments` cannot be redirected server-side, callsites rewritten directly) — commit `7a47532` +- [x] Tests: status-card-grid asserts route existence + per-section render smoke + i18n parity (existing) — commit `1a7e3d6` +- [x] Bundle-size win verified architecturally (overview no longer imports section components); `pnpm build` blocked by pre-existing Node 25 issue +- Result: ok / commits `d8b71b3 12b280b 69c5225 5957e18 a34cd8c 73965cd 961e2a9 7a47532 1a7e3d6` - Detailed report: `.planning/phase-4b-report.md` ## Phase 5 — UX polish diff --git a/.planning/phase-4b-report.md b/.planning/phase-4b-report.md new file mode 100644 index 000000000..5c93e1841 --- /dev/null +++ b/.planning/phase-4b-report.md @@ -0,0 +1,110 @@ +# Phase 4b — Admin Panel refactor report + +Status: done. 9 atomic commits, `pnpm typecheck`, `pnpm lint` (warnings +only, all pre-existing), `pnpm test --run` (96 files / 745 tests) all +green. `pnpm format:check` flags two pre-existing planning reports +owned by Phase 3/4 — not touched by this phase. + +## Scaffolding (commit `d8b71b3`) + +`src/components/admin/section-slugs.ts` declares the 11-slug enum +(`system-status`, `general`, `services`, `integrations`, `feedback`, +`reminders`, `users`, `api-tokens`, `login-overview`, `backups`, +`danger-zone`) plus the legacy-anchor → slug map. The shell at +`src/components/admin/admin-shell.tsx` mirrors ``: mobile +horizontal scroll strip, desktop sticky 220px sidebar with an "Overview" +entry pointing back to `/admin`. Dynamic route at +`src/app/admin/[section]/page.tsx` is a server component that calls +`generateStaticParams()` and delegates to a client renderer in +`renderer.tsx`. The renderer's `switch` carries an `_exhaustive: never` +guard so a future slug added to the enum without a wired component is +caught at type-check time. + +## Section relocation + overview reduction (commit `12b280b`) + +Every existing component (`SystemStatusSection`, `GeneralSettingsSection`, +`ServicesSection`, `UmamiSection`, `GlitchtipSection`, +`WebPushVapidSection`, `BugReportSection`, `FeedbackInboxSection`, +`RemindersSection`, `UserManagementSection`, `ApiTokenOverviewSection`, +`LoginOverviewSection`, `DangerZoneSection`) had its `id` prop made +optional so it works under the new dynamic route. The composite +`` at +`src/components/admin/integrations-group-section.tsx` wraps the four +integration sub-sections under one `/admin/integrations` route. The +old monolithic `/admin/page.tsx` is now a true overview: status-card +grid + a quick-jump menu of the 11 sections. + +## Backups (commit `69c5225`, T2.6) + +`GET /api/admin/backups` returns metadata-only DataBackup rows +(username, type, byte size, createdAt — never the encrypted blob). +`POST /api/admin/backups/run` enqueues an ad-hoc `data-backup` pg-boss +job using the global boss instance from `boss-instance.ts`; returns +503 if the worker isn't running. `` lists rows in a +table with size pretty-printed (B/KB/MB) and a "Backup now" button +that surfaces a sonner toast. + +## Users management (commit `5957e18`, T2.7) + +`` now has filter pills (All / Admins / Users) +and a per-row force-logout action behind an `` +confirmation. The "suspended" filter from the original spec was +mapped to `all` because the User model has no `suspended` field +today — documented in a comment in `user-management-section.tsx` for +forward compatibility. `POST /api/admin/users/[id]/force-logout` +deletes every Session row for the target and writes +`admin.user.force-logout` to the audit log. Rejects self-target via +the `disabled` flag on the row button. + +## i18n (commit `a34cd8c`) + +`admin.shell.{overview,sectionsNav}` plus +`admin.section..{title,subtitle}` for every slug, `users.filter.*` +for the filter pills, and the new force-logout / backups copy. EN +canonical, DE mirrors. The existing `i18n-locale-integrity.test.ts` +already enforces parity at PR time — no new test was needed. + +## Sidebar nav (commit `73965cd`) + +`src/components/layout/sidebar-nav.tsx` now reads `useAuth()` to +conditionally render an "Admin Console" entry between Bug Report and +Settings. When the active route starts with `/admin/`, the entry +expands into an indented sub-list of every section (collapsed-mode +keeps the icon-only button without the sub-list). + +## Status-card hrefs (commit `961e2a9`) + +Every CTA in `status-card-grid.tsx` switched from `/admin#section-*` to +`/admin/`. The honesty problem from v1.4.6 T6 is permanently +fixed. + +## Legacy redirects (commit `7a47532`) + +`proxy.ts` adds `LEGACY_ADMIN_ANCHORS`, a 13-entry 301 map from +`/admin/section-` (path style) to `/admin/`. **Pure URL +fragments (`#foo`) never reach the server**, so the only legacy form +this phase can handle server-side is the path-style mistype. Every +in-app callsite has been rewritten to use the new route directly, so +no live link relies on the redirect — it's defence for hand-typed +bookmarks and referrer rewriters. + +## Tests (commit `1a7e3d6`) + +`status-card-grid.test.tsx` replaces the v1.4.6 anchor-id allowlist +with three checks per card: href matches `/admin/`, slug is in +`ADMIN_SECTION_SLUGS`, and `src/app/admin/[section]/page.tsx` exists +on disk via `existsSync`. New `sections.test.tsx` SSR-renders every +section component reachable from the renderer (uses `vi.mock` on +TanStack Query, the auth hook, and `next/navigation`) and asserts the +v1.5 fold-ins (filter pills, "Backup now" button) paint. + +## Bundle-size verification + +`pnpm build` aborts on the documented Node 25 `Cannot read private +member #state` regression (already noted in STATE.md). Static +inspection of the dependency graph confirms the win: `/admin/page.tsx` +now imports only ``, ``, +``, and `useAdminSettings`. The 13 section components +that previously lived in the monolithic page no longer ship in the +overview chunk; each is reachable only from its own dynamic-route +bundle.