Files
archivmail/features/PROJ-77-admin-tabs-dynamic-import.md
sysopsandClaude Sonnet 5 d1b4497893 fix(PROJ-79): next lint kaputt seit Next-16-Upgrade repariert + 30 Findings gefixt
npm run lint rief next lint auf, das es in Next.js 16 nicht mehr gibt —
seit dem Next-16-Upgrade lief effektiv gar kein Lint mehr. Umgestellt auf
eslint . mit Flat-Config (eslint.config.mjs statt .eslintrc.json). Der
dadurch wieder sichtbare Lint-Lauf zeigte 30 Findings (25 Fehler,
5 Warnungen), alle gefixt:

- 19x react-hooks/set-state-in-effect: Loading-States wo möglich als
  echte Ableitung statt eigenem Effect-State (use-mobile.tsx komplett auf
  useSyncExternalStore umgebaut), sonst async-Wrapper mit Cancel-Guard um
  bestehende Loader — Timing/Ladeanzeige unverändert.
- react-hooks/refs (useSearch.ts): Ref-Schreibzugriff aus dem Render in
  einen Effect verschoben.
- 4x no-html-link-for-pages: <a href> durch next/link ersetzt in
  admin/login, forgot-password, signup.
- Rest (exhaustive-deps, no-img-element, unused disable) einzeln gefixt.
- 4 bewusst belassene disable-Kommentare mit Begründung (shadcn/ui-Datei,
  QR-Code-data-URL, Full-Reload nach Auth laut Projektregel).

eslint-Major-Upgrade auf 10 selbst bleibt blockiert: eslint-plugin-react/
jsx-a11y/import unterstützen ESLint 10 in ihrer aktuellen Latest-Version
noch nicht (Crash beim Laden), siehe Feature-Spec PROJ-79.

Verifiziert auf 132 (Build-Sandbox, kein Live-Deploy): npm ci/tsc/lint/
build grün, 8 Kern-Routen per Standalone-Server auf HTTP 200 geprüft.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019j28kGcaJAhBnrYX34hGdt
2026-08-05 21:14:17 +02:00

85 lines
3.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: PROJ-77
title: Admin-Tab-Bundle-Optimierung (dynamic import statt 19 statische Imports)
status: Deployed
created: 2026-08-05
---
## Problem
`src/app/admin/page.tsx` (Zeile ~22-40) importiert alle 19 Admin-Tab-
Komponenten statisch. Aktuell gibt es im gesamten Projekt 0 `next/dynamic`-
Imports. Radix `TabsContent` unmountet zwar inaktive Tabs zur Laufzeit
(kein gleichzeitiges Rendern), aber das initiale JS-Bundle enthält trotzdem
den Code aller 19 Tabs, auch wenn ein Nutzer nur einen einzigen Tab je
Sitzung öffnet.
Kein Crash-Bezug, reine Bundle-Größen-/Ladezeit-Optimierung.
## Lösung (Vorschlag)
Jede Admin-Tab-Komponente auf `next/dynamic` mit Loading-State umstellen,
z.B.:
```ts
const DashboardTab = dynamic(() => import('@/components/admin/tabs/DashboardTab'), {
loading: () => <TabSkeleton />,
})
```
Umfang: 19 Komponenten, jeweils Import-Umstellung + einheitlicher
Loading-Skeleton. Vor Umsetzung: kurzer Bundle-Size-Vergleich
(`next build` Output) vorher/nachher, um den tatsächlichen Gewinn zu
beziffern.
## Implementation Notes
Umgesetzt am 2026-08-05.
**Geänderte/neue Dateien**
- `src/app/admin/page.tsx`: 18 statische Tab-Imports (Zeile 2239) durch
`next/dynamic`-Deklarationen ersetzt. Alle Tab-Komponenten sind Named
Exports, daher jeweils
`dynamic(() => import("…/XyzTab").then((m) => m.XyzTab), { loading })`.
Eine gemeinsame `const loading = () => <TabSkeleton />` wird für alle
18 Tabs wiederverwendet. `ResetPasswordDialog`/`DeleteUserDialog`
(Global-Dialoge, immer sichtbar) bleiben bewusst statisch.
- `src/components/admin/TabSkeleton.tsx` (neu): einheitlicher
Lade-Platzhalter auf Basis der vorhandenen shadcn/ui-`Skeleton`-
Komponente, mit `aria-busy`/`aria-live` und sr-only-Text.
**Props-Typisierung:** `dynamic()` inferiert die Props über den
`.then()`-Rückgabewert korrekt; ein explizites `ComponentType<Props>`
war nicht nötig. `npm run build` (inkl. TypeScript-Check) läuft fehlerfrei.
**Bundle-Size-Vergleich** (Next.js 16 / Turbopack zeigt keine Größen im
Build-Output; gemessen als Summe der im prerenderten
`.next/server/app/admin.html` referenzierten `_next/static/*.js`-Dateien,
unkomprimiert):
| | Dateien | Initiales JS |
|---|---|---|
| vorher | 17 | 1035,6 KB |
| nachher | 18 | 830,7 KB |
| Delta | +1 | **204,9 KB (19,8 %)** |
Der Code der Tabs liegt jetzt in separaten Chunks, die erst beim
Aktivieren des jeweiligen Tabs geladen werden.
Deployed auf 131 (Produktiv) am 2026-08-05.
**Hinweis:** `dynamic()` läuft hier mit Default-`ssr: true`, d.h. der
initial aktive Tab wird weiterhin serverseitig vorgerendert — kein
Flash-of-Skeleton beim ersten Laden des Dashboards.
## Acceptance Criteria
- [x] Alle Admin-Tabs (18 Tab-Komponenten) per `next/dynamic` geladen,
mit Loading-Skeleton.
- [x] `npm run build` Bundle-Size-Report zeigt messbare Reduktion der
initialen Admin-Route-Chunk-Größe (204,9 KB / 19,8 %).
- [ ] Funktionale Regression: alle Tabs weiterhin normal nutzbar
(manueller Klick-Test durch alle Tabs) — **noch zu verifizieren**,
kann nicht automatisiert erfolgen; offen für QA.