perf(PROJ-77): Admin-Tabs per next/dynamic laden statt statisch importieren

18 Admin-Tab-Komponenten wurden bisher alle statisch in src/app/admin/page.tsx
importiert, unabhängig davon welcher Tab tatsächlich geöffnet wird. Umstellung
auf next/dynamic mit gemeinsamem TabSkeleton-Loading-State reduziert das
initiale JS-Bundle der Admin-Route um 204,9 KB (-19,8%). Initial aktiver Tab
bleibt weiterhin serverseitig vorgerendert (ssr: true), kein Skeleton-Flash
beim ersten Laden.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019j28kGcaJAhBnrYX34hGdt
This commit is contained in:
sysops
2026-08-05 15:05:24 +02:00
co-authored by Claude Sonnet 5
parent 209fdeb8ad
commit e59b11743a
3 changed files with 183 additions and 19 deletions
+79 -19
View File
@@ -1,6 +1,7 @@
"use client";
import { useEffect, useMemo, useState } from "react";
import dynamic from "next/dynamic";
import { useAuth } from "@/hooks/useAuth";
import { useLDAPConfig } from "@/hooks/useLDAPConfig";
import { useTenantLDAPConfig } from "@/hooks/useTenantLDAPConfig";
@@ -18,26 +19,85 @@ import { Navbar } from "@/components/navbar";
import { Skeleton } from "@/components/ui/skeleton";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
// Tab components
import { DashboardTab } from "@/components/admin/tabs/DashboardTab";
import { ServicesTab } from "@/components/admin/tabs/ServicesTab";
import { UsersTab } from "@/components/admin/tabs/UsersTab";
import { AuditTab } from "@/components/admin/tabs/AuditTab";
import { ImportTab } from "@/components/admin/tabs/ImportTab";
import { SecurityTab } from "@/components/admin/tabs/SecurityTab";
import { LDAPTab } from "@/components/admin/tabs/LDAPTab";
import { TenantLDAPTab } from "@/components/admin/tabs/TenantLDAPTab";
import { TenantsTab } from "@/components/admin/tabs/TenantsTab";
import { CertTab } from "@/components/admin/tabs/CertTab";
import { ModulesTab } from "@/components/admin/ModulesTab";
import { IMAPSettingsTab } from "@/components/admin/tabs/IMAPSettingsTab";
import { RetentionTab } from "@/components/admin/tabs/RetentionTab";
import { ArchivingRulesTab } from "@/components/admin/tabs/ArchivingRulesTab";
import { RoutingRulesTab } from "@/components/admin/tabs/RoutingRulesTab";
import { QuotaTab } from "@/components/admin/tabs/QuotaTab";
import { SMTPOutTab } from "@/components/admin/tabs/SMTPOutTab";
import { DSGVOTab } from "@/components/admin/tabs/DSGVOTab";
import { ResetPasswordDialog, DeleteUserDialog } from "@/components/admin/UserDialogs";
import { TabSkeleton } from "@/components/admin/TabSkeleton";
// Tab components — dynamisch nachgeladen (PROJ-77), damit das initiale
// Admin-Bundle nicht den Code aller 19 Tabs enthält.
const loading = () => <TabSkeleton />;
const DashboardTab = dynamic(
() => import("@/components/admin/tabs/DashboardTab").then((m) => m.DashboardTab),
{ loading }
);
const ServicesTab = dynamic(
() => import("@/components/admin/tabs/ServicesTab").then((m) => m.ServicesTab),
{ loading }
);
const UsersTab = dynamic(
() => import("@/components/admin/tabs/UsersTab").then((m) => m.UsersTab),
{ loading }
);
const AuditTab = dynamic(
() => import("@/components/admin/tabs/AuditTab").then((m) => m.AuditTab),
{ loading }
);
const ImportTab = dynamic(
() => import("@/components/admin/tabs/ImportTab").then((m) => m.ImportTab),
{ loading }
);
const SecurityTab = dynamic(
() => import("@/components/admin/tabs/SecurityTab").then((m) => m.SecurityTab),
{ loading }
);
const LDAPTab = dynamic(
() => import("@/components/admin/tabs/LDAPTab").then((m) => m.LDAPTab),
{ loading }
);
const TenantLDAPTab = dynamic(
() => import("@/components/admin/tabs/TenantLDAPTab").then((m) => m.TenantLDAPTab),
{ loading }
);
const TenantsTab = dynamic(
() => import("@/components/admin/tabs/TenantsTab").then((m) => m.TenantsTab),
{ loading }
);
const CertTab = dynamic(
() => import("@/components/admin/tabs/CertTab").then((m) => m.CertTab),
{ loading }
);
const ModulesTab = dynamic(
() => import("@/components/admin/ModulesTab").then((m) => m.ModulesTab),
{ loading }
);
const IMAPSettingsTab = dynamic(
() => import("@/components/admin/tabs/IMAPSettingsTab").then((m) => m.IMAPSettingsTab),
{ loading }
);
const RetentionTab = dynamic(
() => import("@/components/admin/tabs/RetentionTab").then((m) => m.RetentionTab),
{ loading }
);
const ArchivingRulesTab = dynamic(
() => import("@/components/admin/tabs/ArchivingRulesTab").then((m) => m.ArchivingRulesTab),
{ loading }
);
const RoutingRulesTab = dynamic(
() => import("@/components/admin/tabs/RoutingRulesTab").then((m) => m.RoutingRulesTab),
{ loading }
);
const QuotaTab = dynamic(
() => import("@/components/admin/tabs/QuotaTab").then((m) => m.QuotaTab),
{ loading }
);
const SMTPOutTab = dynamic(
() => import("@/components/admin/tabs/SMTPOutTab").then((m) => m.SMTPOutTab),
{ loading }
);
const DSGVOTab = dynamic(
() => import("@/components/admin/tabs/DSGVOTab").then((m) => m.DSGVOTab),
{ loading }
);
export default function AdminPage() {
const { user, loading: authLoading } = useAuth("domain_admin", "/admin/login");
+22
View File
@@ -0,0 +1,22 @@
"use client";
import { Skeleton } from "@/components/ui/skeleton";
/**
* Einheitlicher Lade-Platzhalter für dynamisch nachgeladene Admin-Tabs.
* Wird von next/dynamic als `loading`-Komponente verwendet (PROJ-77).
*/
export function TabSkeleton() {
return (
<div className="mt-4 space-y-4" aria-busy="true" aria-live="polite">
<span className="sr-only">Inhalt wird geladen </span>
<Skeleton className="h-8 w-48" />
<div className="space-y-2">
<Skeleton className="h-4 w-full" />
<Skeleton className="h-4 w-5/6" />
<Skeleton className="h-4 w-2/3" />
</div>
<Skeleton className="h-48 w-full" />
</div>
);
}