feat(PROJ-51): Aufbewahrungsfristen nach Dokumentenart (Retention-Kategorien)

Fuehrt archiving_rules ein (PROJ-43-Basis: Tabelle + CRUD-API + Admin-UI) und
erweitert die Retention-Logik (PROJ-34) um Regel-basierte Fristen, eine
globale Mindestfrist (min_retention_days) sowie Nachvollziehbarkeit der
Frist-Quelle (retain_until_source) in API und Mail-Detailansicht.
This commit is contained in:
sysops
2026-06-13 20:48:16 +02:00
parent 7c08ebe1b7
commit 507dee6431
16 changed files with 1175 additions and 21 deletions
+7
View File
@@ -70,6 +70,7 @@ 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 { QuotaTab } from "@/components/admin/tabs/QuotaTab";
import { SMTPOutTab } from "@/components/admin/tabs/SMTPOutTab";
import { ResetPasswordDialog, DeleteUserDialog } from "@/components/admin/UserDialogs";
@@ -712,6 +713,7 @@ export default function AdminPage() {
{isSuperAdmin && <TabsTrigger value="cert" onClick={loadCert}>Zertifikat</TabsTrigger>}
{isSuperAdmin && <TabsTrigger value="tenants" onClick={loadTenants}>Mandanten</TabsTrigger>}
{isSuperAdmin && <TabsTrigger value="retention">Retention</TabsTrigger>}
{isSuperAdmin && <TabsTrigger value="archiving-rules">Regeln</TabsTrigger>}
{isSuperAdmin && <TabsTrigger value="quotas">Quotas</TabsTrigger>}
{isSuperAdmin && <TabsTrigger value="smtp-out">SMTP-Out</TabsTrigger>}
{isSuperAdmin && <TabsTrigger value="modules">Module</TabsTrigger>}
@@ -975,6 +977,11 @@ export default function AdminPage() {
<RetentionTab />
</TabsContent>
)}
{isSuperAdmin && (
<TabsContent value="archiving-rules">
<ArchivingRulesTab />
</TabsContent>
)}
{isSuperAdmin && (
<TabsContent value="smtp-out">
+31
View File
@@ -46,6 +46,23 @@ function formatDate(iso: string): string {
}
}
// PROJ-51: human-readable label for the retain_until_source marker.
function retentionSourceLabel(source: string): string {
if (source.startsWith("rule:")) {
return `Archivierungsregel #${source.slice(5)}`;
}
switch (source) {
case "tenant_default":
return "Mandanten-Standard";
case "global_default":
return "Globaler Standard";
case "min_retention":
return "Globale Mindestfrist";
default:
return source;
}
}
function triggerDownload(blob: Blob, filename: string) {
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
@@ -115,6 +132,20 @@ function MailHeaderGrid({ mail }: { mail: MailDetail }) {
)}
<OcrBadge status={mail.ocr_status} />
</span>
{/* PROJ-51: retention lock + source for auditor traceability */}
{mail.retain_until && (
<>
<span className="font-medium text-muted-foreground">Aufbewahrung:</span>
<span className="flex flex-wrap items-center gap-2 text-sm">
<span>bis {formatDate(mail.retain_until)}</span>
{mail.retain_until_source && (
<Badge variant="secondary">
{retentionSourceLabel(mail.retain_until_source)}
</Badge>
)}
</span>
</>
)}
</div>
<button