feat(PROJ-69): Admin-Dashboard Tab-Gruppierung (2-Ebenen-Navigation)
17 flache Tabs unter /admin durch Gruppen-Leiste (Übersicht, Benutzer & Zugriff, Compliance, Mail-Eingang, System) ersetzt. Rollenbasierte Sichtbarkeit pro Tab unverändert, nur Navigation strukturiert. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
8bd02facf5
commit
3b2a8388c1
@@ -8992,3 +8992,101 @@ Keine Commits in dieser Session.
|
||||
- features/PROJ-66-backup-strategie.md | 75 ++++++++++++++++++++++++++++++++++++++++++++-
|
||||
|
||||
---
|
||||
## 2026-07-06 11:47 – 11:47 (0m)
|
||||
**Beschreibung:** Claude Code Session
|
||||
**Projekt:** archivmail
|
||||
|
||||
### Commits
|
||||
- 8bd02fa docs(PROJ-67,PROJ-68): Deployment-Status auf Deployed setzen
|
||||
|
||||
### Geänderte Dateien
|
||||
- DEVLOG.md | 829 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
- features/INDEX.md | 2 +-
|
||||
- features/PROJ-67-manticore-upgrade-25-zu-27.md | 157 +++++++++++++++-
|
||||
- features/PROJ-68-sudo-provisionierung-dienststeuerung.md | 111 ++++++++++-
|
||||
|
||||
---
|
||||
## 2026-07-06 11:47 – 11:47 (0m)
|
||||
**Beschreibung:** Claude Code Session
|
||||
**Projekt:** archivmail
|
||||
|
||||
### Commits
|
||||
Keine Commits in dieser Session.
|
||||
|
||||
### Geänderte Dateien
|
||||
- DEVLOG.md | 829 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
- features/INDEX.md | 2 +-
|
||||
- features/PROJ-67-manticore-upgrade-25-zu-27.md | 157 +++++++++++++++-
|
||||
- features/PROJ-68-sudo-provisionierung-dienststeuerung.md | 111 ++++++++++-
|
||||
|
||||
---
|
||||
## 2026-07-06 11:48 – 11:48 (0m)
|
||||
**Beschreibung:** Claude Code Session
|
||||
**Projekt:** archivmail
|
||||
|
||||
### Commits
|
||||
Keine Commits in dieser Session.
|
||||
|
||||
### Geänderte Dateien
|
||||
- DEVLOG.md | 829 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
- features/INDEX.md | 2 +-
|
||||
- features/PROJ-67-manticore-upgrade-25-zu-27.md | 157 +++++++++++++++-
|
||||
- features/PROJ-68-sudo-provisionierung-dienststeuerung.md | 111 ++++++++++-
|
||||
|
||||
---
|
||||
## 2026-07-06 11:49 – 11:49 (0m)
|
||||
**Beschreibung:** Claude Code Session
|
||||
**Projekt:** archivmail
|
||||
|
||||
### Commits
|
||||
Keine Commits in dieser Session.
|
||||
|
||||
### Geänderte Dateien
|
||||
- DEVLOG.md | 829 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
- features/INDEX.md | 2 +-
|
||||
- features/PROJ-67-manticore-upgrade-25-zu-27.md | 157 +++++++++++++++-
|
||||
- features/PROJ-68-sudo-provisionierung-dienststeuerung.md | 111 ++++++++++-
|
||||
|
||||
---
|
||||
## 2026-07-06 11:50 – 11:51 (0m)
|
||||
**Beschreibung:** Claude Code Session
|
||||
**Projekt:** archivmail
|
||||
|
||||
### Commits
|
||||
Keine Commits in dieser Session.
|
||||
|
||||
### Geänderte Dateien
|
||||
- DEVLOG.md | 829 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
- features/INDEX.md | 2 +-
|
||||
- features/PROJ-67-manticore-upgrade-25-zu-27.md | 157 +++++++++++++++-
|
||||
- features/PROJ-68-sudo-provisionierung-dienststeuerung.md | 111 ++++++++++-
|
||||
|
||||
---
|
||||
## 2026-07-06 11:52 – 12:03 (10m)
|
||||
**Beschreibung:** Claude Code Session
|
||||
**Projekt:** archivmail
|
||||
|
||||
### Commits
|
||||
Keine Commits in dieser Session.
|
||||
|
||||
### Geänderte Dateien
|
||||
- DEVLOG.md | 829 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
- features/INDEX.md | 2 +-
|
||||
- features/PROJ-67-manticore-upgrade-25-zu-27.md | 157 +++++++++++++++-
|
||||
- features/PROJ-68-sudo-provisionierung-dienststeuerung.md | 111 ++++++++++-
|
||||
|
||||
---
|
||||
## 2026-07-06 12:04 – 12:08 (4m)
|
||||
**Beschreibung:** Claude Code Session
|
||||
**Projekt:** archivmail
|
||||
|
||||
### Commits
|
||||
Keine Commits in dieser Session.
|
||||
|
||||
### Geänderte Dateien
|
||||
- DEVLOG.md | 829 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
- features/INDEX.md | 2 +-
|
||||
- features/PROJ-67-manticore-upgrade-25-zu-27.md | 157 +++++++++++++++-
|
||||
- features/PROJ-68-sudo-provisionierung-dienststeuerung.md | 111 ++++++++++-
|
||||
|
||||
---
|
||||
|
||||
+2
-1
@@ -84,7 +84,8 @@
|
||||
| PROJ-66 | Backup-Strategie für Store, Keyfile, PostgreSQL (Produktiv + Teilproduktiv) | Deployed | [PROJ-66](PROJ-66-backup-strategie.md) | 2026-07-04 |
|
||||
| PROJ-67 | Manticore Search Upgrade 25.0.0 → 27.1.5 + Auto-Upgrade-Pfad | Deployed | [PROJ-67](PROJ-67-manticore-upgrade-25-zu-27.md) | 2026-07-05 |
|
||||
| PROJ-68 | sudo-Provisionierung für Admin-Dienststeuerung fehlte komplett | Deployed | [PROJ-68](PROJ-68-sudo-provisionierung-dienststeuerung.md) | 2026-07-05 |
|
||||
| PROJ-69 | Admin-Dashboard Tab-Gruppierung (2-Ebenen-Navigation) | Planned | [PROJ-69](PROJ-69-admin-tabs-gruppierung.md) | 2026-07-06 |
|
||||
|
||||
<!-- Add features above this line -->
|
||||
|
||||
## Next Available ID: PROJ-69
|
||||
## Next Available ID: PROJ-70
|
||||
|
||||
+102
-24
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect } from "react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useAuth } from "@/hooks/useAuth";
|
||||
import { useLDAPConfig } from "@/hooks/useLDAPConfig";
|
||||
import { useTenantLDAPConfig } from "@/hooks/useTenantLDAPConfig";
|
||||
@@ -135,6 +135,84 @@ export default function AdminPage() {
|
||||
loadServices();
|
||||
}, [user, loadUsers, loadAudit, loadServices]);
|
||||
|
||||
const tabGroups = useMemo(
|
||||
() => [
|
||||
{
|
||||
id: "overview",
|
||||
label: "Übersicht",
|
||||
tabs: [
|
||||
{ value: "dashboard", label: "Dashboard", visible: true },
|
||||
{ value: "services", label: "Dienste", visible: isSuperAdmin },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "users",
|
||||
label: "Benutzer & Zugriff",
|
||||
tabs: [
|
||||
{ value: "users", label: "Benutzer", visible: true },
|
||||
{ value: "ldap", label: "LDAP (Global)", visible: isSuperAdmin, onClick: loadLDAP },
|
||||
{
|
||||
value: "tenant-ldap",
|
||||
label: "LDAP",
|
||||
visible: !isSuperAdmin && user?.role === "domain_admin",
|
||||
onClick: loadTenantLDAP,
|
||||
},
|
||||
{ value: "tenants", label: "Mandanten", visible: isSuperAdmin, onClick: tenantsState.loadTenants },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "compliance",
|
||||
label: "Compliance",
|
||||
tabs: [
|
||||
{ value: "audit", label: "Audit-Log", visible: true },
|
||||
{ value: "dsgvo", label: "DSGVO-Anfragen", visible: canViewDSGVO },
|
||||
{ value: "retention", label: "Retention", visible: isSuperAdmin },
|
||||
{ value: "archiving-rules", label: "Regeln", visible: isSuperAdmin },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "mail",
|
||||
label: "Mail-Eingang",
|
||||
tabs: [
|
||||
{ value: "import", label: "Import", visible: true },
|
||||
{ value: "imap-settings", label: "IMAP", visible: !isSuperAdmin },
|
||||
{ value: "routing-rules", label: "Routing-Regeln", visible: true },
|
||||
{ value: "smtp-out", label: "SMTP-Out", visible: isSuperAdmin },
|
||||
{ value: "quotas", label: "Quotas", visible: isSuperAdmin },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "system",
|
||||
label: "System",
|
||||
tabs: [
|
||||
{ value: "security", label: "Security", visible: isSuperAdmin },
|
||||
{ value: "cert", label: "Zertifikat", visible: isSuperAdmin, onClick: cert.loadCert },
|
||||
{ value: "modules", label: "Module", visible: isSuperAdmin },
|
||||
],
|
||||
},
|
||||
],
|
||||
[isSuperAdmin, canViewDSGVO, user?.role, loadLDAP, loadTenantLDAP, tenantsState.loadTenants, cert.loadCert]
|
||||
);
|
||||
|
||||
const visibleGroups = tabGroups
|
||||
.map((g) => ({ ...g, tabs: g.tabs.filter((t) => t.visible) }))
|
||||
.filter((g) => g.tabs.length > 0);
|
||||
|
||||
const [activeGroupId, setActiveGroupId] = useState(visibleGroups[0]?.id ?? "overview");
|
||||
const [activeTab, setActiveTab] = useState(visibleGroups[0]?.tabs[0]?.value ?? "dashboard");
|
||||
|
||||
const activeGroup = visibleGroups.find((g) => g.id === activeGroupId) ?? visibleGroups[0];
|
||||
|
||||
function handleGroupChange(groupId: string) {
|
||||
setActiveGroupId(groupId);
|
||||
const group = visibleGroups.find((g) => g.id === groupId);
|
||||
const firstTab = group?.tabs[0];
|
||||
if (firstTab) {
|
||||
setActiveTab(firstTab.value);
|
||||
firstTab.onClick?.();
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen">
|
||||
<Navbar username={user?.username ?? ""} role={user?.role ?? ""} />
|
||||
@@ -148,30 +226,30 @@ export default function AdminPage() {
|
||||
) : (<>
|
||||
<h1 className="mb-6 text-2xl font-bold">Administration</h1>
|
||||
|
||||
<Tabs defaultValue="dashboard">
|
||||
<div className="mb-4 flex flex-wrap gap-1 border-b pb-2">
|
||||
{visibleGroups.map((g) => (
|
||||
<button
|
||||
key={g.id}
|
||||
type="button"
|
||||
onClick={() => handleGroupChange(g.id)}
|
||||
className={`rounded-md px-3 py-1.5 text-sm font-medium transition-colors ${
|
||||
g.id === activeGroup?.id
|
||||
? "bg-primary text-primary-foreground"
|
||||
: "text-muted-foreground hover:bg-muted hover:text-foreground"
|
||||
}`}
|
||||
>
|
||||
{g.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<Tabs value={activeTab} onValueChange={setActiveTab}>
|
||||
<TabsList>
|
||||
<TabsTrigger value="dashboard">Dashboard</TabsTrigger>
|
||||
{isSuperAdmin && <TabsTrigger value="services">Dienste</TabsTrigger>}
|
||||
<TabsTrigger value="users">Benutzer</TabsTrigger>
|
||||
<TabsTrigger value="audit">Audit-Log</TabsTrigger>
|
||||
{canViewDSGVO && <TabsTrigger value="dsgvo">DSGVO-Anfragen</TabsTrigger>}
|
||||
<TabsTrigger value="import">Import</TabsTrigger>
|
||||
{isSuperAdmin && <TabsTrigger value="ldap" onClick={loadLDAP}>LDAP (Global)</TabsTrigger>}
|
||||
{!isSuperAdmin && user?.role === "domain_admin" && (
|
||||
<TabsTrigger value="tenant-ldap" onClick={loadTenantLDAP}>LDAP</TabsTrigger>
|
||||
)}
|
||||
{!isSuperAdmin && user?.role === "domain_admin" && (
|
||||
<TabsTrigger value="imap-settings">IMAP</TabsTrigger>
|
||||
)}
|
||||
{isSuperAdmin && <TabsTrigger value="security">Security</TabsTrigger>}
|
||||
{isSuperAdmin && <TabsTrigger value="cert" onClick={cert.loadCert}>Zertifikat</TabsTrigger>}
|
||||
{isSuperAdmin && <TabsTrigger value="tenants" onClick={tenantsState.loadTenants}>Mandanten</TabsTrigger>}
|
||||
{isSuperAdmin && <TabsTrigger value="retention">Retention</TabsTrigger>}
|
||||
{isSuperAdmin && <TabsTrigger value="archiving-rules">Regeln</TabsTrigger>}
|
||||
<TabsTrigger value="routing-rules">Routing-Regeln</TabsTrigger>
|
||||
{isSuperAdmin && <TabsTrigger value="quotas">Quotas</TabsTrigger>}
|
||||
{isSuperAdmin && <TabsTrigger value="smtp-out">SMTP-Out</TabsTrigger>}
|
||||
{isSuperAdmin && <TabsTrigger value="modules">Module</TabsTrigger>}
|
||||
{activeGroup?.tabs.map((t) => (
|
||||
<TabsTrigger key={t.value} value={t.value} onClick={t.onClick}>
|
||||
{t.label}
|
||||
</TabsTrigger>
|
||||
))}
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="dashboard" className="mt-4 space-y-6">
|
||||
|
||||
Reference in New Issue
Block a user