--- 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: () => , }) ``` 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 22–39) 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 = () => ` 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` 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.