'use client' import React, { useCallback, useEffect, useState } from 'react' import { Activity, AlertTriangle, BarChart3, Globe, GraduationCap, Loader2, Plus, Settings, Shield, Truck, Users, } from 'lucide-react' import type { CreateNamespaceForm, TenantNamespace, TenantOverview } from '../_types' import { DATA_CLASSIFICATIONS, EMPTY_NAMESPACE_FORM, ISOLATION_LEVELS } from './constants' import { apiFetch, formatDate, getRiskBadgeClasses, getScoreColor, getStatusBadge, slugify } from './helpers' import { ComplianceRing } from './ComplianceRing' import { Modal } from './Modal' export function TenantDetailModal({ open, onClose, tenant, onSwitchTenant, }: { open: boolean onClose: () => void tenant: TenantOverview | null onSwitchTenant: (t: TenantOverview) => void }) { const [namespaces, setNamespaces] = useState([]) const [namespacesLoading, setNamespacesLoading] = useState(false) const [namespacesError, setNamespacesError] = useState(null) const [showCreateNs, setShowCreateNs] = useState(false) const [nsForm, setNsForm] = useState({ ...EMPTY_NAMESPACE_FORM }) const [nsCreating, setNsCreating] = useState(false) const [nsError, setNsError] = useState(null) const loadNamespaces = useCallback(async () => { if (!tenant) return setNamespacesLoading(true) setNamespacesError(null) try { const data = await apiFetch<{ namespaces: TenantNamespace[]; total: number }>( `/tenants/${tenant.id}/namespaces` ) setNamespaces(data.namespaces || []) } catch (err) { setNamespacesError(err instanceof Error ? err.message : 'Fehler beim Laden') } finally { setNamespacesLoading(false) } }, [tenant]) useEffect(() => { if (open && tenant) { loadNamespaces() setShowCreateNs(false) setNsForm({ ...EMPTY_NAMESPACE_FORM }) setNsError(null) } }, [open, tenant, loadNamespaces]) const handleNsNameChange = (name: string) => { setNsForm((prev) => ({ ...prev, name, slug: slugify(name), })) } const handleCreateNamespace = async (e: React.FormEvent) => { e.preventDefault() if (!tenant || nsForm.name.trim().length < 2) return setNsCreating(true) setNsError(null) try { await apiFetch(`/tenants/${tenant.id}/namespaces`, { method: 'POST', body: JSON.stringify({ name: nsForm.name.trim(), slug: nsForm.slug.trim(), isolation_level: nsForm.isolation_level, data_classification: nsForm.data_classification, }), }) setNsForm({ ...EMPTY_NAMESPACE_FORM }) setShowCreateNs(false) loadNamespaces() } catch (err) { setNsError(err instanceof Error ? err.message : 'Fehler beim Erstellen') } finally { setNsCreating(false) } } if (!tenant) return null const statusInfo = getStatusBadge(tenant.status) const scoreColor = getScoreColor(tenant.compliance_score) return (
{/* Overview Section */}

{tenant.name}

{statusInfo.icon} {statusInfo.label}

{tenant.slug}

{tenant.risk_level} Erstellt: {formatDate(tenant.created_at)}
{/* Compliance Breakdown */}

Compliance-Uebersicht

Offene Vorfaelle

{tenant.open_incidents}

Hinweisgebermeldungen

{tenant.open_reports}

Ausstehende DSRs

{tenant.pending_dsrs}

Schulungsquote

{tenant.training_completion_rate}%

Hochrisiko-Dienstleister

{tenant.vendor_risk_high}

Compliance-Score

{tenant.compliance_score}/100

{/* Settings */}

Mandanten-Einstellungen

Max. Benutzer

{tenant.max_users.toLocaleString('de-DE')}

LLM Kontingent / Monat

{tenant.llm_quota_monthly.toLocaleString('de-DE')}

{/* Namespaces */}

Namespaces ({namespaces.length})

{/* Create Namespace Form */} {showCreateNs && (
{nsError && (
{nsError}
)}
handleNsNameChange(e.target.value)} placeholder="z.B. Produktion" className="w-full px-2.5 py-1.5 border border-slate-300 rounded-lg text-xs focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500" autoFocus />
)} {/* Namespaces List */} {namespacesLoading ? (
{Array.from({ length: 3 }).map((_, i) => (
))}
) : namespacesError ? (
{namespacesError}
) : namespaces.length === 0 ? (
Noch keine Namespaces vorhanden.
) : (
{namespaces.map((ns) => (

{ns.name}

{ns.slug}

{ns.isolation_level} {ns.data_classification}
))}
)}
{/* Switch Tenant Action */}
) }