16 KiB
Frontend Nyla -- Architektur
Überblick
Frontend Nyla ist eine React-Single-Page-Application (SPA) mit TypeScript und Vite. Routing über React Router; Schichten: Presentation (Pages, Components), Business Logic (Hooks, *Logic-Module), Data Access (api.ts, Feature-API-Module), Infrastructure (Contexts, Provider, Konfiguration). PageManager lädt Seiten dynamisch (Lazy Loading, State Preservation, Lifecycle-Hooks). Authentifizierung über Azure MSAL; HTTP über Axios mit Interceptors.
Technologie-Stack (Stand UI-Doku): React 19.x, Vite 5.x, TypeScript 5.8.x, React Router DOM 7.x, Axios, @azure/msal-browser / @azure/msal-react, Framer Motion, XState, CSS Modules, React Icons.
Ordnerstruktur (src/)
| Ordner | Inhalt |
|---|---|
pages/ |
Seiten: admin/, basedata/, billing/, settings/, views/ (workspace, commcoach, chatbot, trustee, graphicalEditor, realestate, neutralization, teamsbot) |
components/ |
Wiederverwendbar: FormGenerator (Table, Form, Tree, List, Report), Navigation, UnifiedDataBar, FlowEditor (Graphical Editor), OnboardingAssistant |
hooks/ |
useApiRequest (useApi.ts), useFiles, useNavigation, useConfirm, usePrompt, useResizablePanels, useSpeechAudioCapture (useVoiceStream: Mic + WebSocket /voice-google/stt/stream, optionale STT-open-Parameter), etc. |
contexts/ |
FileContext, PekContext, ToastContext, WorkflowSelectionContext |
api/ |
API-Client (api.ts) und Feature-spezifische API-Module |
core/ |
PageManager |
layouts/ |
Layout-Komponenten |
locales/ |
i18n: API-basiertes Language-Loading (index.ts, types.ts), keine statischen Locale-Files |
types/ |
TypeScript-Typen |
utils/ |
Utility-Funktionen |
Ergänzend typische Root-Dateien und Bereiche im Repo: main.tsx, App.tsx, api.ts (Root), auth/ (MSAL, ProtectedRoute), assets/ — Feature-Module unter components/ oft nach Muster: Haupt-.tsx, *.module.css, *Logic.tsx, *Types.ts, optional index.ts.
Wichtige UI-Komponenten
| Komponente | Zweck |
|---|---|
UnifiedDataBar (UDB) |
Multi-Tab-Panel: Chats, Files, Sources — wird in Workspace, CommCoach und Graphical Editor genutzt. Sources-Tab rendert zwei FormGeneratorTree-Sektionen ("Persoenliche Quellen", "Mandanten-Daten") mit dedizierter UDB-Domain-Schicht (components/UnifiedDataBar/sources/); UDB-Provider mappen Backend-Tree-Nodes auf das generische TreeNode-Schema, populieren extraActions fuer RAG-Toggle + Settings, und triggern Sektion-Refresh nach Patches |
FormGenerator |
Dynamische Formulare/Tabellen aus Backend-Attribut-Definitionen (siehe formgenerator.md) |
FormGeneratorTree |
Generische Baumkomponente mit Provider-Pattern (TreeNodeProvider), Multiselect, DnD, Inline-Editing, Scope/Neutralize, Batch-Actions, generische Mixed-State-Symbole, Pending-Spinner und extraActions-Slot. Konsumenten: FilesTab, SourcesTab, FilesPage. Siehe formgenerator.md. |
MandateNavigation |
Feature-Baum-Navigation mit Mandanten-Kontext |
Automation2FlowEditor |
Konsolidierter n8n-style Flow-Builder (Graphical Editor) mit 3-Column-Layout: [UDB + Chat/Tracing] [Canvas + Header] [Nodes] |
FlowCanvas |
Custom React Canvas fuer Workflow-Graph-Rendering mit Node-Highlighting (SSE Live-Tracing) |
RunTracingPanel |
Live-Tracing von Workflow-Runs via SSE mit Fallback-Polling |
EditorChatPanel |
AI Chat im Editor mit Drag&Drop-Dateianhang und Source-Picker |
CanvasHeader |
Workflow-Metadaten, Versioning (Draft/Publish/Archive), Save-as-Template, New-from-Template |
TemplatePicker |
Modal zur Auswahl von Workflow-Vorlagen beim Erstellen neuer Workflows |
WorkspacePage |
AI-Workspace mit Chat, UDB, Agent-Streaming |
RagInventoryPage |
Globale RAG-Inventar-Seite (/rag-inventory): pro Connection Card mit Consent-Toggle, Stop-Button, Reindex, DataSource-Uebersicht. Primaerer Ort fuer RAG-Management (keine duplizierte UI auf ConnectionsPage). API: /api/rag/inventory/* |
RagRunningBadge |
Floating-Badge in MainLayout.tsx: zeigt laufende RAG-Bootstrap-Jobs, Polling gegen /api/rag/inventory/jobs, Click-through zum RAG-Inventar |
AddConnectionWizard |
Connector-Type-Aware Wizard (Google/MSFT/ClickUp/Infomaniak): dynamische Step-Definition pro Typ, integrierter MSFT Admin-Consent-Step + Infomaniak-PAT-Step. Keine Kostenschaetzung, keine Preferences — nur Anbieter → Consent → Connect |
TrusteeDataTablesView |
Konsolidierte Daten-Tabellen-Seite (Treuhand): 13 Tabs (Stammdaten, Lokale Daten, Konfiguration, Buchhaltungs-Daten) ueber generischen TrusteeDataTab-Wrapper, Lazy-Mount pro Tab, URL-State ?tab=<key>, read-only fuer Sync-Tabellen |
Teams Bot Views (pages/views/teamsbot/) |
Dashboard (KPI + SSE), Assistent (Wizard), Module (CRUD + ?moduleId), Live-Session (SSE + MFA), Settings — API api/teamsbotApi.ts; Architektur teams-bot/architecture.md |
Routing
- Einstieg:
main.tsxrendertApp.tsxmit Providern (z. B. Sprache, Auth) und React Router (Routes/Route). - Geschützte Bereiche: Route-Guards (z. B.
ProtectedRoute) prüfen Authentifizierung; Redirect bei fehlender Session. - Haupt-App: Nach Login
MainLayout.tsxmitMandateNavigation(Sidebar) und<Outlet />(React Router). - Seiten-Mapping:
pageRegistry.tsxdefiniertPAGE_REGISTRYundFEATURE_REGISTRY; Seiten werden lazy geladen.core/PageManager/enthält ergänzende Infrastruktur (State Preservation, Lifecycle-Hooks). - Teams Bot:
pages/views/teamsbot/— fünf Tabs (Dashboard, Assistent, Module, Live-Session, Einstellungen). Dashboard:teamsbotApi.createDashboardStream(SSE). Live-Session:createSessionStream. Module-Liste: URL?moduleId=<uuid>klappt ein Modul auf. Detail: teams-bot/architecture.md. - i18n: DB-backed via
LanguageContext(t()-Hook). Sprachsets werden dynamisch via public API geladen (GET /api/i18n/sets/{code}). Key-Konvention: Deutscher Klartext = Key. Jeder neue/geaenderte UI-Text MUSS mitt('Deutscher Klartext')getaggt werden.t()darf NUR String-Literale enthalten —t(variable)ist verboten. Werte vom Backend (Labels, Descriptions) sind bereits uebersetzt und werden direkt gerendert. Variable Interpolation:t('Text {var}', {var: 'Wert'}). Fallback: Ziel-Set →de-Set →[key](eckige Klammern machen fehlende Uebersetzungen sichtbar). Keine statischen Locale-Files. Fuer statische Frontend-Maps (z.B. Wochentage, Monatsnamen, Status-Labels) wirdt()perswitch/Funktion mit Literalen aufgerufen, nicht ueber Map-Lookup. - TextMultilingual: Felder vom Typ
TextMultilingualrendern dynamisch Eingabefelder fuer alle verfuegbaren Sprachen (ausavailableLanguages).xxist das Pflichtfeld (Quelltext). Keine hardcodierten Sprach-Codes. Sprachcodes folgen ISO 639-1 (en, de, fr, it, …). - Theme: global über Context; API-Requests mit Auth-Header (Interceptor in zentralem API-Client).
UI-Regeln
- Keine Browser-Dialoge (
alert/confirm/prompt) — stattdessenuseConfirm()undusePrompt()Hooks - i18n-Pflicht: Jeder UI-Text (Label, Button, Placeholder, Tooltip, Fehlermeldung) MUSS mit
t('Deutscher Klartext')getaggt werden. Hardcodierte deutsche Strings im JSX sind nicht erlaubt. Import:const { t } = useLanguage();.t()darf NUR String-Literale enthalten — nie Variablen. Backend-Werte (feature.label, role.description etc.) sind bereits uebersetzt und werden direkt gerendert. - Alle internen Funktionen mit
_Prefix - camelCase für Variablen und Funktionen
Schlüssel-Dateien
| Datei / Bereich | Rolle |
|---|---|
main.tsx |
Application Entry Point |
App.tsx |
Root-Komponente, Router-Setup, Provider |
api.ts (Root) |
Axios-Instanz, Base URL, Token-Interceptor, 401-Handling |
hooks/useSpeechAudioCapture.ts |
useVoiceStream: STT-Streaming; start(language, sttOpenOptions?); CommCoach setzt model/lightweight/singleUtterance, Workspace nutzt Defaults |
layouts/MainLayout.tsx |
Haupt-Shell mit MandateNavigation + Outlet |
config/pageRegistry.tsx |
PAGE_REGISTRY, FEATURE_REGISTRY, Lazy Imports |
core/PageManager/ |
State Preservation, Lifecycle, Caching |
auth/authConfig, authProvider, ProtectedRoute |
MSAL, geschützte Routen |
contexts/* |
FileContext, PekContext, Toast, Workflow-Auswahl, etc. |
components/FlowEditor/editor/Automation2FlowEditor.tsx |
Haupt-Editor-Komponente (3-Column-Layout, State-Management) |
components/FlowEditor/editor/FlowCanvas.tsx |
Custom Canvas mit Node-Rendering, Connections, Highlighting; CanvasNode traegt inputPorts/outputPorts |
components/FlowEditor/nodes/frontendTypeRenderers/index.tsx |
FRONTEND_TYPE_RENDERERS Registry — generischer Renderer pro FrontendType statt pro Node-Typ |
components/FlowEditor/editor/NodeConfigPanel.tsx |
Generischer Config-Renderer via FRONTEND_TYPE_RENDERERS (kein NODE_CONFIG_REGISTRY mehr) |
components/FlowEditor/nodes/shared/graphUtils.ts |
fromApiGraph/toApiGraph serialisieren inputPorts/outputPorts auf CanvasNode |
components/FlowEditor/nodes/shared/DataPicker.tsx |
Schema-basierte Pfad-Aufloesung, Transit-Chain, System-Variablen-Sektion |
components/FlowEditor/context/Automation2DataFlowContext.tsx |
Stellt portTypeCatalog und systemVariables im Context bereit |
components/FlowEditor/editor/RunTracingPanel.tsx |
SSE Live-Push + Canvas-Highlighting fuer Workflow-Runs |
components/FlowEditor/editor/EditorChatPanel.tsx |
AI Chat mit Dateianhang (Drag&Drop) und Source-Picker |
components/FlowEditor/editor/CanvasHeader.tsx |
Versioning, Template-Management, Workflow-Aktionen |
components/FlowEditor/editor/TemplatePicker.tsx |
Template-Auswahl-Modal |
pages/views/graphicalEditor/GraphicalEditorPage.tsx |
Feature-Seite mit KeepAlive, URL-basiertem Workflow-Loading |
locales/index.ts, types.ts |
i18n: API-basiertes Language-Loading (DB-backed, keine statischen Files) |
providers/language/LanguageContext.tsx |
t()-Hook mit {variable}-Interpolation, Fallback-Kette, availableLanguages |
components/FormGenerator/FormGeneratorForm/FormGeneratorForm.tsx |
Rendert frontend_type: multilingual (TextMultilingual) dynamisch nach availableLanguages; Button „In alle Sprachen uebersetzen“ (KI via POST /api/i18n/translate-field) |
pages/admin/AdminLanguagesPage.tsx |
Admin-Seite: Sprachset-Verwaltung (CRUD, AI-Übersetzung, UI/API-Key-Counts) |
pages/admin/AdminLanguagesKeepAlive.tsx |
KeepAlive-Wrapper: Sprach-Seite bleibt persistent bei Seitenwechsel |
Tab-Pattern fuer mehrere Modelle pro Seite
Wenn eine Feature-Seite N verwandte Modelle in eigenen Tabs zeigen soll (Beispiel: TrusteeDataTablesView mit 13 Trustee-Tabellen), gilt folgendes Muster:
- Pro Tab ein Wrapper-Component, das exakt einen Hook aufruft. So bleiben React Hook Rules eingehalten und inaktive Tabs fetchen keine Daten (Lazy-Mount).
FormGeneratorTabledirekt im Wrapper (siehe formgenerator.md -- "Bei Tabs / Wrapper-Komponenten"). Jeder Wrapper reicht die Flex-Chain (flex:1; min-height:0) durch, sonst kollabiert die Tabelle auf 0 px.- URL-State via
?tab=<key>(useSearchParams). Erlaubt Deep-Links aus Notifications, Quick Actions oder Doku. - Read-only-Tabs: separater Wrapper ohne Operations-Hook,
readOnly={true}. Sync-Tabellen (z.B.TrusteeData*) verzichten auf Edit-/Delete-Buttons, damit der naechste Sync nicht versehentlich ueberschriebene Daten zerstoert. - Tab-Bar im Stil bestehender Trustee-Tab-Seiten (
TrusteeAbschlussView,TrusteeAccountingSettingsView); Gruppierung perTabGroupDef(z.B. "Stammdaten", "Lokale Daten", "Konfiguration", "Buchhaltungs-Daten") erhoeht Lesbarkeit bei vielen Tabs. - Spalten-Definitionen kommen aus
getModelAttributeDefinitions(...)(Backend) -- nie hardcodiert. So bleibt das UI automatisch synchron mit Pydantic-Modellaenderungen.
FlowEditor -- Typed Action Architecture (Phasen 1-5)
Seit April 2026 stuetzt sich der FlowEditor (Graphical Editor) auf eine typisierte Action-Architektur. Quellen: wiki/c-work/3-validate/2026-04-typed-action-architecture.md, Tests in frontend_nyla/src/components/FlowEditor/**/*.test.tsx.
Picker-Komponenten
| Komponente | Datei | Zweck |
|---|---|---|
RequiredAttributePicker |
nodes/shared/RequiredAttributePicker.tsx |
Auflistung + Auswahl von 0/1/N Pflicht-Attributen (featureInstanceId, connection, ...). Bei genau einer Quelle: Auto-Bind mit Override-Knopf; bei N: explizite Auswahl; bei 0: Hint "Iterieren-Vorschlag" oder Ressource fehlt. |
DataPicker |
nodes/shared/DataPicker.tsx |
Strict-Type-Filter (expectedParamType) + Object-Drill-Down + *-Wildcard fuer List-Iteration. Default ist Strict-Mode (Nur kompatible-Toggle). Hard-Mismatches werden ausgeblendet, Coerce-Faelle (z.B. int -> str) bleiben sichtbar. |
paramValidation.ts |
nodes/shared/paramValidation.ts |
Single source of truth fuer Pflicht-Feld-Pruefung. Liefert nodeErrors: List[NodeError] (genutzt von Save-Toast, Run-Block, Node-Highlighting). |
Save / Run Gating (AC-9)
- Save ist immer erlaubt (auch mit Pflicht-Fehlern).
CanvasHeader.tsxzeigt nach Save bei Fehlern einen amber-Banner ("Gespeichert mit X Pflicht-Fehlern in Y Nodes"). - Run wird per
executeBlockedReasonblockiert; der Button wechselt auf "Pflicht-Felder fehlen", behaeltaria-disabledund triggertonExecuteBlockedClick(markiert die First-Error-Node). - Hintergrund:
executeGraphvalidiert serverseitig zusaetzlich (Port-Mismatch, fehlende Pflicht-Bindings) und liefert klare Fehlermeldungen falls jemand den Editor-Block umgeht.
FeatureInstanceRef im Frontend
featureInstanceId-Bindings werden als typisierter Envelope{$type: "FeatureInstanceRef", id, featureCode}ans Backend geschickt; das Backend persistiert sie 1:1 (Phase 5).- Der Picker zeigt
[<featureCode>] <Label>(z.B.[trustee] Buchhaltung PWG), damit die Variante auf einen Blick erkennbar ist. - Legacy-Workflows (rohe UUIDs) bleiben funktionsfaehig -- die Backend-Engine migriert sie bei jedem Run automatisch (
materializeFeatureInstanceRefs).
Tests (Vitest + RTL)
frontend_nyla nutzt seit Track A1 Vitest 2.x + jsdom + @testing-library/react. Die Test-Dateien liegen neben den Komponenten als *.test.tsx:
| Test | Datei | Akzeptanzkriterium |
|---|---|---|
| RequiredAttributePicker (0/1/N + Override + Iterate) | RequiredAttributePicker.test.tsx |
T5, T6 |
| DataPicker -- strict-mode + coerce | DataPicker.strict.test.tsx (in DataPicker.test.tsx) |
T7 |
DataPicker -- generic object drill-down + *-Wildcard |
DataPicker.drillDown.test.tsx (in DataPicker.test.tsx) |
T8 |
| CanvasHeader Run/Save-Gating + Warning-Toast | CanvasHeader.test.tsx |
T10, AC-9 |
Befehle: npm test (CI-Mode), npm run test:ui (Vitest UI). Konfiguration: vitest.config.ts, vitest.setup.ts.
Regeln / Invarianten
- Feature-Organisation: UI, Logic (
*Logic), Types und CSS Modules pro Feature-Modul trennen; Exporte überindex.tswo üblich. - Datenfluss: Pages → Feature-Komponenten → Hooks →
useApi/ API-Module → Gateway; Fehler nicht verschlucken, konsistente Backend-Fehlerdarstellung. - Performance: Seiten lazy laden; bei konfigurierten Seiten State Preservation nutzen.
- Styling: CSS Modules, Theme über CSS-Variablen (Light/Dark) wo vorgesehen.
- Typisierung: TypeScript durchgängig; API-Response-Typen an den Grenzen definieren.
- Naming: Komponenten PascalCase, Hooks
use*, interne Hilfsfunktionen_+ camelCase — keinsnake_casein neuem Frontend-Code.