CI / Web-Barrierefreiheitspruefung (axe, Tastatur, Reflow, Textabstaende) (push) Canceled after 0s
CI / Web-Barrierefreiheitspruefung (axe, Tastatur, Reflow, Textabstaende) (pull_request) Canceled after 0s
CI / Core-Modul (Build/Vet/Test) (push) Successful in 38s
CI / DMS Security-Test-Gate (AC5, Merge-Gate) (push) Successful in 3s
CI / Connect Security-Test-Gate (QA-01 AC4, Merge-Gate) (push) Successful in 3s
CI / DMS-Modul Supply-Chain-Scan (govulncheck) (push) Failing after 14s
CI / Web (dms-browser) (push) Failing after 53s
CI / Web (pdf-viewer) (push) Failing after 38s
CI / Web (shl) (push) Failing after 8s
Supply-Chain-Scan (Go) / govulncheck (push) Failing after 37s
AI-Pflichttest-Gate / pflichttest-gate (pull_request) Successful in 15m54s
CI / Core-Modul (Build/Vet/Test) (pull_request) Successful in 37s
CI / DMS Security-Test-Gate (AC5, Merge-Gate) (pull_request) Successful in 2s
CI / Connect Security-Test-Gate (QA-01 AC4, Merge-Gate) (pull_request) Successful in 2s
CI / DMS-Modul Supply-Chain-Scan (govulncheck) (pull_request) Failing after 13s
CI / Web (dms-browser) (pull_request) Failing after 53s
CI / Web (pdf-viewer) (pull_request) Failing after 38s
CI / Web (shl) (pull_request) Failing after 8s
Core-Schnittstellen-Vertragstests / contract-tests (pull_request) Successful in 16m2s
Supply-Chain-Scan (Go) / govulncheck (pull_request) Failing after 56s
Supply-Chain-Scan (npm) / npm-audit (pull_request) Failing after 1m13s
Core-Pflichttest-Gate / pflichttest-gate (pull_request) Successful in 16m18s
RBAC-Rollenhierarchie-Pflichtgate / rbac-policy-invariant (pull_request) Successful in 15m49s
Workflow-Pflichttest-Gate / pflichttest-gate (pull_request) Failing after 22m6s
DB-Integrationstests / db-tests (pull_request) Canceled after 0s
CI / DMS-Modul (Lint/Build/Test/Coverage/Artefakt) (pull_request) Canceled after 0s
CI / DMS-Modul (Lint/Build/Test/Coverage/Artefakt) (push) Canceled after 0s
- semanticsearchapi: ai.search.read per policyclient (WithPolicy), Tenant aus
dem Anfragekoerper, nach Service-Token-Pruefung
- taskapi: workflow.task.read (GET /tasks) und workflow.task.write (PATCH
status); Paket jetzt in cmd/workflow-app verdrahtet (lag bisher nur im
a11y-Fixture) und damit hinter bearerauth
- connectorsetupapi: connect.setup.read/write je Route
- Migration 0029 (up/down): neue Rechte fuer user und tenant_admin
- Wiring: NEXARCH_{AI,WORKFLOW,CONNECT}_APP_POLICY_URL/_POLICY_SERVICE_TOKEN;
ohne URL Warnung und keine Pruefung (Uebergang), URL ohne Token Startfehler;
connect-app setzt die bestehende statusapi-Pruefung damit ebenfalls scharf
- Tests je API: 403 bei Ablehnung, 200 bei Freigabe, 503 bei Ausfall, Policy
wird ohne Identitaet/Service-Token nicht befragt
- MODULE_REGISTRY: ai_search_chat, workflow_tasks, connect_setup (+ Icons,
Portal-Rechteliste)
- Doku: BETRIEB-PORTAL.md, ENG-11 Umgebungsvariablen
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
158 lines
6.5 KiB
TypeScript
158 lines
6.5 KiB
TypeScript
"use client";
|
|
|
|
// Globale Navigation ueber alle Modul-Frontends (SHL-01 Akzeptanzkriterium 1).
|
|
// Statische Registry statt Live-Abruf: die Modul-Frontends dieses Testhosts
|
|
// nutzen unterschiedliche, noch nicht vereinheitlichte Anmeldemodelle
|
|
// (Core-Sitzung vs. feste Demo-Kennung per Umgebungsvariable) -- ein
|
|
// gemeinsamer Live-Abruf ueber /api/visible-modules wuerde fuer die Haelfte
|
|
// der Module eine Core-Anmeldung voraussetzen, die sie bisher nicht haben.
|
|
// Einzige Quelle fuer die Modul-Liste (SHL-01 P1-Review): web/portal
|
|
// importiert MODULE_REGISTRY von hier statt einer separaten JSON-Kopie
|
|
// (web/portal/lib/modules.ts). description/permission sind optional, weil
|
|
// das statische Nav hier (ModuleNavShell) nur key/label/href braucht;
|
|
// portal/lib/modules.ts nutzt die zusaetzlichen Felder fuer die
|
|
// rechte-gefilterte visible-modules-API.
|
|
|
|
import { useEffect, useState } from "react";
|
|
import type { ReactNode } from "react";
|
|
import { Shell } from "./Shell";
|
|
|
|
export type ModuleGroup = "work" | "admin" | "audit";
|
|
|
|
export interface ModuleRegistryEntry {
|
|
key: string;
|
|
group?: ModuleGroup;
|
|
label: string;
|
|
href: string;
|
|
description?: string;
|
|
permission?: string;
|
|
}
|
|
|
|
export const MODULE_REGISTRY: ModuleRegistryEntry[] = [
|
|
{
|
|
key: "dms", group: "work", label: "Dokumente", href: "http://localhost:3101",
|
|
description: "Dokumente durchsuchen und verwalten.", permission: "dms.document.read",
|
|
},
|
|
{
|
|
key: "dms_upload", group: "work", label: "Dokumente hochladen", href: "http://localhost:3107",
|
|
description: "Dokumente und Kassenbelege hochladen, inklusive mobiler Kamera-Erfassung.", permission: "dms.upload.read",
|
|
},
|
|
{
|
|
key: "mail", group: "work", label: "Postfach", href: "http://localhost:3102",
|
|
description: "E-Mails durchsuchen und verwalten.", permission: "mail.message.read",
|
|
},
|
|
{
|
|
key: "workflow", group: "work", label: "Aufgaben & Freigaben", href: "http://localhost:3103",
|
|
description: "Freigaben bearbeiten, Akten einsehen, Aufgaben verwalten.", permission: "workflow.approval.read",
|
|
},
|
|
{
|
|
key: "workflow_cases", group: "work", label: "Akten & Vorgänge", href: "http://localhost:3108",
|
|
description: "Akten, Vermerke und Wiedervorlagen einsehen.", permission: "workflow.case.read",
|
|
},
|
|
{
|
|
key: "routing_rules", group: "admin", label: "Routing-Regeln", href: "http://localhost:3109",
|
|
description: "Regelbasiertes Routing anlegen und testen.", permission: "workflow.routingrule.read",
|
|
},
|
|
{
|
|
key: "ai", group: "work", label: "KI-Funktionen", href: "http://localhost:3104",
|
|
description: "Zusammenfassungen, Extraktion und semantische Suche.", permission: "ai.summary.read",
|
|
},
|
|
{
|
|
key: "ai_extraction", group: "work", label: "Extraktions-Korrektur", href: "http://localhost:3110",
|
|
description: "Von der KI extrahierte Werte prüfen und korrigieren.", permission: "ai.extraction.read",
|
|
},
|
|
{
|
|
key: "ai_search_chat", group: "work", label: "Suche & Chat", href: "http://localhost:3115",
|
|
description: "Semantische Suche und Dokumenten-Chat mit Quellenangaben.", permission: "ai.search.read",
|
|
},
|
|
{
|
|
key: "workflow_tasks", group: "work", label: "Aufgabenboard", href: "http://localhost:3116",
|
|
description: "Aufgaben als Liste und Kanban bearbeiten.", permission: "workflow.task.read",
|
|
},
|
|
{
|
|
key: "connect_setup", group: "admin", label: "Anbindungen einrichten", href: "http://localhost:3117",
|
|
description: "Connectoren anlegen, Zugangsdaten und Feld-Mapping pflegen.", permission: "connect.setup.read",
|
|
},
|
|
{
|
|
key: "connect", group: "admin", label: "Externe Anbindungen", href: "http://localhost:3105",
|
|
description: "Status externer Systemanbindungen (ERP, DATEV, CRM).", permission: "connect.status.read",
|
|
},
|
|
{
|
|
key: "connect_datev", group: "admin", label: "DATEV-Export", href: "http://localhost:3111",
|
|
description: "DATEV-Exporte anstoßen und Übertragungsprotokoll einsehen.", permission: "connect.datev.read",
|
|
},
|
|
{
|
|
key: "tenant_admin", group: "admin", label: "Mandantenverwaltung", href: "http://localhost:3106",
|
|
description: "Mandanten und Import-Verzeichnisse verwalten.", permission: "core.tenant.admin",
|
|
},
|
|
{
|
|
key: "rbac_admin", group: "admin", label: "Rollen & Rechte", href: "http://localhost:3112",
|
|
description: "Rollen und Berechtigungen verwalten.", permission: "core.tenant.admin",
|
|
},
|
|
{
|
|
key: "kennzahlen_dashboard", group: "audit", label: "Kennzahlen", href: "http://localhost:3113",
|
|
description: "Löschkennzahlen je Tenant und Modul einsehen.", permission: "kennzahlen.read",
|
|
},
|
|
{ key: "portal", label: "Übersicht", href: "http://localhost:3900" },
|
|
];
|
|
|
|
export interface ModuleNavShellProps {
|
|
activeKey: string;
|
|
tenantLabel?: string;
|
|
children: ReactNode;
|
|
}
|
|
|
|
// Ersetzt "localhost" im Modul-Href durch den tatsaechlichen Hostnamen,
|
|
// auf dem die Seite ausgeliefert wird (Testhosts binden Module unter
|
|
// derselben IP auf unterschiedlichen Ports). Einzige Quelle fuer diese
|
|
// Umschreibung (SHL-01 P1-Review): vorher identisch dupliziert als
|
|
// resolveModuleHref in web/portal/app/ModuleGrid.tsx.
|
|
export function resolveModuleHref(
|
|
href: string,
|
|
currentHostname: string = typeof window !== "undefined" ? window.location.hostname : "localhost"
|
|
): string {
|
|
try {
|
|
const url = new URL(href);
|
|
if (url.hostname === "localhost" && currentHostname !== "localhost") {
|
|
url.hostname = currentHostname;
|
|
}
|
|
return url.toString();
|
|
} catch {
|
|
return href;
|
|
}
|
|
}
|
|
|
|
// Liefert serverseitig/beim ersten Client-Render immer den unveraenderten
|
|
// Original-Href (identisch zu SSR), damit React 19 keine
|
|
// Hydration-Mismatch-Warnung wirft. Der Hostname-Tausch passiert erst in
|
|
// einem useEffect nach dem Mount, analog zum bestehenden themeInit-Pattern.
|
|
function resolveHrefClientOnly(href: string): string {
|
|
return resolveModuleHref(href);
|
|
}
|
|
|
|
function tenantLabel(): string {
|
|
if (typeof process === "undefined") return "";
|
|
return process.env.NEXT_PUBLIC_TENANT_SLUG ?? process.env.NEXT_PUBLIC_MAIL_TENANT_SLUG ?? "demo";
|
|
}
|
|
|
|
export function ModuleNavShell({ activeKey, tenantLabel: tenantLabelProp, children }: ModuleNavShellProps) {
|
|
const [hrefs, setHrefs] = useState<Record<string, string>>({});
|
|
|
|
useEffect(() => {
|
|
setHrefs(Object.fromEntries(MODULE_REGISTRY.map((m) => [m.key, resolveHrefClientOnly(m.href)])));
|
|
}, []);
|
|
|
|
const modules = MODULE_REGISTRY.map((m) => ({
|
|
key: m.key,
|
|
label: m.label,
|
|
href: hrefs[m.key] ?? m.href,
|
|
active: m.key === activeKey,
|
|
}));
|
|
|
|
return (
|
|
<Shell modules={modules} tenantLabel={tenantLabelProp ?? tenantLabel()}>
|
|
{children}
|
|
</Shell>
|
|
);
|
|
}
|