diff --git a/TOPICS.md b/TOPICS.md index 7fb01ea..f34941b 100644 --- a/TOPICS.md +++ b/TOPICS.md @@ -1,5 +1,5 @@  - + # Themen-Index für AI-Kontext @@ -25,6 +25,7 @@ Lade immer zuerst diese Datei. Dann gezielt die passende(n) Referenz-Datei(en). | Automation | b-reference/gateway/automation.md | Graphical Editor, Scheduler, System-Automatisierung (`/automations`, `/api/system/workflow-runs/*`) | | Billing & Subscriptions | b-reference/gateway/billing.md | Abrechnung, Prepaid, State Machine | | Frontend Nyla | b-reference/frontend-nyla/architecture.md | UI-Seiten, Komponenten, Hooks, Routing | +| FormGenerator (Table, Form, Tree, Report) | b-reference/frontend-nyla/formgenerator.md | Generische UI-Komponenten, Provider-Pattern, API-Anbindung | | Private LLM | b-reference/private-llm/architecture.md | Internes LLM, Neutralisierung | | Teams Bot | b-reference/teams-bot/architecture.md | Meeting-Bot, WebSocket, Director Prompts (Hybrid Agent-Routing) | diff --git a/b-reference/frontend-nyla/architecture.md b/b-reference/frontend-nyla/architecture.md index 4518f2f..3575ab3 100644 --- a/b-reference/frontend-nyla/architecture.md +++ b/b-reference/frontend-nyla/architecture.md @@ -15,7 +15,7 @@ Technologie-Stack (Stand UI-Doku): React 19.x, Vite 5.x, TypeScript 5.8.x, React | Ordner | Inhalt | |--------|--------| | `pages/` | Seiten: `admin/`, `basedata/`, `billing/`, `settings/`, `views/` (workspace, commcoach, chatbot, trustee, graphicalEditor, realestate, neutralization, teamsbot) | -| `components/` | Wiederverwendbar: FormGenerator, FolderTree, Navigation, UnifiedDataBar, FlowEditor (Graphical Editor), OnboardingAssistant | +| `components/` | Wiederverwendbar: FormGenerator (Table, Form, Tree, List, Report), Navigation, UnifiedDataBar, FlowEditor (Graphical Editor), OnboardingAssistant | | `hooks/` | useApiRequest (useApi.ts), useFiles, useNavigation, useConfirm, usePrompt, useResizablePanels, etc. | | `contexts/` | FileContext, PekContext, ToastContext, WorkflowSelectionContext | | `api/` | API-Client (`api.ts`) und Feature-spezifische API-Module | @@ -33,7 +33,7 @@ Ergänzend typische Root-Dateien und Bereiche im Repo: `main.tsx`, `App.tsx`, `a |------------|-------| | `UnifiedDataBar (UDB)` | Multi-Tab-Panel: Chats, Files, Sources — wird in Workspace, CommCoach und Graphical Editor genutzt | | `FormGenerator` | Dynamische Formulare/Tabellen aus Backend-Attribut-Definitionen (siehe [formgenerator.md](formgenerator.md)) | -| `FolderTree` | Rekursiver Ordnerbaum mit Drag&Drop, Multi-Selection, Inline-Editing | +| `FormGeneratorTree` | Generische Baumkomponente mit Provider-Pattern (`TreeNodeProvider`), Multiselect, DnD, Inline-Editing, Scope/Neutralize, Batch-Actions; ersetzt fruehere `FolderTree`-Komponente (siehe [formgenerator.md](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) | diff --git a/b-reference/frontend-nyla/formgenerator.md b/b-reference/frontend-nyla/formgenerator.md index ec25173..c429d4c 100644 --- a/b-reference/frontend-nyla/formgenerator.md +++ b/b-reference/frontend-nyla/formgenerator.md @@ -1,6 +1,6 @@ - - + + # FormGenerator -- Referenz @@ -14,8 +14,10 @@ Der FormGenerator besteht aus mehreren Komponenten: | `FormGeneratorControls` | `components/FormGenerator/FormGeneratorControls/FormGeneratorControls.tsx` | Toolbar: Suche, Pagination, Batch-Action-Buttons, "Select All Filtered"-Banner | | `FormGeneratorForm` | `components/FormGenerator/FormGeneratorForm/FormGeneratorForm.tsx` | Dynamische Formulare aus Backend-Attribut-Definitionen | | `FormGeneratorList` | `components/FormGenerator/FormGeneratorList/FormGeneratorList.tsx` | Listen-Darstellung | +| `FormGeneratorReport` | `components/FormGenerator/FormGeneratorReport/FormGeneratorReport.tsx` | Berichte / Report-Layouts (nicht: es gibt kein separates `FormGeneratorChart`) | +| `FormGeneratorTree` | `components/FormGenerator/FormGeneratorTree/FormGeneratorTree.tsx` | Generische Baumkomponente mit Provider-Pattern, Multiselect, DnD, Inline-Editing, Scope/Neutralize, Batch-Actions | -Diese Referenz fokussiert auf **FormGeneratorTable** und das zugehoerige Backend-API-Pattern. +Diese Referenz dokumentiert **FormGeneratorTable** (inkl. Backend-API-Pattern) und **FormGeneratorTree**. --- @@ -344,3 +346,91 @@ Alle folgenden Endpunkte unterstuetzen `mode=filterValues` und `mode=ids`: | `routeFeatureTrustee.py` | `GET /api/trustee/{id}/positions` | RBAC SQL + In-Memory Fallback | | `routeFeatureRealEstate.py` | `GET /api/realestate/{id}/projects` | In-Memory | | `routeFeatureRealEstate.py` | `GET /api/realestate/{id}/parcels` | In-Memory | + +--- + +## FormGeneratorTree + +`FormGeneratorTree` ist die generische Baumkomponente der FormGenerator-Familie. +Sie ersetzt die fruehere `FolderTree`-Komponente und folgt einem Provider-Pattern, +bei dem die Datenlogik (API-Aufrufe, CRUD-Operationen) in austauschbare +`TreeNodeProvider`-Implementierungen ausgelagert wird. + +### Dateien + +| Datei | Inhalt | +|-------|--------| +| `FormGeneratorTree.tsx` | Haupt-Komponente (Rendering, Selection, DnD, Inline-Editing) | +| `FormGeneratorTree.module.css` | Styles | +| `types.ts` | `TreeNode`, `TreeNodeProvider`, `TreeBatchAction`, `FormGeneratorTreeProps` | +| `providers/FolderFileProvider.tsx` | Referenz-Provider fuer Files + Folders | + +### TreeNodeProvider-Interface + +```typescript +interface TreeNodeProvider { + rootKey: string; + loadChildren(parentId: string | null, ownership: Ownership): Promise[]>; + canCreate?(parentId: string | null): boolean; + canRename?(node: TreeNode): boolean; + canDelete?(node: TreeNode): boolean; + canMove?(source: TreeNode, target: TreeNode | null): boolean; + canPatchScope?(node: TreeNode): boolean; + canPatchNeutralize?(node: TreeNode): boolean; + createChild?(parentId: string | null, name: string): Promise>; + renameNode?(id: string, newName: string): Promise; + deleteNodes?(ids: string[]): Promise; + moveNodes?(ids: string[], targetParentId: string | null): Promise; + patchScope?(ids: string[], scope: ScopeValue, cascadeChildren?: boolean): Promise; + patchNeutralize?(ids: string[], neutralize: boolean): Promise; + getBatchActions?(): TreeBatchAction[]; +} +``` + +Die `can*`-Methoden steuern, welche Affordances pro Knoten sichtbar sind. +Alle Mutations-Methoden sind optional -- ein read-only Provider implementiert nur +`loadChildren`. + +### Built-in Features + +- **Expand/Collapse** mit Lazy-Load via `loadChildren` +- **Multiselect** mit Shift/Ctrl; getrennt zwischen `ownership='own'` und `'shared'` +- **Cascade-Selection**: Folder selektieren -> alle eigenen Children selektiert +- **DnD** (`application/x-poweron-tree-items`); aus `shared` nur lesende Drops +- **Inline-Rename** (Doppelklick / F2) -- nur fuer eigene Knoten +- **Scope/Neutralize** Icons: interaktiv fuer eigene, Indikator fuer geteilte +- **Batch-Actions** mit `typeFilter` (z.B. separate Delete-Buttons fuer Ordner vs. Dateien) +- **Refresh-Button** im Section-Header +- **Confirmation-Dialoge** vor Delete-Aktionen + +### Props (`FormGeneratorTreeProps`) + +| Prop | Typ | Beschreibung | +|------|-----|-------------| +| `provider` | `TreeNodeProvider` | Daten- und Operations-Provider | +| `ownership` | `'own' \| 'shared'` | Bestimmt Tree-Kontext (CRUD vs. read-only) | +| `title` | `string?` | Section-Header | +| `compact` | `boolean?` | Kompakte Darstellung (z.B. in UDB) | +| `collapsible` | `boolean?` | Section kollabierbar | +| `defaultCollapsed` | `boolean?` | Initial kollabiert | +| `emptyMessage` | `string?` | Anzeige bei leerem Tree | +| `onNodeClick` | `(node) => void` | Callback bei Knoten-Klick | +| `onSelectionChange` | `(selectedIds) => void` | Callback bei Selektions-Aenderung | +| `onRefresh` | `() => void` | Callback nach internem Refresh (z.B. fuer Tabellen-Sync) | + +### Verwendung + +Aktuell in zwei Kontexten eingebunden, jeweils mit `FolderFileProvider`: + +- **`UnifiedDataBar/FilesTab.tsx`**: Zwei Sektionen (Eigene / Geteilt mit mir) +- **`pages/basedata/FilesPage.tsx`**: Split-View mit Tree links und `FormGeneratorTable` rechts; + Tree-Selektion filtert Tabelle nach Ordner + +### Tree vs. Table-with-Grouping + +| Kriterium | `FormGeneratorTree` | `FormGeneratorTable` mit `groupingConfig` | +|-----------|--------------------|--------------------------------------------| +| Persistenz | Knoten sind persistente Entitaeten (z.B. Folders) | Gruppen sind UI-only JSON (`TableGrouping`) | +| CRUD | Create/Rename/Delete/Move pro Knoten | Kein CRUD auf Gruppen-Ebene | +| RBAC | Scope/Neutralize pro Knoten, Owner-Guards | Keine pro-Gruppen-Berechtigungen | +| Einsatz | Hierarchische Datenstrukturen | Flache Listen mit visueller Kategorisierung | diff --git a/c-work/1-plan/2026-04-ai-reports-theming-and-pipeline.md b/c-work/4-done/2026-04-ai-reports-theming-and-pipeline.md similarity index 91% rename from c-work/1-plan/2026-04-ai-reports-theming-and-pipeline.md rename to c-work/4-done/2026-04-ai-reports-theming-and-pipeline.md index 6eecf9a..4c7417b 100644 --- a/c-work/1-plan/2026-04-ai-reports-theming-and-pipeline.md +++ b/c-work/4-done/2026-04-ai-reports-theming-and-pipeline.md @@ -1,7 +1,8 @@ - + + # AI Reports: Generisches Style-Management, AI-Call-Konfiguration, Inline-Bilder @@ -388,46 +389,46 @@ flowchart LR ### Phase 1 -- MD->JSON konsolidieren + Inline-Runs -- [ ] `subDocumentUtility.markdownToDocumentJson` zur einzigen Quelle +- [x] `subDocumentUtility.markdownToDocumentJson` zur einzigen Quelle machen (module-level Funktion, Z. 12+). -- [ ] `_mediaTools._markdownToDocumentJson` (nested in +- [x] `_mediaTools._markdownToDocumentJson` (nested in `_registerMediaTools`, Z. 27-163) ENTFERNEN. Stattdessen `subDocumentUtility.markdownToDocumentJson` aufrufen. Die Image- Resolution-Logik (Z. 241-277: fileId -> base64 aus KnowledgeStore/Chat) VERBLEIBT als Post-Processing nach dem Parser-Aufruf. -- [ ] Schema-Erweiterung in `datamodels/datamodelJson.py`: +- [x] Schema-Erweiterung in `datamodels/datamodelJson.py`: - `paragraph.inlineRuns: List[InlineRun]` (KEIN Backwards-Compat- Feld `text: str` -- Clean-Break). - `bullet_list.items: List[List[InlineRun]]`. - `table.cell` neu: `cellContent: List[InlineRun]`. - Altes String-basiertes Format wird NICHT mehr unterstuetzt. -- [ ] MD-Parser im konsolidierten Helper: +- [x] MD-Parser im konsolidierten Helper: - Inline `![alt](file:xyz)` an beliebiger Position erkennen -> `image`-Run. - Inline `[text](url)` -> `link`-Run. - `**bold**` / `*italic*` / `` `code` `` -> entsprechende Runs. - Tabellen-Zellen: voll geparst (inkl. Bild, Links, Formatierung). -- [ ] Helper `_renderInlineRuns(runs, style, container)` als gemeinsame +- [x] Helper `_renderInlineRuns(runs, style, container)` als gemeinsame Spec; pro Renderer eigene Implementierung. ### Phase 2 -- Style-System -- [ ] Defaults-Modul `gateway/modules/serviceCenter/services/serviceGeneration/styleDefaults.py` +- [x] Defaults-Modul `gateway/modules/serviceCenter/services/serviceGeneration/styleDefaults.py` mit neutralen Default-Werten fuer alle Style-Felder. -- [ ] Style-Resolver in `mainServiceGeneration.py`: +- [x] Style-Resolver in `mainServiceGeneration.py`: `_resolveStyle(metadataStyle: dict | None) -> ResolvedStyle` (deep-merge defaults <- agentStyle). -- [ ] Renderer auf Resolver umstellen: kein Renderer hat mehr eigene +- [x] Renderer auf Resolver umstellen: kein Renderer hat mehr eigene Default-Konstanten. -- [ ] `renderDocument`-Tool (`_mediaTools.py`) Schema: +- [x] `renderDocument`-Tool (`_mediaTools.py`) Schema: - Optionaler Parameter `style` (object) wird in `metadata.style` gemappt. - Tool-Beschreibung dokumentiert das Schema mit Beispielen. ### Phase 3 -- Renderer-Refactor (generische Style-Komponenten) -- [ ] DOCX (`rendererDocx.py`): +- [x] DOCX (`rendererDocx.py`): - `_createTableRowXml` (~ 644-697) Header-Farbe/-Schrift aus `style.table`. - `_renderJsonImage` (~ 1016-1082) Default-Breite aus @@ -437,48 +438,48 @@ flowchart LR - Page-Margins / Header-Logo / Footer-Text aus `style.page`. - `templateName: corporate|minimal` deprecaten (1 Release lang warnen, dann raus). -- [ ] XLSX (`rendererXlsx.py`): +- [x] XLSX (`rendererXlsx.py`): - `_renderJsonImage` Bildbreite aus `style.image`. - Tabellen-Header aus `style.table`. - Inline-Bilder per Cell-Anchor (siehe Phase 4). -- [ ] PPTX (`rendererPptx.py`): +- [x] PPTX (`rendererPptx.py`): - Slide-Size aus `style.page`. - Title/Body aus `style.headings`. - Inline-Bilder als zusaetzliche Shape (Fallback). -- [ ] HTML (`rendererHtml.py`): +- [x] HTML (`rendererHtml.py`): - `lang` aus `metadata.language`. - Inline-CSS aus `style` generiert (`_generateCssStyles` umbauen). - Inline-Bilder als ``. -- [ ] PDF (`rendererPdf.py`): +- [x] PDF (`rendererPdf.py`): - Sample-Style-Set aus `style`. - Page-Margins aus `style.page.marginsPt`. - Inline-Bilder als reportlab Inline-Image. ### Phase 4 -- Inline-Bilder ueberall (Renderer-Spezifika) -- [ ] DOCX: Cell-Image via `cell.add_paragraph().add_run().add_picture(...)`. -- [ ] DOCX/PDF: Inline-Image im Paragraph als Run-mit-Image. -- [ ] XLSX: `openpyxl.drawing.image.Image` mit Cell-Anchor; pro Bild eine +- [x] DOCX: Cell-Image via `cell.add_paragraph().add_run().add_picture(...)`. +- [x] DOCX/PDF: Inline-Image im Paragraph als Run-mit-Image. +- [x] XLSX: `openpyxl.drawing.image.Image` mit Cell-Anchor; pro Bild eine neue Image-Instance (openpyxl-Constraint). -- [ ] HTML: `` direkt in `` / `

` / `

  • ` -- trivial. -- [ ] PPTX: Inline-in-Text-Run nicht moeglich -> Image als zusaetzliche +- [x] HTML: `` direkt in `` / `

    ` / `

  • ` -- trivial. +- [x] PPTX: Inline-in-Text-Run nicht moeglich -> Image als zusaetzliche Shape unter dem Text-Frame, mit Warning im JSON-Output (`_renderingFallback: pptx-image-below-text`). -- [ ] Tests pro Format: 1× Inline-Bild im Paragraph, 1× in +- [x] Tests pro Format: 1× Inline-Bild im Paragraph, 1× in Tabellenzelle, 1× in Listen-Item. ### Phase 5 -- AI-Call-Konfiguration pro Node (Datenmodell + Backend) -- [ ] `gateway/modules/datamodels/datamodelAi.py`: +- [x] `gateway/modules/datamodels/datamodelAi.py`: - `AiCallOptions.allowedModels: Optional[List[str]] = None` (analog zu `allowedProviders` in Z. 164 -- KEIN neues Feld in `AiCallRequest`). -- [ ] `gateway/modules/datamodels/datamodelAuto.py` (oder wo immer das +- [x] `gateway/modules/datamodels/datamodelAuto.py` (oder wo immer das `Workflow`-Modell liegt): - `Workflow.allowedModels: Optional[List[str]] = None` (analog `Workflow.allowedProviders`). -- [ ] `gateway/modules/serviceCenter/services/serviceAi/mainServiceAi.py`: +- [x] `gateway/modules/serviceCenter/services/serviceAi/mainServiceAi.py`: - Neue private Methode `_calculateEffectiveModels()` analog zu `_calculateEffectiveProviders()` (Z. ~1196). RBAC-Modelle ∩ `Workflow.allowedModels` ∩ `request.options.allowedModels`. @@ -490,14 +491,14 @@ flowchart LR - Fail-fast `RuntimeError` wenn die Filterkette leer wird, mit Audit-Log-Zeile (modelle, provider, rbac-permitted, workflow, node). -- [ ] `_ServicesAdapter.__getattr__` in `mainServiceAi.py` (Z. 88-90) +- [x] `_ServicesAdapter.__getattr__` in `mainServiceAi.py` (Z. 88-90) erweitern: Tuple-Liste um `"allowedModels"` ergaenzen (damit `service.allowedModels` analog `service.allowedProviders` aus dem Workflow durchgereicht wird). ### Phase 5a -- Workspace User-Settings (Persistenz fuer alle drei Felder) -- [ ] Neue persistente Settings pro User × Workspace-Instanz. +- [x] Neue persistente Settings pro User x Workspace-Instanz. Tabelle `WorkspaceUserSettings` in **`poweron_app`** (dort wo `FeatureAccess`, `FeatureInstance` etc. leben; User-spezifische Instanz-Daten). Additiv via Auto-Init: @@ -507,22 +508,22 @@ flowchart LR - `allowedProviders JSONB DEFAULT '[]'` - `allowedModels JSONB DEFAULT '[]'` - PK / Unique: (`instanceId`, `userId`). -- [ ] Backend-Endpoints in `routeFeatureWorkspace.py`: +- [x] Backend-Endpoints in `routeFeatureWorkspace.py`: - `GET /api/workspace/{instanceId}/user-settings` -- effektive Settings fuer current user (Auto-Insert mit Defaults wenn leer). - `PUT /api/workspace/{instanceId}/user-settings` -- speichern (alle drei Felder im Body). -- [ ] `WorkspaceInputRequest.allowedModels: List[str] = Field(default_factory=list)` +- [x] `WorkspaceInputRequest.allowedModels: List[str] = Field(default_factory=list)` (additiv, `routeFeatureWorkspace.py` Z. 102-113). `requireNeutralization` und `allowedProviders` sind dort schon vorhanden (Z. 111-112). -- [ ] In `_runWorkspaceAgent` (Z. 693+): `allowedModels` ebenso +- [x] In `_runWorkspaceAgent` (Z. 693+): `allowedModels` ebenso durchreichen wie heute `allowedProviders` (Z. 713-717) -- in das `request.options.allowedModels`-Feld schreiben (siehe Phase 5 Datenmodell). Pattern analog: `aiService.services.allowedModels = allowedModels`. -- [ ] Frontend Workspace: +- [x] Frontend Workspace: - Workspace-Settings-Tab/Panel mit allen drei Feldern: - Toggle `requireNeutralization`. - Multi-Select `allowedProviders` (Optionen aus @@ -539,12 +540,12 @@ flowchart LR ### Phase 5b -- AI-Nodes im FlowEditor -- [ ] `gateway/modules/features/graphicalEditor/nodeDefinitions/ai.py`: +- [x] `gateway/modules/features/graphicalEditor/nodeDefinitions/ai.py`: - Modul-lokaler Helper `_AI_COMMON_PARAMS = [...]` mit den 2 neuen Parametern. - In ALLE 8 Eintraege von `AI_NODES` als `*_AI_COMMON_PARAMS` anhaengen. -- [ ] `gateway/modules/workflows/methods/methodAi/actions/*.py`: +- [x] `gateway/modules/workflows/methods/methodAi/actions/*.py`: - In jeder Action (`process.py`, `webResearch.py`, `summarizeDocument.py`, `translateDocument.py`, `convertDocument.py`, `generateDocument.py`, @@ -555,7 +556,7 @@ flowchart LR `request.options.allowedModels` setzen. - Helper-Funktion `_applyCommonAiParams(parameters, request)` in `methodAi/_common.py` (neu) zum Reduzieren von Boilerplate. -- [ ] FlowEditor (Frontend): +- [x] FlowEditor (Frontend): - Neuer `frontendType: "modelMultiSelect"` -- holt verfuegbare Modelle ueber `GET /api/system/ai-models` und stellt Multi-Select dar. @@ -564,27 +565,27 @@ flowchart LR ### Phase 6 -- Trustee-Templates anpassen (entkoppelt vom Bau-Plan B) -- [ ] In `mainTrustee.py`-Templates: neuen Node-Parameter `style`-Block +- [x] In `mainTrustee.py`-Templates: neuen Node-Parameter `style`-Block setzen (Finanzreport-Defaults: dunkelblaue Headings, konservatives Calibri etc.) -- damit Trustee out-of-the-box gut aussieht. Der in Plan B gesetzte `documentTheme: "finance"` wird als Hint fuer den Agent im Prompt benutzt (NICHT als Renderer-Parameter); der Agent soll daraus seinen `style`-Block ableiten. -- [ ] `requireNeutralization: true` falls Trustee-Daten sensibel - (entscheidet Compliance-Doku, Default empfohlen). -- [ ] `allowedModels` pro Trustee-Workflow optional pinned (z.B. nur +- [x] `requireNeutralization: false` (User-Entscheid: Buchhaltungsdaten + duerfen nicht anonymisiert werden, sonst Fantasie-Zahlen). +- [x] `allowedModels` pro Trustee-Workflow optional pinned (z.B. nur private LLM fuer Finance-Daten). -- [ ] `documentTheme`-Parameter im Budget-Template (aus Plan B) in den +- [x] `documentTheme`-Parameter im Budget-Template (aus Plan B) in den Prompt als Kontext-Hint einfuegen, statt als separaten Renderer-Parameter. Der Renderer kennt KEIN Theme-Konzept. ### Phase 7 -- Tests / Snapshots -- [ ] Pro Format ein Snapshot-Test mit Custom-Style: +- [x] Pro Format ein Snapshot-Test mit Custom-Style: `test_render_with_agent_style_.py`. -- [ ] Inline-Bilder-Tests pro Format (siehe Phase 4). -- [ ] AI-Gate-Tests: +- [x] Inline-Bilder-Tests pro Format (siehe Phase 4). +- [x] AI-Gate-Tests: `test_allowed_models_filter.py` (Whitelist greift), `test_allowed_models_empty_fails.py` (Fail-fast), `test_workspace_propagates_neutralization.py`. @@ -611,18 +612,18 @@ flowchart LR | ID | AC | Art | Automatisiert | Repo-Pfad | Status | |----|----|-----|--------------|-----------|--------| -| T1 | 1,2 | snapshot | ja | gateway/tests/serviceGeneration/test_agent_style_docx.py | pending | -| T2 | 3 | integration | ja | gateway/tests/serviceGeneration/test_inline_image_paragraph.py | pending | -| T3 | 4 | integration | ja | gateway/tests/serviceGeneration/test_inline_image_table_cell.py | pending | -| T4 | 5 | integration | ja | gateway/tests/serviceGeneration/test_inline_image_list_item_html.py | pending | -| T5 | 6 | integration | ja | gateway/tests/serviceGeneration/test_inline_image_pptx_fallback.py | pending | -| T6 | 7 | integration | ja | gateway/tests/serviceAi/test_node_neutralization_flag.py | pending | -| T7 | 8,9 | integration | ja | gateway/tests/serviceAi/test_allowed_models_whitelist.py | pending | -| T8 | 10 | integration | ja | gateway/tests/features/workspace/test_user_settings_persistence.py | pending | -| T9 | 11 | integration | ja | gateway/tests/serviceAi/test_workspace_propagates_settings.py | pending | -| T10 | 12 | unit | ja | gateway/tests/serviceAi/test_effective_models_and_providers.py | pending | -| T11 | 13 | unit | ja | gateway/tests/serviceCenter/test_md_to_json_consolidation.py | pending | -| T12 | -- | unit | ja | frontend_nyla/src/components/flowEditor/__tests__/AiNodeProperties.test.tsx | pending | +| T1 | 1,2 | snapshot | ja | gateway/tests/serviceGeneration/test_agent_style_docx.py | done | +| T2 | 3 | integration | ja | gateway/tests/serviceGeneration/test_inline_image_paragraph.py | done | +| T3 | 4 | integration | ja | gateway/tests/serviceGeneration/test_inline_image_table_cell.py | done | +| T4 | 5 | integration | ja | gateway/tests/serviceGeneration/test_inline_image_list_item_html.py | done | +| T5 | 6 | integration | ja | gateway/tests/serviceGeneration/test_inline_image_pptx_fallback.py | done | +| T6 | 7 | integration | ja | gateway/tests/serviceAi/test_node_neutralization_flag.py | done | +| T7 | 8,9 | integration | ja | gateway/tests/serviceAi/test_allowed_models_whitelist.py | done | +| T8 | 10 | integration | ja | gateway/tests/features/workspace/test_user_settings_persistence.py | done | +| T9 | 11 | integration | ja | gateway/tests/serviceAi/test_workspace_propagates_settings.py | done | +| T10 | 12 | unit | ja | gateway/tests/serviceAi/test_effective_models_and_providers.py | done | +| T11 | 13 | unit | ja | gateway/tests/serviceCenter/test_md_to_json_consolidation.py | done | +| T12 | -- | unit | ja | frontend_nyla/src/components/flowEditor/__tests__/AiNodeProperties.test.tsx | deferred | ## Links @@ -640,10 +641,10 @@ flowchart LR ## Abschluss - [ ] `wiki/b-reference/gateway/ai-agent.md` -- Style-System + per-call - Konfiguration ergaenzen + Konfiguration ergaenzen (TODO: bei naechster Wiki-Review-Runde) - [ ] `wiki/b-reference/gateway/workflow.md` -- AI-Node-Standardparameter - dokumentieren + dokumentieren (TODO: bei naechster Wiki-Review-Runde) - [ ] `wiki/b-reference/platform/neutralization.md` -- per-Node-Trigger - ergaenzen (Praezedenzkette: Feature -> Workspace -> Node -> Request) + ergaenzen (TODO: bei naechster Wiki-Review-Runde) - [ ] `wiki/TOPICS.md` ggf. neuer Eintrag "AI-Node-Konfiguration" -- [ ] Dieses Dokument -> `z-archive/` verschoben +- [x] Dieses Dokument -> `4-done/` verschoben diff --git a/c-work/3-validate/2026-04-id-unified-knowledge-indexing-rag-concept.md b/c-work/4-done/2026-04-id-unified-knowledge-indexing-rag-concept.md similarity index 100% rename from c-work/3-validate/2026-04-id-unified-knowledge-indexing-rag-concept.md rename to c-work/4-done/2026-04-id-unified-knowledge-indexing-rag-concept.md diff --git a/c-work/3-validate/2026-04-trustee-budget-comparison-refactor.md b/c-work/4-done/2026-04-trustee-budget-comparison-refactor.md similarity index 100% rename from c-work/3-validate/2026-04-trustee-budget-comparison-refactor.md rename to c-work/4-done/2026-04-trustee-budget-comparison-refactor.md diff --git a/c-work/4-done/2026-05-formgenerator-tree-and-folder-recovery.md b/c-work/4-done/2026-05-formgenerator-tree-and-folder-recovery.md new file mode 100644 index 0000000..7ee40ac --- /dev/null +++ b/c-work/4-done/2026-05-formgenerator-tree-and-folder-recovery.md @@ -0,0 +1,749 @@ + + + + + +# FormGeneratorTree (NEU) + Recovery der persistenten Folder/File-Funktionen + +## Beschreibung und Kontext + +In den Commits `c8e9304` (2026-04-29), `e7a79a3` und `7c05cb0` (beide 2026-04-30, +zusammen -1263 / +897) wurde `FolderTree` aus + +- `frontend_nyla/src/pages/basedata/FilesPage.tsx` +- `frontend_nyla/src/components/UnifiedDataBar/FilesTab.tsx` + +entfernt und durch `FormGeneratorTable` mit `groupingConfig` ersetzt. Begleitend +wurden die Folder-Operationen aus `FileContext.tsx` und `useFiles.ts` reduziert, +`FileInfo.folderId` aus `fileApi.ts` und das Pydantic-Feld `FileItem.folderId` +aus `gateway/modules/datamodels/datamodelFiles.py` entfernt. Im Repo liegt das +Archiviertes Skript `gateway/modules/migrations/_archive/migrate_folders_to_groups.py`, das die +`FileFolder`-Tabelle + `FileItem.folderId`-Spalte in einen JSON-Tree +(`TableGrouping.rootGroups`, Context `files/list`) ueberfuehren *kann* -- per +Default `--dry-run`, ohne begleitende Alembic-Migration zum DROP-COLUMN / +DROP-TABLE. + +**Datenbank-Stand:** `FileFolder` und `FileItem.folderId` sind in der DB +unveraendert vorhanden. Recovery laeuft als reine Pydantic-/Routen-/FE-Wieder- +verdrahtung -- **kein** neues Schema fuer diesen Track. + +### Beobachtete funktionale Luecken (nach dem Wechsel zu Table-only) + +- Folders koennen nicht mehr neu erstellt werden (UI-Affordance entfernt). +- Pro-File / pro-Folder Scope-Icons + Neutralize-Toggle fehlen + (`patchGroupScope` / `patchGroupNeutralize` sind Bulk-Operationen auf Group- + Items und ersetzen die Per-Item-UI nicht). +- Multiselect / Multidelete mit Cascade ueber gemischte Folders+Files-Auswahl + ist nicht abgebildet. +- DnD-Protokoll inkompatibel: `application/group-id`, + `application/group-file-ids` und `application/tree-items` mit + `type: 'file' | 'group'` waren auf Tabellen-Gruppierungen optimiert; + `WorkspaceInput.TreeItemDrop` musste mitziehen. +- Mandanten-geteilte Folder-Strukturen lassen sich mit per-User-`TableGrouping` + nicht abbilden. + +### Tree-Fragmentierung im Frontend (zusaetzlicher Treiber) + +Heute existieren mindestens **acht parallele** Tree-Implementierungen: + +| # | Datei | Inhalt | Lazy | Multiselect | Inline-Actions | +|---|------|--------|---|---|---| +| 1 | `components/FolderTree/FolderTree.tsx` | Files+Folders (entkoppelt) | nein | ja | ja | +| 2 | `components/FolderTree/SharepointBrowseTree.tsx` | SharePoint-Browse | ja | nein | nein | +| 3 | `components/UnifiedDataBar/SourcesTab.tsx` | Connections + Feature-Daten | ja | partiell | partiell | +| 4 | `components/UnifiedDataBar/ChatsTab.tsx` | Chat-Historien | nein | nein | partiell | +| 5 | `components/FlowEditor/nodes/shared/DataPicker.tsx` | Upstream-Outputs | nein | nein | nein | +| 6 | `components/Navigation/TreeNavigation/*` + `MandateNavigation.tsx` | Sidebar | nein | nein | nein | +| 7 | `pages/admin/InstanceHierarchyView.tsx` | RBAC-Hierarchie | nein | nein | nein | +| 8 | `pages/views/redmine/redmineTreeLogic.ts` | Redmine-Issues | nein | nein | partiell | +| 9 | `components/AccessRules/AccessRulesEditor.tsx` | RBAC-Rules | nein | nein | partiell | + +### Geschaeftstreiber + +1. Recovery der Datei-/Ordner-Funktionen, ohne `FormGeneratorTable.groupingConfig` + fuer andere Use-Cases zurueckzubauen. +2. Konsolidierung der Tree-Implementierungen auf eine getestete + Komponente -- ein Bugfix-/Feature-Punkt statt acht. +3. UDB-Konsistenz: drei feste Tabs (Files/Folders, Sources, Chats), alle als + hierarchische Sicht auf persistente Entitaeten. + +--- + +## Architektur + +### `FormGeneratorTree` als neues Familienmitglied + +Die FormGenerator-Familie wird um eine Tree-Komponente erweitert (parallel zu +`FormGeneratorTable`, `FormGeneratorForm`, `FormGeneratorList`, +`FormGeneratorReport`). + +| Aspekt | Bestimmung | +|---|---| +| Aufgabe | Hierarchische, lazy-ladbare Sicht auf persistente Entitaeten | +| Datenbindung | Pro Use-Case ein **Provider** (siehe Provider-Interface unten) | +| Spalten | Backend-Attribut-Resolution analog `resolveColumnTypes`; **keine** hardcoded Cell-Renderer in der Page; Pflicht-Spalte = Tree-Spalte (Indent + Chevron + Icon + Name) | +| Selektion | Multiselect mit Shift/Ctrl/Cmd, kaskadierend (Folder selektieren -> alle eigenen Children); `BatchAction[]`-API analog Table | +| DnD | Standard-MIME `application/x-poweron-tree-items`, Payload `{ id, type, name, providerKey }[]` | +| Nicht zustaendig | Report-Rendering (das ist `FormGeneratorReport`); Forms (`FormGeneratorForm`); eigene Datenpersistenz (haelt der Provider); Replikation der `groupingConfig`-Logik aus `FormGeneratorTable` | + +**Provider-Interface (vorlaeufig):** + +```typescript +type Ownership = 'own' | 'shared'; + +interface TreeNode { + id: string; + name: string; + parentId: string | null; + ownership: Ownership; + scope?: 'personal' | 'featureInstance' | 'mandate' | 'global'; + neutralize?: boolean; + // Parent-Kontext (z.B. Folder) ist fuer den Current-User nicht sichtbar; + // Knoten wird im Root mit Hint-Icon gerendert. + contextOrphan?: boolean; + data?: T; +} + +interface TreeNodeProvider { + rootKey: string; + loadChildren(parentId: string | null, ownership: Ownership): Promise[]>; + getAttributes(): Promise; + // Mutations gelten ausschliesslich fuer ownership='own'. + canCreate?(parentId: string | null): boolean; + canRename?(node: TreeNode): boolean; + canDelete?(node: TreeNode): boolean; + canMove?(source: TreeNode, target: TreeNode | null): boolean; + canPatchScope?(node: TreeNode): boolean; + canPatchNeutralize?(node: TreeNode): boolean; + createChild?(parentId: string | null, name: string): Promise>; + renameNode?(id: string, newName: string): Promise; + deleteNodes?(ids: string[]): Promise; + moveNodes?(ids: string[], targetParentId: string | null): Promise; + patchScope?(ids: string[], scope: string, cascadeChildren?: boolean): Promise; + patchNeutralize?(ids: string[], neutralize: boolean): Promise; +} +``` + +### Folder/File vs. `TableGrouping` (zwei orthogonale Konzepte) + +| Eigenschaft | Folder/File (`FileFolder`, `FileItem`) | Group (`TableGrouping`) | +|---|---|---| +| Identitaet | First-class Entity (UUID, audit, RBAC) | JSON-Knoten in einem User-spezifischen Tree | +| Persistenz | DB-Zeile | JSON-Blob in `TableGrouping.rootGroups` | +| Sichtbarkeit | Per `scope` (personal/featureInstance/mandate/global) | per-User (`userId` ist Partition-Key) | +| RBAC | Per-Item Scope, Owner-only-Mutationen | keine; nur enthaltene Items haben Scope | +| Move / Delete | Beeinflusst alle berechtigten User | Lokal pro User | +| Use-Case | Files/Folders (mandate-shared) | Persoenliche Sortier-Sicht auf flachen Listen (Workflow-Runs, Trustee-Positionen, ...) | + +`FormGeneratorTable.groupingConfig` bleibt unveraendert. `FormGeneratorTree` +und `groupingConfig` sind komplementaer; beide Sichten duerfen +nebeneinander auf denselben Files leben (siehe `FilesPage` View-Toggle). + +### Use-Case-Zuordnung + +| Use-Case | Komponente | +|---|---| +| Files in Folders (mandate-shared) | `FormGeneratorTree` + `FolderFileProvider` | +| External-Source-Browse (kDrive, SharePoint, ...) | `FormGeneratorTree` + `ConnectorBrowseProvider` | +| Feature-Daten in UDB (Mandate -> FeatureConnection -> Group/Table -> Record) | `FormGeneratorTree` + `FeatureDataProvider` | +| Chat-Historie nach Feature gruppiert | `FormGeneratorTree` + `ChatProvider` | +| AccessRules-Editor | `FormGeneratorTree` + `RbacRuleProvider` | +| FlowEditor DataPicker (Upstream-Output-Pfade) | `FormGeneratorTree` + `DataRefProvider` | +| InstanceHierarchyView (RBAC-Audit) | `FormGeneratorTree` + `InstanceHierarchyProvider` | +| Workflow-Runs nach Mandant + Status filtern | `FormGeneratorTable` + `groupingConfig` (flach, per-User) | +| Sidebar (`MandateNavigation`, `TreeNavigation`) | bleibt eigene Komponente -- Routing-Affordance, kein CRUD-Tree | + +Faustregel: Hat das Item eine eigene DB-Identitaet jenseits des aktuellen Users? +Ja -> `FormGeneratorTree`. Nein -> `FormGeneratorTable.groupingConfig`. + +### RBAC- und Sichtbarkeits-Modell (verbindlich) + +Die Recovery folgt dem fuer Files **bereits implementierten** RBAC-Modell +(siehe `interfaceRbac.buildFilesScopeWhereClause`, +`interfaceDbManagement.updateFile / deleteFile / deleteFilesBatch`). Folders +bekommen 1:1 dieselbe Mechanik: + +1. **Default-Sichtbarkeit `personal`.** Jedes neu angelegte Folder/File ist + nur fuer `sysCreatedBy = currentUser` sichtbar. +2. **Sharing nur ueber `scope`.** Andere User sehen ein Objekt, sobald der + Eigentuemer den Scope explizit erhoeht (`featureInstance` / + `mandate` / `global`; `global` setzt `ALL`-Permission voraus, in der + Praxis SysAdmin). +3. **Mutation = Owner-only** (oder `ALL`-Permission). Rename / Move / Delete / + Scope-Cycle / Neutralize-Toggle sind fuer Nicht-Eigentuemer 403; die UI + blendet die Affordance aus. +4. **Zwei getrennte Trees in jeder Files-Sicht:** + - **„Eigene“** -- `sysCreatedBy = currentUser`. Volles CRUD; das Scope-Icon + zeigt Sharing-Status an. + - **„Geteilt mit mir“** -- `sysCreatedBy != currentUser`, ueber `scope` + sichtbar. Read-only; Scope-/Neutralize nur als Indikator-Icon. DnD aus + diesem Tree ist rein lesend (z.B. Drop in Chat-Input); Drop in den + eigenen Tree erzeugt **keinen** Move (kein Ownership-Wechsel). +5. **Render-Regel.** Files erscheinen unter ihrem Folder, wenn der Folder fuer + den Current-User sichtbar ist. Fehlt der Folder-Kontext (`FileItem.scope` + geteilt, `FileFolder.scope` nicht), wird die Datei im Root des passenden + Trees mit `contextOrphan`-Hint („kein Folder-Kontext geteilt“) gerendert. + Aendert der Owner spaeter den Folder-Scope, wandert die Datei automatisch + unter den Folder. +6. **Cascade-Delete nur Owner.** Multidelete eines (Sub-)Trees laeuft genau + dann, wenn alle Items dem Current-User gehoeren. Gemischte Selektionen + (Eigene + Geteilte) blockieren die Bulk-Aktion mit Hinweis. +7. **Folder-Scope-Cascade auf Files = opt-in.** Beim Setzen eines Folder- + Scopes wird der Owner per Dialog gefragt, ob die enthaltenen Files den + Scope erben. Default: nein (Folders und Files behalten unabhaengige Scopes, + konsistent mit der heutigen Files-Logik). +8. **Folder-Neutralize-Vererbung beim Drop = nein.** Beim Drop eines Files in + einen Folder mit `neutralize=true` wird der Datei-Wert nicht automatisch + geaendert; ein Indikator zeigt den Mismatch. + +> **Datentabellen-Sicht (`FormGeneratorTable.groupingConfig` auf Files):** +> Status quo. Die Tabelle zeigt weiterhin alle sichtbaren Files (eigene + +> geteilte) flach; nur die Tree-Sicht trennt Eigene/Geteilt. Falls spaeter +> gewuenscht, kann ein `ownerFilter` ergaenzt werden -- nicht in diesem Plan. + +--- + +## Entscheidungen (konsolidiert) + +| # | Datum | Bereich | Entscheidung | Begruendung | +|---|-------|--------|-------------|------------| +| D1 | 2026-05-01 | Komponenten | UDB hat fix drei Tabs (Files, Sources, Chats); alle drei rendern `FormGeneratorTree` | Konsistente UDB-Struktur | +| D2 | 2026-05-01 | Komponenten | FormGenerator-Familie um `Tree` erweitern; nicht `FolderTree` v2 | Konsolidiert ~8 parallele Tree-Implementierungen | +| D3 | 2026-05-01 | Komponenten | `FormGeneratorTable.groupingConfig` bleibt unveraendert | Gilt fuer flache Daten (Workflow-Runs, Trustee-Positionen) | +| D4 | 2026-05-01 | Komponenten | Berichte = `FormGeneratorReport`; **kein** `FormGeneratorChart` (Namens-Klarstellung) | Bestehende Familienkomponente | +| D5 | 2026-05-01 | Komponenten | Sidebar (`MandateNavigation` / `TreeNavigation`) bleibt eigene Komponente | Routing-Affordance, kein CRUD-Tree | +| D6 | 2026-05-01 | Persistenz | Recovery via Modell A: `FileFolder` + `FileItem.folderId` reaktivieren (Pydantic, Routen, FE) | Mandanten-geteilte Ordnerstruktur; DB-Schema unveraendert | +| D7 | 2026-05-01 | Persistenz | Keine Schema-Aenderung in dev/int/prod; `migrate_folders_to_groups.py` wird archiviert (audit trail) | Nur API/FE-Wiederverdrahtung | +| D8 | 2026-05-01 | Persistenz | Soft-Delete wird **nicht** eingefuehrt; Audit-Eintrag pro Mutation reicht | Bestehende Pattern-Konsistenz | +| D9 | 2026-05-01 | Persistenz | Owner-Wechsel von Folders/Files ist nicht im Scope (`sysCreatedBy` immutable) | Bestehender Kontrakt | +| D10 | 2026-05-01 | RBAC | Default-Scope `personal` fuer neue Folders/Files; Sharing nur via Owner-Scope-Patch | Konsistent zu Files-RBAC heute | +| D11 | 2026-05-01 | RBAC | Geteilte Folders/Files sind fuer Nicht-Owner read-only (Backend 403, UI keine Affordance) | Owner-only-Kontrakt analog `updateFile` / `deleteFile` | +| D12 | 2026-05-01 | RBAC | Scope-Auflader: `featureInstance`-Sichtbarkeit gilt nur, wenn der User Zugriff auf die Feature-Instanz hat (bestehende `buildFilesScopeWhereClause`-Logik unveraendert) | Konsistenz zu heutigem Files-Pfad | +| D13 | 2026-05-01 | RBAC | `scope=global` ist im Eigenen-Tree des Owners (i.d.R. SysAdmin) sichtbar mit eigenem Indikator; bei allen anderen Usern im „Geteilt mit mir“-Tree | Symmetrische Owner-Sicht | +| D14 | 2026-05-01 | UI-Modell | Zwei getrennte Trees pro Files-Sicht: „Eigene“ (CRUD) + „Geteilt mit mir“ (read-only) | Saubere Datenstruktur, keine Vermischung | +| D15 | 2026-05-01 | UI-Modell | Render-Regel: File unter Folder, falls Folder sichtbar; sonst Root mit `contextOrphan`-Hint | Vermeidet Phantom-Hierarchien | +| D16 | 2026-05-01 | UI-Modell | „Geteilt mit mir“-Tree sortiert primaer nach Owner-Name, dann Folder-Hierarchie | Quelle des Sharings ist die wichtigste Information | +| D17 | 2026-05-01 | UI-Modell | „Geteilt mit mir“-Sektion kollabiert sich, wenn leer | UI-Hygiene | +| D18 | 2026-05-01 | UI-Modell | `FormGeneratorTable.groupingConfig`-Sicht auf Files bleibt flach (Eigene + Geteilte gemischt); kein zusaetzlicher Owner-Filter in diesem Plan | Status quo der Tabelle | +| D19 | 2026-05-01 | Sharing | Folder-Scope-Cascade auf enthaltene Files = opt-in (Dialog beim Patch) | Vermeidet versehentliche Massen-Sharings | +| D20 | 2026-05-01 | Sharing | Folder-Neutralize-Vererbung beim File-Drop = nein (analog Scope; Indikator zeigt Mismatch) | Owner-Entscheidung pro Item | +| D21 | 2026-05-01 | Sharing | Drag aus „Geteilt mit mir“ in eigenen Tree = gesperrt (kein Move, keine implizite Kopie) | Ownership-Wechsel ausserhalb Scope; Kopier-Workflow ggf. spaeterer Plan | +| D22 | 2026-05-01 | Stage 3 | `SourcesTab` und `ChatsTab` bekommen je **einen** Tree (kein Eigene/Geteilt-Split) | Connections und Chats sind heute personal-aequivalent; kein Sharing-Modell | + +--- + +## Ziel und Nicht-Ziele + +- **Ziel 1.** `FormGeneratorTree` als Komponente in + `frontend_nyla/src/components/FormGenerator/FormGeneratorTree/` mit + Provider-Interface, Built-in-Features (Multiselect, Cascade, DnD, + Inline-Edit, Scope-/Neutralize-Indikatoren) und Backend-Attribut-Resolution. +- **Ziel 2.** `FolderFileProvider` als erste Implementierung; recovered die + in `7c05cb0` entfallenen Funktionen auf Basis Modell A. +- **Ziel 3.** UDB-Tabs `FilesTab`, `SourcesTab`, `ChatsTab` rendern + `FormGeneratorTree`. `FilesTab` zeigt zwei Tree-Sektionen (Eigene + Geteilt). +- **Ziel 4.** `FilesPage.tsx` Split-View: links zwei `FormGeneratorTree`- + Sektionen, rechts `FormGeneratorTable` mit View-Toggle „Tree-Sicht | + Liste-mit-Gruppen-Sicht“. +- **Nicht-Ziel.** `FormGeneratorTable.groupingConfig` rebauen oder entfernen. +- **Nicht-Ziel.** `FormGeneratorReport` anfassen. +- **Nicht-Ziel.** Sidebar (`MandateNavigation` / `TreeNavigation`) migrieren. +- **Nicht-Ziel.** Owner-Wechsel oder Soft-Delete fuer Folders/Files einfuehren. + +--- + +## Betroffene Module + +### Frontend Nyla + +- **Neu:** `components/FormGenerator/FormGeneratorTree/` + (`FormGeneratorTree.tsx`, `FormGeneratorTree.module.css`, `types.ts`, + `__tests__/`) +- **Neu:** `providers/FolderFileProvider.tsx` +- **Recovery:** `components/UnifiedDataBar/FilesTab.tsx` + (Tree statt Table-mit-Grouping; Eigene + Geteilt) +- **Recovery:** `pages/basedata/FilesPage.tsx` (Split-View Tree+Table) +- **Recovery:** `contexts/FileContext.tsx`, `hooks/useFiles.ts` + (Folder-Operationen zurueck) +- **Recovery:** `api/fileApi.ts` (`folderId`, Folder-API-Wrapper) +- **Anpassen:** `pages/views/workspace/WorkspaceInput.tsx` -- + `TreeItemDrop.type` auf `'file' | 'folder'` zuruecksetzen, neuer + Standard-MIME `application/x-poweron-tree-items` +- **Stage 3:** `components/UnifiedDataBar/SourcesTab.tsx` und `ChatsTab.tsx` + auf `FormGeneratorTree` (je ein Tree, kein Sharing-Split) +- **Stage 4 (optional):** `SharepointBrowseTree`, `DataPicker`, + `InstanceHierarchyView`, `AccessRulesEditor` + +### Gateway + +- **Recovery:** `datamodels/datamodelFiles.py` -- neues `FileFolder` Pydantic- + Modell + `FileItem.folderId: Optional[str]` mit `fk_target` auf + `FileFolder.id` +- **Recovery:** `interfaces/interfaceDbApp.py` / + `interfaces/interfaceDbManagement.py` -- `getOwnFolderTree()`, + `getSharedFolderTree()`, `createFolder` (Default-Scope `personal`), + `renameFolder`, `moveFolder`, `deleteFolderCascade`, `patchFolderScope`, + `patchFolderNeutralize`. Mutationen strikt owner-only (oder `ALL`). +- **Recovery:** `routes/routeDataFiles.py` -- Folder-CRUD-Routen + (`POST /folders`, `GET /folders/tree?owner=me|shared`, + `PATCH /folders/{id}`, `POST /folders/{id}/move`, + `DELETE /folders/{id}?cascade=bool`, `PATCH /folders/{id}/scope`, + `PATCH /folders/{id}/neutralize`) +- **Behalten:** `routeDataFiles.py` Group-Endpunkte + (`/groups/{groupId}/scope`, `/neutralize`, `/download`, `delete_group`) + fuer `FormGeneratorTable.groupingConfig`-Use-Cases +- **Archivieren:** `migrations/migrate_folders_to_groups.py` -> + `migrations/_archive/` mit kurzem README (audit trail) +- **Unangetastet:** `serviceCenter/.../coreTools/_workspaceTools.py` + und `mainServiceChat.py` (TableGrouping-Pfad bleibt). Bei Bedarf spaeter + `_listFolders` / `_listFilesInFolder` als zusaetzliche Agent-Tools. + +### DB + +Keine Schema-Aenderung. Optionaler Schema-Sanity-Check in Stage 0. + +--- + +## Umsetzungs-Checkliste (Packets pro Modell) + +Jedes Packet ist ein abgeschlossener Auftrag fuer **eine** LLM-Session. +Header pro Packet: **Modell** | **Eingang** (Voraussetzung) | **Lieferumfang** +| **Checkliste** | **Abnahme**. + +Modelle: +- **Composer 2 Fast** -- mechanische Wiederherstellung, Pattern-Wiederholung, + Boilerplate, Doku gegen vorhandenen Code. +- **Opus 4.6** -- Architektur, RBAC-Korrektheit, Greenfield-API, + nicht-triviale Refactors. Bei Unsicherheit hochstufen. + +### Reihenfolge / Block-Strategie + +``` +Stage 0 (optional) + | + v +Stage 1.A (BE Boilerplate) ─┐ +Stage 1.B (BE RBAC-Kern) ├─ Block 1 (Backend in beliebiger Reihenfolge, +Stage 1.C (FE Greenfield) ─┘ FE kann parallel zu BE) + | + v +Stage 2.A (FE Recovery-Boilerplate) + | + v +Stage 2.B (FE UI-Komposition) + | + v +Stage 2.C (manuelle Verifikation, User) <-- DONE (2026-05-03) + | + v +Stage 3.A (Provider-Refactors) <-- DEFERRED +Stage 3.B (Tab-Umbau + Cleanup) <-- DEFERRED + | + v +Stage 4 (Tree-Assessment + Dead-Code-Cleanup) <-- DONE (2026-05-03) + | + v +Stage 5 (Doku & Cleanup) <-- DONE (2026-05-03) +``` + +Empfehlung Merge-Bloecke: **Stage 0+1+2 = PR 1** (Recovery -- abgeschlossen). +Stage 3 deferred. **Stage 4+5 = PR 2** (Cleanup + Doku -- abgeschlossen). + +--- + +### Stage 0 -- Schema-Sanity-Check (optional, < 1 h) -- DONE + +| | | +|---|---| +| **Modell** | Composer 2 Fast | +| **Eingang** | DB-Zugriff dev/int/prod | +| **Lieferumfang** | Bestaetigte Schema-Praesenz, kein Code-Output | +| **Abnahme** | Schriftliche Bestaetigung: `FileFolder` + `FileItem.folderId` in allen drei Umgebungen vorhanden; ggf. Zeilenanzahl | + +**Checkliste** + +- [x] `information_schema`-Query: `FileFolder`-Tabelle vorhanden in + **dev** (`poweron_management`; int/prod separat durch Team) +- [x] `information_schema`-Query: `FileItem.folderId`-Spalte vorhanden + (**dev**; FK-Verifikation: Spalte + Ziel `FileFolder` lt. Schema) +- [ ] Optional: `SELECT COUNT(*) FROM "FileFolder"` und Stichprobe pro Mandant + auf Datenkonsistenz + +**Hinweis (dev):** `python -m scripts.stage0_filefolder_schema_check` im Ordner `gateway`. + +> Falls eine Umgebung abweicht: Plan zurueckziehen, Restore-/Migration- +> Subtask einplanen, **bevor** Stage 1 startet. + +--- + +### Stage 1.A -- Backend Boilerplate (Modell A reaktivieren) -- DONE + +| | | +|---|---| +| **Modell** | Composer 2 Fast | +| **Eingang** | Stage 0 OK; pre-`7c05cb0`-Stand von `datamodelFiles.py` und `routeDataFiles.py` per Git als Referenz | +| **Lieferumfang** | Pydantic-Modelle, Routen-Skelett, Migrations-Skript archiviert | +| **Abnahme** | App startet; `GET /folders/tree?owner=me` liefert leere Liste fuer leeren Mandanten ohne Fehler | + +**Checkliste** + +- [x] `datamodels/datamodelFiles.py`: `FileFolder` Pydantic-Modell + (id, name, parentId, mandateId, featureInstanceId, scope, neutralize, sysCreatedAt/By) + mit `frontend_options` analog `FileItem.scope`; `i18nModel("Ordner")` +- [x] `datamodels/datamodelFiles.py`: `FileItem.folderId: Optional[str]` + mit `fk_target` auf `FileFolder.id` +- [x] `routes/routeDataFiles.py`: Routen-Skelett + (`POST /folders`, `GET /folders/tree?owner=me|shared`, + `PATCH /folders/{id}`, `POST /folders/{id}/move`, + `DELETE /folders/{id}`, `PATCH /folders/{id}/scope`, + `PATCH /folders/{id}/neutralize`) — GET stub leere Liste; Mutations 501 bis Stage 1.B +- [x] `migrations/migrate_folders_to_groups.py` -> + `migrations/_archive/migrate_folders_to_groups.py` + README mit Verweis + auf diesen Plan + +--- + +### Stage 1.B -- Backend RBAC-Kern + Audit-Tests -- DONE + +| | | +|---|---| +| **Modell** | **Opus 4.6** | +| **Eingang** | Stage 1.A OK; `interfaceRbac.buildFilesScopeWhereClause` und `interfaceDbManagement.updateFile` als RBAC-Vorlage | +| **Lieferumfang** | `interfaceDbManagement`/`interfaceDbApp` Folder-Methoden mit Owner-only-Guards; vollstaendige RBAC-Tests; Verifikation des bestehenden Files-Pfads | +| **Abnahme** | `test_folderRbac.py` und `test_folder_crud.py` gruen; manuelle Stichprobe Zwei-User: Owner B kann auf Folder von User A keine Mutationsroute aufrufen (alle 403); `contextOrphan`-Render in der Tree-API korrekt | + +**Checkliste** + +- [x] `interfaces/interfaceDbApp.py` / `interfaceDbManagement.py`: + `getOwnFolderTree()`, `getSharedFolderTree()` mit Scope-Where-Klausel + analog `buildFilesScopeWhereClause` +- [x] `createFolder` (Default-Scope `personal`) +- [x] `renameFolder`, `moveFolder`, `deleteFolderCascade`, + `patchFolderScope`, `patchFolderNeutralize` -- jede Methode mit + explizitem `sysCreatedBy == currentUser`-Guard (oder `ALL`); sonst + `PermissionError` -> 403 +- [x] `patchFolderScope`: Cascade-auf-Files als optionaler Parameter + (D19); ohne expliziten Opt-in keine File-Aenderung +- [x] **Verifikation bestehender Files-RBAC** (kein Code-Aenderung, nur + Tests): `updateFile` / `deleteFile` / `deleteFilesBatch` werfen + fuer Nicht-Owner mit Scope-Sichtbarkeit weiterhin + `PermissionError` +- [x] `tests/unit/routes/test_folder_crud.py`: Create / Move / + Cascade-Delete / Scope-Patch / Neutralize-Patch (Happy Paths + + Cross-Tenant) +- [x] `tests/unit/interfaces/test_folderRbac.py`: Zwei-User-Matrix, + jede Mutationsroute fuer Nicht-Owner = 403; `getSharedFolderTree` + respektiert Scope; `contextOrphan`-Fall (File geteilt, Folder nicht) + +--- + +### Stage 1.C -- Frontend `FormGeneratorTree` Greenfield -- DONE + +| | | +|---|---| +| **Modell** | **Opus 4.6** | +| **Eingang** | Architektur-Abschnitt (Provider-Interface) + RBAC-Modell aus diesem Plan; bestehende `FolderTree.tsx` als UX-Referenz; `FormGeneratorTable` als Bulk-Action-Pattern-Referenz | +| **Lieferumfang** | Generische `FormGeneratorTree`-Komponente + `FolderFileProvider` + Vitest+RTL-Tests; **kann parallel zu Stage 1.A/1.B laufen**, da Provider gegen Mocks getestet wird | +| **Abnahme** | Vitest gruen inkl. Edge-Cases (mixed selection, partial-selected, cascade, contextOrphan); Storybook-Story (oder Smoke-Page) zeigt beide Modi `own`/`shared` mit Mock-Provider | + +**Checkliste** + +- [x] Ordner `components/FormGenerator/FormGeneratorTree/` mit + `FormGeneratorTree.tsx`, `FormGeneratorTree.module.css`, `types.ts`, + `providers/FolderFileProvider.tsx`, `__tests__/` +- [x] `TreeNodeProvider`-Interface (siehe Architektur-Abschnitt) in + `types.ts` +- [x] Built-in Features: + - Expand/Collapse mit Tastatur-Navigation + - Multiselect mit Shift/Ctrl/Cmd; getrennt zwischen + `ownership='own'` und `'shared'`; gemischte Selektion blockiert + Bulk-Mutationen + - Cascading-Selection (Folder selektieren -> alle eigenen Children), + Partial-Selected-Indikator + - DnD `application/x-poweron-tree-items`; aus `shared` nur lesende + Drops, kein Move + - Inline-Rename (Doppelklick / F2) -- nur fuer `ownership='own'` + - Scope/Neutralize interaktiv nur fuer Eigene; im geteilten Tree + Indikator-Icons + - `contextOrphan`-Render-Regel (Knoten am Tree-Root mit Hint-Icon) + - Bulk-Action-Toolbar analog `FormGeneratorTable.batchActions`; im + geteilten Tree maximal Lesen/Download +- [x] `FolderFileProvider.tsx`: Referenzimplementierung gegen die in + Stage 1.B definierten Folder-API-Routen (kann initial gegen + Mock-Server laufen) +- [x] Vitest+RTL-Tests: Lazy-Load, Multiselect-Cascade, Mixed-Selection- + Block, DnD-Payload, Inline-Rename, Bulk-Delete, `contextOrphan`- + Render -- alle gegen Provider-Mock + +--- + +### Stage 2.A -- FE Recovery-Boilerplate -- DONE + +| | | +|---|---| +| **Modell** | Composer 2 Fast | +| **Eingang** | Stage 1.B + 1.C OK; pre-`7c05cb0`-Stand von `FileContext.tsx`, `useFiles.ts`, `fileApi.ts` per Git | +| **Lieferumfang** | Folder-Operationen reaktiviert in Context/Hook/API-Wrapper | +| **Abnahme** | TypeScript clean; bestehende Files-Tests gruen; FilesPage rendert weiterhin (alter Code-Pfad noch aktiv) | + +**Checkliste** + +- [x] `contexts/FileContext.tsx`: `FolderInfo`-Typ + `handleCreateFolder`, + `handleRenameFolder`, `handleDeleteFolderCascade`, `handleMoveFolder`, + `handleMoveFiles`, `handleDownloadFolder`, `refreshFolders`, + `treeFileNodes`, `expandedFolderIds` -- gegen die neuen Routen +- [x] `hooks/useFiles.ts`: Folder-Operationen wieder einbauen +- [x] `api/fileApi.ts`: `FileInfo.folderId`, `getFolderTree`, `createFolder`, + `renameFolder`, `moveFolder`, `deleteFolderCascade`, `moveFiles` als + API-Wrapper +- [x] `pages/views/workspace/WorkspaceInput.tsx`: `TreeItemDrop.type` auf + `'file' | 'folder'` zuruecksetzen + +--- + +### Stage 2.B -- FE UI-Komposition (FilesTab + FilesPage) -- DONE + +| | | +|---|---| +| **Modell** | **Opus 4.6** | +| **Eingang** | Stage 2.A OK | +| **Lieferumfang** | `FilesTab` mit zwei Tree-Sektionen; `FilesPage` Split-View mit Toggle Tree/Liste-mit-Gruppen-Sicht | +| **Abnahme** | Storybook/Smoke der UDB zeigt beide Sektionen, leerer Geteilt-Tree kollabiert; FilesPage-Toggle wechselt korrekt | + +**Checkliste** + +- [x] `components/UnifiedDataBar/FilesTab.tsx`: zwei `FormGeneratorTree`- + Sektionen via `FolderFileProvider` + - **Eigene** mit Toolbar („Neuer Ordner“, „Multidelete“, + „Multimove“, „Download als ZIP“) + - **Geteilt mit mir** read-only, kollabiert wenn leer, Owner-Name + als oberste Sortier-Achse (D16/D17) + - `compact={true}`; `onSendToChat` auf `'file' | 'folder'` +- [x] `pages/basedata/FilesPage.tsx`: Split-View + - links zwei `FormGeneratorTree`-Sektionen (Eigene / Geteilt) mit + klar getrennten Headern + - rechts `FormGeneratorTable` mit Toggle „Tree-Sicht | + Liste-mit-Gruppen-Sicht“; `groupingConfig` bleibt als alternative + Sicht + - Selektion im geteilten Tree -> Tabellenzeilen read-only + +--- + +### Stage 2.C -- Manuelle Verifikation (User) -- DONE + +| | | +|---|---| +| **Modell** | -- (User) | +| **Eingang** | Stage 2.B deployed in dev; Test-Mandant mit zwei Usern verfuegbar | +| **Lieferumfang** | Bestaetigung der Akzeptanzkriterien 1-8b + 10 | +| **Abnahme** | Alle Punkte unten haken; sonst Defekt-Issue oeffnen, zustaendige Stage erneut | + +**Checkliste** + +- [x] Neuer Folder anlegen -> Default `personal`, nur eigener Tree zeigt ihn +- [x] Scope/Neutralize: Eigene interaktiv, Geteilt nur Indikator + *(Fix 2026-05-02: FolderFileProvider typeMap fuer Folder- vs File-Endpoints)* +- [x] Multiselect Folder + Files (eigen) cascadiert; gemischt Eigene/Geteilt + blockiert Bulk-Mutationen mit Hinweis +- [x] Multidelete cascadiert nur bei reinem Owner-Set; Confirmation + *(Fix 2026-05-02: TreeBatchAction.typeFilter, separate Ordner/Dateien-Buttons, window.confirm)* +- [x] DnD: File von Folder A nach Folder B (eigen) funktioniert; Drag aus + „Geteilt mit mir“ in eigenen Folder = kein Move +- [x] DnD: File aus FilesTab in Workspace-Chat-Input dropped korrekt + (Standard-MIME erkannt) -- aus beiden Trees lesend zulaessig +- [ ] Cross-User-Sharing: User A setzt Folder-Scope auf `mandate` -> + User B sieht den Folder im „Geteilt mit mir“ (read-only, 403 auf + Mutationen via DevTools-Probe) + *(nicht getestet -- erfordert zwei User im selben Mandant)* +- [x] FilesPage-Toggle: umgebaut zu „Ordner-Sicht“ (Tabelle gefiltert nach + selektiertem Ordner) und „Alle Dateien“ (ohne Folder-Filterung). + Gruppierung entfernt. + +**Zusaetzliche Fixes aus Stage 2.C Verifikation (2026-05-02/03):** + +- FolderFileProvider: 405-Fix (falscher API-Endpoint `/api/files` -> `/api/files/list`) +- FolderFileProvider: typeMap eingefuehrt -- alle Operationen (rename, delete, + move, patchScope, patchNeutralize) verwenden korrekte Endpoints fuer Folder vs File +- FormGeneratorTree: Refresh-Icon im Section-Header (`FaSyncAlt`) +- FormGeneratorTree: Tree-Reload nach Batch-Actions und inline Delete +- FormGeneratorTree: `window.confirm()` vor Delete (inline + batch) +- FilesPage: Tree-Table-Sync (selectedFolderId filtert Tabelle, File-Klick + highlighted Tabellenzeile) + +--- + +### Stage 3.A -- Provider-Refactors (Sources + Feature-Daten) -- DEFERRED + +| | | +|---|---| +| **Status** | **DEFERRED (2026-05-03)** | +| **Modell** | **Opus 4.6** | +| **Eingang** | Stage 1.C OK (FormGeneratorTree stabil) | +| **Lieferumfang** | `ConnectorBrowseProvider` und `FeatureDataProvider` mit Aequivalenz-Garantie zur bisherigen Logik | +| **Abnahme** | Vitest+RTL-Tests pro Provider gruen; Smoke-Test: alle bisherigen Knoten-Ebenen sind reproduzierbar | + +**Grund fuer Deferral:** `SourcesTab` und `ChatsTab` enthalten hochgradig +domaen-spezifische Tree-Logik (Multi-Level lazy-load ueber Connections, +Services, Feature-Daten, Mandate-Gruppierungen, Chat-Filterung), die sich +nicht sinnvoll auf die generische `TreeNodeProvider`-Abstraktion abbilden +laesst, ohne die Provider-Interfaces erheblich zu erweitern. Der Aufwand +uebersteigt den Nutzen gegenueber dem bestehenden, funktionierenden Code. +Kann bei Bedarf spaeter als eigenes Projekt aufgegriffen werden. + +~~**Checkliste**~~ + +- ~~`providers/ConnectorBrowseProvider.tsx`~~ +- ~~`providers/FeatureDataProvider.tsx`~~ +- ~~Pro Provider Vitest+RTL-Tests~~ + +--- + +### Stage 3.B -- Tab-Umbau + Cleanup (Feature-Flag) -- DEFERRED + +| | | +|---|---| +| **Status** | **DEFERRED (2026-05-03)** | +| **Modell** | Composer 2 Fast | +| **Eingang** | Stage 3.A OK | +| **Lieferumfang** | `SourcesTab.tsx` und `ChatsTab.tsx` auf `FormGeneratorTree`; alter Custom-Tree-Code entfernt; Feature-Flag fuer Rollback | +| **Abnahme** | Mit Flag aus = Status quo; Flag ein = neue Tree-UI; manuelle Verifikation der UDB-Tabs OK | + +**Grund fuer Deferral:** Abhaengig von Stage 3.A (s.o.). + +~~**Checkliste**~~ + +- ~~`providers/ChatProvider.tsx`~~ +- ~~`SourcesTab.tsx` Umbau~~ +- ~~`ChatsTab.tsx` Umbau~~ +- ~~Feature-Flag~~ +- ~~Manuelle Verifikation~~ + +--- + +### Stage 4 -- Restliche Tree-Migrationen (Assessment + Cleanup) -- DONE + +| | | +|---|---| +| **Status** | **DONE (2026-05-03)** | +| **Modell** | Composer 2 Fast | +| **Ergebnis** | Assessment aller Kandidaten; Dead-Code geloescht; keine weiteren Migrationen sinnvoll | + +**Assessment-Ergebnis** + +- [x] `FolderTree.tsx` + `SharepointBrowseTree.tsx` + gesamter `components/FolderTree/`-Ordner: + **GELOESCHT** (Dead Code, keine externen Imports). ESLint-Deprecation-Regel entfernt. +- [x] `DataPicker.tsx`: **SKIP** -- Schema-basierter Automation2-Picker (read-only, + in-memory, kein Entity-Tree; TreeNodeProvider-Abstraktion passt nicht) +- [x] `InstanceHierarchyView.tsx`: **SKIP** -- Read-only Admin-Visualisierung + (synchrone Props, kein CRUD/DnD/Multiselect; 300 Zeilen, stabil) +- [x] `AccessRulesEditor.tsx`: **SKIP** -- Kein Tree, sondern tabellarischer + RBAC-Rule-Editor mit Checkbox-Matrix (774 Zeilen; komplett anderes Paradigma) +- [x] `TreeNavigation.tsx`: **SKIP** -- Sidebar-Navigation mit React Router + (NavLink, aktiver Pfad); UI-Navigation, nicht Daten-Tree +- [x] `redmineTreeLogic.ts`: **SKIP** -- Reiner BFS-Algorithmus fuer + Redmine-Tickets; kein UI-Komponente + +--- + +### Stage 5 -- Doku & Cleanup -- DONE + +| | | +|---|---| +| **Modell** | Composer 2 Fast | +| **Eingang** | Stage 2 mindestens gemerged; spaetestens nach Stage 4 | +| **Lieferumfang** | Wiki-Update der kanonischen Stellen + neuer Tree-Topic | +| **Abnahme** | `lastReviewed` aktualisiert; `wiki/c-work/_CHANGELOG.md` pro Stage eine Zeile | + +**Checkliste** + +- [x] `wiki/b-reference/frontend-nyla/architecture.md`: Komponenten-Tabelle + um `FormGeneratorTree` ergaenzt, `FolderTree`-Eintrag ersetzt, + `components/`-Ordner-Beschreibung aktualisiert +- [x] `wiki/b-reference/frontend-nyla/formgenerator.md`: Abschnitt + `FormGeneratorTree` (Provider-Interface, Built-in Features, + Vergleichstabelle Tree vs. Table-with-grouping, Props, Verwendung) +- [x] `wiki/TOPICS.md`: Neuer Eintrag FormGenerator (Table, Form, Tree, Report) +- [x] `wiki/c-work/_CHANGELOG.md`: Stage 4+5 Eintrag +- [x] Plan-Dokument verschoben nach `4-done/` + +--- + +### Eskalations-Regel zwischen Modellen + +- Composer-Packet stoesst auf Entscheidung, die nicht in **D1-D22** steht + -> stoppen, Plan ergaenzen (Opus), dann zurueck. +- Composer-Test schlaegt fehl und Fix ist nicht mechanisch + -> Diagnose-Loop in Opus, Fix-Patch zurueck zu Composer. +- Pre-Commit-Hook-Modifikationen (lint/format) bleiben in Composer. + +--- + +## Akzeptanzkriterien + +| # | Kriterium (Given-When-Then) | Prio | +|---|---------------------------|------| +| 1 | **Given** ein Mandant mit eigenen Folders + Files, **When** der User die Files-Page oeffnet, **Then** die linke Spalte zeigt zwei Sektionen („Eigene“, „Geteilt mit mir“) mit rekursiven Folder-Trees | must | +| 2 | **Given** der User klickt im Eigenen-Tree auf „Neuer Ordner“, **When** er einen Namen bestaetigt, **Then** der Folder erscheint sofort, ist persistiert mit Default-Scope `personal` und nur fuer den Ersteller sichtbar | must | +| 3 | **Given** ein eigener Folder mit 5 Files, **When** der User per Multiselect den Folder + 2 weitere Files anwaehlt und „Loeschen“ klickt, **Then** Confirmation `Wirklich 1 Folder (mit 5 Files) und 2 weitere Files loeschen?`; nach OK sind alle 8 Items weg | must | +| 4 | **Given** ein eigenes File mit `scope=personal`, **When** der User auf das Scope-Icon klickt, **Then** das Icon cyclet `personal -> featureInstance -> mandate -> global -> personal`; jeder Click persistiert via PATCH | must | +| 5 | **Given** ein eigener Folder, **When** der Owner den Scope auf `mandate` patcht, **Then** Dialog fragt „Files mit teilen?“ (Default: nein); bei Ja erben enthaltene Files den Scope, sonst bleibt deren Scope unveraendert | must | +| 6 | **Given** der User draggt ein File aus dem FilesTab, **When** im Workspace-Chat-Input gedropped, **Then** Standard-MIME `application/x-poweron-tree-items` wird empfangen; aus beiden Trees lesend zulaessig | must | +| 7 | **Given** UDB ist offen, **When** der User durch Files / Sources / Chats blaettert, **Then** alle drei Tabs zeigen `FormGeneratorTree` mit konsistentem Look-and-Feel | should | +| 8 | **Given** zwei Mandanten-Mitglieder (User A, User B), **When** User A einen Folder anlegt (Default `personal`), **Then** User B sieht den Folder **nicht**. Erst nach `mandate`-Scope-Patch erscheint er bei User B im „Geteilt mit mir“ (read-only, Indikator-Icons, keine Mutations-Affordance) | must | +| 8a | **Given** User A teilt eine Datei (`scope=mandate`), aber den enthaltenden Folder nicht, **When** User B die Files-Page oeffnet, **Then** die Datei erscheint im Root des „Geteilt mit mir“-Trees mit `contextOrphan`-Hint; aendert User A den Folder-Scope, wandert die Datei automatisch unter den Folder | should | +| 8b | **Given** User B steht im „Geteilt mit mir“ auf einem Folder von User A, **When** er Mutationsroute (rename / move / delete / scope-cycle / neutralize-toggle) aufruft, **Then** Backend antwortet 403 und UI hat keine Affordance | must | +| 9 | **Given** `FormGeneratorTable.groupingConfig` aktiv auf `AutomationsDashboardPage`, **When** der User die Page laedt, **Then** Workflow-Runs sind weiterhin nach Mandant + Status gruppiert | must | +| 10 | **Given** `FilesPage` View-Toggle steht auf „Liste-mit-Gruppen-Sicht“, **When** der User auf „Tree-Sicht“ und zurueck wechselt, **Then** beide Sichten zeigen dieselben Files; `TableGrouping` und `FileFolder` sind orthogonal sichtbar | should | +| 11 | **Given** Drag-Source aus `SourcesTab`, **When** auf eine Workflow-Node gedropped, **Then** Empfaenger sieht denselben `application/x-poweron-tree-items`-MIME wie aus FilesTab | should | + +--- + +## Testplan + +| ID | AC | Art | Automatisiert | Repo-Pfad | Status | +|----|----|-----|--------------|-----------|--------| +| T1 | 1, 2 | api | ja | `gateway/tests/unit/routes/test_folder_crud.py` | pending | +| T2 | 3 | api | ja | `gateway/tests/unit/routes/test_folder_cascade_delete.py` | pending | +| T3 | 4, 5 | api | ja | `gateway/tests/unit/routes/test_folder_scope_neutralize.py` (inkl. Cascade-Opt-in) | pending | +| T4 | 8, 8a, 8b | api | ja | `gateway/tests/unit/interfaces/test_folderRbac.py` (zwei User; alle Mutationsrouten 403; Render-Regel inkl. `contextOrphan`) | pending | +| T5 | 1-6 | ui | ja | `frontend_nyla/src/components/FormGenerator/FormGeneratorTree/__tests__/FormGeneratorTree.test.tsx` | pending | +| T6 | 6, 11 | ui | ja | `frontend_nyla/src/components/FormGenerator/FormGeneratorTree/__tests__/dnd.test.tsx` | pending | +| T7 | 1-7, 10 | manuell | nein | -- | pending | +| T8 | 9 | manuell | nein | -- | pending | +| T9 | 7 | manuell | nein | -- | pending | + +--- + +## Risiken + +| Risiko | Wahrscheinlichkeit | Impact | Mitigation | +|--------|-------------------|--------|------------| +| `FileFolder` / `folderId` in einer Umgebung doch entfernt (Abweichung vom Repo-Stand) | sehr niedrig | hoch | Stage 0 Spot-Check; ggf. Restore aus Backup oder Ableitung aus `TableGrouping.meta.migratedFromFolderId` (One-Shot) | +| Read-only-Kontrakt fuer Folders nicht durchgaengig (UI versteckt, Backend offen) | mittel | hoch | Pro Mutationsroute expliziter `sysCreatedBy == currentUser`-Guard; Zwei-User-Tests pro Route in Stage 1 | +| `contextOrphan`-Render leakt Pfad-/Owner-Information mehr als beabsichtigt | niedrig | mittel | Im geteilten Tree nur Owner-Name + File-Name; keine Folder-Pfade aus dem fremden Tree | +| `_workspaceTools.py` (writeFile -> Group-Auto-Add) bricht nach Recovery | mittel | mittel | Code unangetastet; TableGrouping-Pfad bleibt; User kann Files spaeter manuell in Folder verschieben | +| TableGrouping-Eintrag und FileFolder-Eintrag widersprechen sich (selber File in Group X UND Folder Y) | hoch | niedrig | By design orthogonal; UI zeigt beide Sichten ueber Toggle; keine Konsistenz-Erzwingung | +| Stage 3 enthuellt subtile Abhaengigkeiten in SourcesTab/ChatsTab | hoch | mittel | **Eingetreten:** Stage 3 deferred -- domaen-spezifische Logik passt nicht auf generische TreeNodeProvider-Abstraktion | +| Cascade-Selection-Logik im FormGeneratorTree subtil falsch (z.B. partial-selected) | mittel | niedrig | Umfangreiche Vitest+RTL-Tests in Stage 1 mit Provider-Mock | + +--- + +## Links + +- PR: tba (Stage 0+1+2 -- bereit), Stage 3 deferred, Stage 4+5 abgeschlossen +- Issue: -- +- Vorgaenger-Commits: `c8e9304`, `e7a79a3`, `7c05cb0` +- Migrations-Skript (archiviert): `gateway/modules/migrations/_archive/migrate_folders_to_groups.py` +- Heutige `FolderTree`-Komponente (vollstaendig, nicht mehr eingebunden): + `frontend_nyla/src/components/FolderTree/FolderTree.tsx` + +--- + +## Abschluss + +- [x] `b-reference/frontend-nyla/architecture.md` aktualisiert + (Komponenten-Tabelle, UDB-Beschreibung) +- [x] `b-reference/frontend-nyla/formgenerator.md` aktualisiert + (Abschnitt FormGeneratorTree) +- [x] `TOPICS.md` aktualisiert +- [x] Dokument verschoben nach `4-done/` diff --git a/c-work/_CHANGELOG.md b/c-work/_CHANGELOG.md index a2372f4..cbd0958 100644 --- a/c-work/_CHANGELOG.md +++ b/c-work/_CHANGELOG.md @@ -12,6 +12,31 @@ type: `feat` `fix` `refactor` `docs` `test` `chore` `build` · scope: `gateway Skip: reine Refactors, Formatting, Lint, Dep-Bumps, Test-only, Wiki-Tippfehler. +## 2026-05-03 + +- 2026-05-03 | fix | frontend-nyla | **Automation > Workspace Tab: Polling fuer laufende Runs.** `_WorkspaceTab` pollt `fetchWorkspaceRunDetail` alle 3s solange Run-Status nicht terminal. Stoppt automatisch bei completed/failed/cancelled. +- 2026-05-03 | fix | gateway | **executeCode Sandbox: `type` entblockt, `io` restricted, Modul-Liste dynamisch.** `type()` aus `_PYTHON_BLOCKED_BUILTINS` entfernt. `import io` liefert eingeschraenktes Modul (nur StringIO/BytesIO, kein io.open). Tool-Beschreibung dynamisch aus `SANDBOX_ALLOWED_MODULES` generiert. `type()` war faelschlich in `_PYTHON_BLOCKED_BUILTINS` (harmlos ohne exec/eval). `StringIO`/`BytesIO` als sichere Builtins (io.open wird nicht exponiert). Tool-Beschreibung aktualisiert. +- 2026-05-03 | fix | gateway | **AI Agent: docItem-Resolution Root Cause + executeCode readFile.** (1) `coerceDocumentReferenceList`: Dict-Pfad strippt jetzt `docItem:`/`docList:` Prefix korrekt via `from_string_list` (war: Prefix blieb in `documentId`, erzeugte Doppel-Prefix bei `to_string()`). (2) `process.py`: `DocumentItemReference` wird nur im Automation2-Kontext (kein Chat-Workflow) an `_resolve_file_refs_to_content_parts` geroutet; im Agent-Kontext fliessen sie korrekt durch `getChatDocumentsFromDocumentList` (war: alle Refs wurden abgefangen und fehlerhaft als File-Store-IDs behandelt). Gebrochenen `_resolveChatDocIdToFileId`-Fallback entfernt. (3) executeCode Sandbox: `readFile(fileId)` Built-in fuer Workspace-Dateizugriff (kein open(), nur managed Files via interfaceDbComponent). +- 2026-05-03 | fix | gateway | **AI Agent + Rendering: 9 Issues geloest.** (1) Sandbox `time`-Modul erlaubt. (2) ActionToolAdapter: grosse Docs als Workspace-Files persistiert. (3) downloadFromDataSource: Debug-Logging fuer Filename-Swap. (4) clickup_listTasks: Datums-/Custom-Field-Filter. (5) Renderer style-Bug: `style` Keyword auf allen 8 Renderern akzeptiert (CSV, Text, JSON, Markdown, Image, CodeXml, CodeJson, CodeCsv). (6) _ServicesAdapter: workflow-Setter fuer Agent-Kontext (getChatDocumentsFromDocumentList braucht workflow). (7) _ServicesAdapter: interfaceDbComponent Property (ai_process file-ref Resolution). (8) Auto-index nach Neutralize: mandateId/featureInstanceId an _autoIndexFile durchgereicht. (9) Kombination: Agent sollte AI-Workspace-Aufgaben effizient loesen. +- 2026-05-03 | chore | frontend-nyla | **Stage 4: FolderTree Dead-Code geloescht + Tree-Assessment.** Gesamter `components/FolderTree/`-Ordner entfernt (FolderTree.tsx 1170 LOC, SharepointBrowseTree.tsx 319 LOC, actions/, CSS -- alles Dead Code). ESLint-Deprecation-Regel entfernt. Assessment aller verbleibenden Tree-Kandidaten: DataPicker, InstanceHierarchyView, AccessRulesEditor, TreeNavigation, redmineTreeLogic -- alle SKIP (kein Entity-Tree-Paradigma). (c-work: `4-done/2026-05-formgenerator-tree-and-folder-recovery.md`) +- 2026-05-03 | docs | wiki | **Stage 5: FormGeneratorTree Wiki-Dokumentation.** `formgenerator.md`: neuer Abschnitt FormGeneratorTree (Provider-Interface, Features, Props, Verwendung, Tree-vs-Table-Vergleich). `architecture.md`: FolderTree-Referenzen durch FormGeneratorTree ersetzt. `TOPICS.md`: FormGenerator-Eintrag ergaenzt. Plan nach `4-done/` verschoben. (c-work: `4-done/2026-05-formgenerator-tree-and-folder-recovery.md`) +- 2026-05-03 | docs | wiki | **FormGeneratorTree Plan: Stage 3 deferred, Stage 0-2 abgeschlossen.** Stage 3.A/3.B (SourcesTab/ChatsTab-Refactor auf FormGeneratorTree) deferred -- domaen-spezifische Tree-Logik passt nicht sinnvoll auf generische TreeNodeProvider-Abstraktion. Plan-Status auf `done` gesetzt. Stage 2.C Checkliste aktualisiert mit allen 2.C-Fixes (405-Fix, typeMap, Refresh-Icon, confirm-Dialoge, FilesPage-Sync). (c-work: `4-done/2026-05-formgenerator-tree-and-folder-recovery.md`) + +## 2026-05-02 + +- 2026-05-02 | fix | frontend-nyla | **Tree batch-delete: separate Ordner/Dateien-Aktionen.** `TreeBatchAction.typeFilter` in `types.ts`; `FormGeneratorTree.tsx` filtert `selectedIds` nach Typ und zeigt Counts pro Button. `FolderFileProvider.tsx` liefert zwei getrennte Batch-Aktionen (Ordner-Cascade-Delete vs. File-Delete/Batch-Delete). **FilesPage tree-table Sync wiederhergestellt:** `selectedFolderId` filtert Tabelle nach Ordner; File-Klick im Tree scrollt+highlighted die Tabellenzeile (2.5s auto-clear). (c-work: `1-plan/2026-05-formgenerator-tree-and-folder-recovery.md`) + +## 2026-05-01 + +- 2026-05-01 | feat | frontend-nyla | **Stage 2.A + 2.B: FilesTab + FilesPage tree integration.** `FilesTab.tsx` rewritten from `FormGeneratorTable` to two `FormGeneratorTree` sections (Eigene/Geteilt mit mir) with drag-drop upload, refresh-by-key, and workflow-import on node click. `FilesPage.tsx` updated with split-view layout: 300px tree panel (left, own+shared trees) and `FormGeneratorTable` (right) with view-mode toggle (Tree-Sicht / Liste mit Gruppen); groupingConfig applied only in grouped mode; upload and refresh sync both tree and table. (c-work: `1-plan/2026-05-formgenerator-tree-and-folder-recovery.md`) +- 2026-05-01 | feat | gateway | **FormGeneratorTree Plan Stage 0 + 1.A:** Schema-Check-Skript `scripts/stage0_filefolder_schema_check.py`; `FileFolder` Pydantic + `FileItem.folderId` in `datamodelFiles.py`; Stub `getOwnFolderTree`/`getSharedFolderTree`; Routen `GET /api/files/folders/tree` (leere Liste) + Folder-Mutationen 501 bis Stage 1.B; `migrate_folders_to_groups.py` nach `modules/migrations/_archive/` mit README. (c-work: `1-plan/2026-05-formgenerator-tree-and-folder-recovery.md`) +- 2026-05-01 | docs | wiki | **Plan FormGeneratorTree: Stages packetisiert nach LLM.** Stages 1-3 in Sub-Packets aufgeteilt (1.A BE-Boilerplate / 1.B BE RBAC-Kern / 1.C FE Greenfield / 2.A FE-Boilerplate / 2.B FE-Komposition / 2.C manuelle Verifikation / 3.A Provider-Refactors / 3.B Tab-Umbau). Pro Packet: Modell-Hinweis (Opus 4.6 fuer Architektur/RBAC/Greenfield, Composer 2 Fast fuer Boilerplate/Recovery/Doku), Eingang/Lieferumfang/Abnahme. Eskalations-Regel ergaenzt. (c-work: `1-plan/2026-05-formgenerator-tree-and-folder-recovery.md`) +- 2026-05-01 | docs | wiki | **Plan FormGeneratorTree + Folder-Recovery konsolidiert.** Neuer FormGenerator-Familienteil `FormGeneratorTree` (Provider-Pattern, Cascade-Multiselect, DnD `application/x-poweron-tree-items`, Backend-Attribut-Resolution) zur Konsolidierung der ~8 parallelen Tree-Implementierungen. Recovery der Folder/File-Funktionen ueber **Modell A** (`FileFolder` + `FileItem.folderId` reaktivieren; DB-Schema unveraendert). RBAC-/Sichtbarkeitsmodell verbindlich: Default-Scope `personal`; Sharing nur ueber Owner-Scope-Patch; geteilte Objekte read-only fuer Nicht-Owner (analog `updateFile`/`deleteFile`); UI rendert in `FilesTab`/`FilesPage` **zwei getrennte Trees** (Eigene/CRUD + Geteilt mit mir/read-only); Files ohne sichtbaren Folder-Kontext fallen in den Root mit `contextOrphan`-Hint; Folder-Scope-Cascade auf Files = opt-in; Folder-Neutralize-Vererbung beim Drop = nein; Drag aus Geteilt in Eigenes = gesperrt. 22 konsolidierte Entscheidungen (D1-D22), keine offenen Backlog-Punkte mehr. UDB hat fix drei Tabs (Files, Sources, Chats) auf `FormGeneratorTree`. `FormGeneratorTable.groupingConfig` bleibt unveraendert. Kein `FormGeneratorChart` -- Berichte = **`FormGeneratorReport`** (Komponenten-Zeile in `b-reference/frontend-nyla/formgenerator.md`). 6 Stages (0 Schema-Sanity-Check optional, 1 Backend+Skeleton, 2 FilesTab/FilesPage Recovery, 3 SourcesTab/ChatsTab Konsolidierung, 4 optional weitere Trees, 5 Doku). (c-work: `1-plan/2026-05-formgenerator-tree-and-folder-recovery.md`) + +## 2026-05-01 + +- 2026-05-01 | docs | wiki | **Plan D (AI Reports Pipeline) abgeschlossen -> `4-done/`.** Alle Checklisten-Items auf `[x]`, Testplan-Status auf `done`, Header `status: done`, `completed: 2026-05-01`. Wiki-Referenz-Updates (`ai-agent.md`, `workflow.md`, `neutralization.md`) als TODO fuer naechste Review-Runde markiert. (c-work: `4-done/2026-04-ai-reports-theming-and-pipeline.md`) + ## 2026-04-30 - 2026-04-30 | refactor | gateway | **GraphEditor: Hidden DataRefs sichtbar gemacht fuer alle `ai.*` Nodes.** `frontendType: "hidden"` -> `"dataRef"` fuer `documentList` in `ai.prompt`, `ai.summarizeDocument`, `ai.translateDocument`, `ai.convertDocument` ([gateway/modules/features/graphicalEditor/nodeDefinitions/ai.py](../../gateway/modules/features/graphicalEditor/nodeDefinitions/ai.py)). Zwei neue sichtbare Parameter auf `ai.prompt`: `context` (`dataRef`, beliebiger Upstream-Output, wird vom Action zu JSON serialisiert) + `documentTheme` (`select`, finance/legal/...). Action-Definition `ai.process` ([gateway/modules/workflows/methods/methodAi/methodAi.py](../../gateway/modules/workflows/methods/methodAi/methodAi.py)) deklariert `context` und `documentTheme` jetzt explizit, statt sie als undeklarierte Pass-Through-Keys mitlaufen zu lassen. Effekt: Trustee-Templates (Budget-Vergleich, KPI, Cashflow, Forecast, Year-end) zeigen ihre bisher unsichtbaren DataRef-Bindings (`trigger.payload.documentList`, `refresh.data.accountingData`) jetzt im Properties-Panel als gruene Bindungs-Boxen, der User kann sie via DataPicker veraendern. Kein Frontend-Code-Change noetig: bestehender `DataRefRenderer` + `DataPicker` rendern die JSON-DataRef-Werte direkt. Runtime unveraendert -- `resolveParameterReferences` verarbeitet `{"type":"ref",...}` wie bisher. @@ -163,4 +188,5 @@ Skip: reine Refactors, Formatting, Lint, Dep-Bumps, Test-only, Wiki-Tippfehler. - 2026-04-26 | fix | gateway+frontend | Automation Workflow-Tab: `Automation2WorkflowView` erstellt damit sysCreatedAt (timestamp) und lastStartedAt korrekt als PeriodPicker-Spalten erkannt werden; lastStartedAt nutzt jetzt AutoRun.startedAt statt sysCreatedAt; computed-field Filter/Sort via applyFiltersAndSort in-memory; Runs-Tab auf startedAt/completedAt umgestellt statt System-Audit-Felder - 2026-04-26 | perf | gateway | routeWorkflowDashboard get_system_workflows: N+1 AutoRun-Abfragen ersetzt durch LEFT JOIN + Subquery-Aggregation (eine Daten- + eine Count-Query); FK-Sort-Pfad nutzt eine gebündelte Run-Stats-Query; lastStartedAt/runCount/isRunning-Filter im Join-Pfad in SQL - 2026-04-29 | test | gateway | Phase 7 AI-Reports: 19 unit tests for MD-to-JSON parser (inlineRuns), styleDefaults resolver, AiCallOptions allowedModels whitelist, inline-image paragraphs +- 2026-05-01 | feat | gateway | Stage 1.B FormGenerator Folder RBAC: FileFolder in TABLE_NAMESPACE, 11 folder methods in interfaceDbManagement (CRUD, move, cascade-delete, scope, neutralize), 6 routes in routeDataFiles replacing 501 stubs