863 lines
24 KiB
Markdown
863 lines
24 KiB
Markdown
# Navigation API Konzept
|
|
|
|
## Übersicht
|
|
|
|
Dieses Dokument beschreibt das Konzept für die Navigation-API, welche dem UI alle notwendigen Informationen für das Rendering der Navigation liefert. Die API ist die **Single Source of Truth** für die Navigationsstruktur.
|
|
|
|
## Grundprinzipien
|
|
|
|
### 1. Backend liefert, UI rendert
|
|
- Das **Gateway** bestimmt, welche Navigationselemente ein User sehen darf
|
|
- Das **UI** rendert nur das, was es vom Gateway erhält
|
|
- Keine Permission-Logik im UI für Navigation
|
|
|
|
### 2. Trennung von Daten und Darstellung
|
|
- Die API liefert **keine Style-Elemente** (Icons, CSS-Klassen, etc.)
|
|
- Die API liefert **UI-Komponenten-Codes**, die das UI auf seine Komponenten mappt
|
|
- Verschiedene UIs können dieselben Daten unterschiedlich darstellen
|
|
|
|
### 3. Nur sichtbare Elemente
|
|
- Elemente ohne Zugriffsberechtigung werden **nicht** in den Baum aufgenommen
|
|
- Kein `hasAccess: false` - wenn kein Zugriff, dann nicht im Response
|
|
|
|
### 4. Fehlertoleranz
|
|
- Wenn ein Code im UI nicht gemappt werden kann → Fehlertext im Nav-Tree
|
|
- Synchronisationsprobleme zwischen Gateway und UI werden sofort sichtbar
|
|
|
|
### 5. Personalisierbare Sortierung
|
|
- Jedes Element hat eine `order` Nummer für die Sortierung
|
|
- User können die Reihenfolge anpassen durch User-Settings
|
|
- Gateway liefert bereits sortiert (Default + User-Override)
|
|
|
|
---
|
|
|
|
## API Response Struktur
|
|
|
|
### Endpoint
|
|
|
|
```
|
|
GET /api/navigation
|
|
```
|
|
|
|
### Response Format
|
|
|
|
```json
|
|
{
|
|
"language": "de",
|
|
"blocks": [
|
|
{
|
|
"type": "static",
|
|
"id": "system",
|
|
"title": "SYSTEM",
|
|
"order": 10,
|
|
"items": [...]
|
|
},
|
|
{
|
|
"type": "static",
|
|
"id": "workflows",
|
|
"title": "WORKFLOWS",
|
|
"order": 20,
|
|
"items": [...]
|
|
},
|
|
{
|
|
"type": "dynamic",
|
|
"id": "features",
|
|
"title": "FEATURES",
|
|
"order": 100,
|
|
"mandates": [...]
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Block-Typen
|
|
|
|
### Static Block
|
|
|
|
Für System-Seiten, die nicht an Feature-Instanzen gebunden sind.
|
|
|
|
```json
|
|
{
|
|
"type": "static",
|
|
"id": "system",
|
|
"title": "SYSTEM",
|
|
"order": 10,
|
|
"items": [
|
|
{
|
|
"uiComponent": "page.system.home",
|
|
"uiLabel": "Übersicht",
|
|
"uiPath": "/",
|
|
"order": 10,
|
|
"objectKey": "ui.system.home"
|
|
},
|
|
{
|
|
"uiComponent": "page.system.settings",
|
|
"uiLabel": "Einstellungen",
|
|
"uiPath": "/settings",
|
|
"order": 20,
|
|
"objectKey": "ui.system.settings"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
**Felder pro Item:**
|
|
| Feld | Beschreibung |
|
|
|------|--------------|
|
|
| `uiComponent` | Eindeutiger Code für UI-Mapping (z.B. `page.system.home`) |
|
|
| `uiLabel` | Anzeigetext (bereits in der gewählten Sprache) |
|
|
| `uiPath` | URL-Pfad für Navigation |
|
|
| `order` | Sortierreihenfolge (Default oder User-Override) |
|
|
| `objectKey` | Vollqualifizierter RBAC-Objektname (für Debugging/Referenz) |
|
|
|
|
### Dynamic Block
|
|
|
|
Für Feature-Instanzen, gruppiert nach Mandanten.
|
|
|
|
```json
|
|
{
|
|
"type": "dynamic",
|
|
"id": "features",
|
|
"title": "FEATURES",
|
|
"order": 100,
|
|
"mandates": [
|
|
{
|
|
"id": "5ce04434-c4ce-4269-9861-19ff7ebc2a1d",
|
|
"uiLabel": "SOHA Treuhand AG",
|
|
"order": 10,
|
|
"features": [
|
|
{
|
|
"uiComponent": "feature.trustee",
|
|
"uiLabel": "Treuhand",
|
|
"order": 10,
|
|
"instances": [
|
|
{
|
|
"id": "2fc48f66-ad1b-4581-aa87-6aaa1e7c16e0",
|
|
"uiLabel": "ValueOn AG 2026",
|
|
"order": 10,
|
|
"views": [
|
|
{
|
|
"uiComponent": "page.feature.trustee.dashboard",
|
|
"uiLabel": "Übersicht",
|
|
"uiPath": "/mandates/5ce04434.../trustee/2fc48f66.../dashboard",
|
|
"order": 10,
|
|
"objectKey": "ui.feature.trustee.dashboard"
|
|
},
|
|
{
|
|
"uiComponent": "page.feature.trustee.positions",
|
|
"uiLabel": "Positionen",
|
|
"uiPath": "/mandates/5ce04434.../trustee/2fc48f66.../positions",
|
|
"order": 20,
|
|
"objectKey": "ui.feature.trustee.positions"
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
**Hinweis:** Der `dynamic` Block ist nur vorhanden, wenn der User mindestens eine Feature-Instanz hat.
|
|
|
|
**Sortierung auf allen Ebenen:**
|
|
- Mandanten: `mandates[].order`
|
|
- Features: `features[].order`
|
|
- Instanzen: `instances[].order`
|
|
- Views: `views[].order`
|
|
|
|
---
|
|
|
|
## User-Personalisierung (Order Override)
|
|
|
|
### Konzept
|
|
|
|
User können die Reihenfolge der Navigation anpassen. Die Einstellungen werden in den User-Settings gespeichert.
|
|
|
|
### User Settings Struktur
|
|
|
|
```json
|
|
{
|
|
"userId": "user-123",
|
|
"settings": {
|
|
"navOrder": {
|
|
"page.system.home": 10,
|
|
"page.system.prompts": 5,
|
|
"page.admin.users": 100,
|
|
"feature.trustee": 20,
|
|
"feature.realestate": 10,
|
|
"mandate.5ce04434-...": 5,
|
|
"instance.2fc48f66-...": 1
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Merge-Logik im Gateway
|
|
|
|
```
|
|
Effektive Order = User-Override ?? Default-Order ?? 50
|
|
```
|
|
|
|
1. Gateway lädt Default-Order aus Konfiguration
|
|
2. Gateway lädt User-Settings (falls vorhanden)
|
|
3. Für jedes Element: User-Order überschreibt Default
|
|
4. Sortierung erfolgt im Gateway, UI erhält bereits sortiert
|
|
|
|
### Sortier-Priorität
|
|
|
|
| Ebene | Key für navOrder | Beispiel |
|
|
|-------|------------------|----------|
|
|
| Block | Block-ID | `"system"`, `"workflows"`, `"features"` |
|
|
| Static Item | uiComponent | `"page.system.home"` |
|
|
| Mandate | `mandate.{id}` | `"mandate.5ce04434-..."` |
|
|
| Feature | uiComponent | `"feature.trustee"` |
|
|
| Instance | `instance.{id}` | `"instance.2fc48f66-..."` |
|
|
| View | uiComponent | `"page.feature.trustee.dashboard"` |
|
|
|
|
### API für Order-Update
|
|
|
|
```
|
|
PUT /api/user/settings/navOrder
|
|
Body: { "page.system.prompts": 5, "feature.trustee": 20 }
|
|
```
|
|
|
|
---
|
|
|
|
## Code-Konvention
|
|
|
|
### Statische Seiten
|
|
```
|
|
page.system.<page-name>
|
|
page.admin.<page-name>
|
|
```
|
|
|
|
Beispiele:
|
|
- `page.system.home`
|
|
- `page.system.settings`
|
|
- `page.system.prompts`
|
|
- `page.admin.users`
|
|
- `page.admin.mandates`
|
|
|
|
### Feature-Seiten
|
|
```
|
|
page.feature.<feature-code>.<view-name>
|
|
```
|
|
|
|
Beispiele:
|
|
- `page.feature.trustee.dashboard`
|
|
- `page.feature.trustee.positions`
|
|
- `page.feature.trustee.instance-roles`
|
|
- `page.feature.realestate.projects`
|
|
|
|
### Feature-Codes (für Gruppierung)
|
|
```
|
|
feature.<feature-code>
|
|
```
|
|
|
|
Beispiele:
|
|
- `feature.trustee`
|
|
- `feature.realestate`
|
|
- `feature.chatworkflow`
|
|
|
|
---
|
|
|
|
## ObjectKey-Konvention (RBAC)
|
|
|
|
Die `objectKey` werden für Access Rules verwendet und sind vollqualifiziert:
|
|
|
|
### UI Objekte
|
|
```
|
|
ui.system.<name> → System-Seiten
|
|
ui.admin.<name> → Admin-Seiten
|
|
ui.feature.<code>.<view> → Feature-Views
|
|
```
|
|
|
|
### DATA Objekte
|
|
```
|
|
data.system.<table> → System-Tabellen
|
|
data.feature.<code>.<table> → Feature-Tabellen
|
|
```
|
|
|
|
### RESOURCE Objekte
|
|
```
|
|
resource.system.<action> → System-Aktionen
|
|
resource.feature.<code>.<action> → Feature-Aktionen
|
|
```
|
|
|
|
---
|
|
|
|
## UI Mapping & Fehlerbehandlung
|
|
|
|
### UI Code Registry
|
|
|
|
Das UI definiert ein Mapping von Codes zu Komponenten:
|
|
|
|
```typescript
|
|
// frontend_nyla/src/config/pageRegistry.ts
|
|
|
|
export const PAGE_REGISTRY: Record<string, PageDefinition> = {
|
|
// Static pages
|
|
'page.system.home': {
|
|
component: HomePage,
|
|
icon: FaHome,
|
|
layout: 'default',
|
|
},
|
|
'page.system.settings': {
|
|
component: SettingsPage,
|
|
icon: FaCog,
|
|
layout: 'default',
|
|
},
|
|
'page.admin.users': {
|
|
component: AdminUsersPage,
|
|
icon: FaUsers,
|
|
layout: 'admin',
|
|
},
|
|
|
|
// Feature pages
|
|
'page.feature.trustee.dashboard': {
|
|
component: TrusteeDashboardView,
|
|
icon: FaChartBar,
|
|
layout: 'feature',
|
|
},
|
|
'page.feature.trustee.positions': {
|
|
component: TrusteePositionsView,
|
|
icon: FaListAlt,
|
|
layout: 'feature',
|
|
},
|
|
// ...
|
|
};
|
|
|
|
export const FEATURE_REGISTRY: Record<string, FeatureDefinition> = {
|
|
'feature.trustee': {
|
|
icon: FaBriefcase,
|
|
color: '#e74c3c',
|
|
},
|
|
'feature.realestate': {
|
|
icon: FaBuilding,
|
|
color: '#3498db',
|
|
},
|
|
};
|
|
```
|
|
|
|
### Fehlerbehandlung
|
|
|
|
Wenn ein `uiComponent` Code nicht gemappt werden kann:
|
|
|
|
```typescript
|
|
function renderNavItem(item: NavItem) {
|
|
const pageDef = PAGE_REGISTRY[item.uiComponent];
|
|
|
|
if (!pageDef) {
|
|
// Fehler sichtbar machen!
|
|
return (
|
|
<NavError>
|
|
⚠️ Unbekannter Code: {item.uiComponent}
|
|
<small>ObjectKey: {item.objectKey}</small>
|
|
</NavError>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<NavLink to={item.uiPath} icon={pageDef.icon}>
|
|
{item.uiLabel}
|
|
</NavLink>
|
|
);
|
|
}
|
|
```
|
|
|
|
**Wichtig:** Fehler werden im Nav-Tree angezeigt, nicht versteckt! So werden Synchronisationsprobleme zwischen Gateway und UI sofort sichtbar.
|
|
|
|
### Sortierung im UI
|
|
|
|
Das UI sortiert die bereits vom Gateway sortierten Elemente NICHT neu. Die Sortierung erfolgt ausschliesslich im Gateway basierend auf:
|
|
|
|
1. Default-Order aus Konfiguration
|
|
2. User-Override aus Settings (falls vorhanden)
|
|
|
|
```typescript
|
|
// UI rendert in der Reihenfolge wie empfangen
|
|
function renderBlock(block: Block) {
|
|
// items sind bereits sortiert
|
|
return block.items.map(item => renderNavItem(item));
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Implementierungsschritte
|
|
|
|
### Phase 1: Gateway - Navigation Endpoint
|
|
|
|
1. **Neuer Endpoint** `/api/navigation` erstellen
|
|
2. **Static Blocks** aus `NAVIGATION_SECTIONS` generieren
|
|
3. **Dynamic Block** aus Feature-Instanzen generieren
|
|
4. **Permission-Filter** anwenden (nur sichtbare Elemente)
|
|
5. **uiComponent Codes** gemäß Konvention setzen
|
|
6. **Default-Order** für alle Elemente setzen
|
|
|
|
### Phase 2: Gateway - Access Rules
|
|
|
|
1. **TEMPLATE_ROLES** auf vollqualifizierte `objectKey` umstellen
|
|
2. **Permission-Check** auf `objectKey` umstellen
|
|
3. **Migration** bestehender Access Rules (falls nötig)
|
|
|
|
### Phase 3: Gateway - User Settings
|
|
|
|
1. **User Settings Modell** erweitern mit `navOrder`
|
|
2. **API Endpoint** `PUT /api/user/settings/navOrder` erstellen
|
|
3. **Merge-Logik** implementieren (User-Override → Default)
|
|
4. **Sortierung** vor Response durchführen
|
|
|
|
### Phase 4: Frontend
|
|
|
|
1. **PAGE_REGISTRY** erstellen mit uiComponent → Component Mapping
|
|
2. **FEATURE_REGISTRY** erstellen mit Feature-Code → Style Mapping
|
|
3. **useNavigation Hook** auf neuen Endpoint umstellen
|
|
4. **MandateNavigation** refactoren für neue Datenstruktur
|
|
5. **Fehlerhandling** für unbekannte uiComponents implementieren
|
|
|
|
### Phase 5: Frontend - Drag & Drop (Optional)
|
|
|
|
1. **Drag & Drop UI** für Nav-Tree Sortierung
|
|
2. **API Call** bei Order-Änderung
|
|
3. **Optimistic Update** für schnelle UX
|
|
|
|
### Phase 6: Cleanup
|
|
|
|
1. **FEATURE_REGISTRY** (alte Version in mandate.ts) entfernen
|
|
2. **Hardcodierte Navigation** im Frontend entfernen
|
|
3. **Tests** für Synchronisation Gateway ↔ UI
|
|
|
|
---
|
|
|
|
## Beispiel: Vollständige Response
|
|
|
|
```json
|
|
{
|
|
"language": "de",
|
|
"blocks": [
|
|
{
|
|
"type": "static",
|
|
"id": "system",
|
|
"title": "SYSTEM",
|
|
"order": 10,
|
|
"items": [
|
|
{
|
|
"uiComponent": "page.system.home",
|
|
"uiLabel": "Übersicht",
|
|
"uiPath": "/",
|
|
"order": 10,
|
|
"objectKey": "ui.system.home"
|
|
},
|
|
{
|
|
"uiComponent": "page.system.settings",
|
|
"uiLabel": "Einstellungen",
|
|
"uiPath": "/settings",
|
|
"order": 20,
|
|
"objectKey": "ui.system.settings"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"type": "static",
|
|
"id": "workflows",
|
|
"title": "WORKFLOWS",
|
|
"order": 20,
|
|
"items": [
|
|
{
|
|
"uiComponent": "page.system.playground",
|
|
"uiLabel": "Chat Playground",
|
|
"uiPath": "/workflows/playground",
|
|
"order": 10,
|
|
"objectKey": "ui.system.playground"
|
|
},
|
|
{
|
|
"uiComponent": "page.system.chats",
|
|
"uiLabel": "Chats",
|
|
"uiPath": "/workflows/list",
|
|
"order": 20,
|
|
"objectKey": "ui.system.chats"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"type": "static",
|
|
"id": "basedata",
|
|
"title": "BASISDATEN",
|
|
"order": 30,
|
|
"items": [
|
|
{
|
|
"uiComponent": "page.system.prompts",
|
|
"uiLabel": "Prompts",
|
|
"uiPath": "/basedata/prompts",
|
|
"order": 10,
|
|
"objectKey": "ui.system.prompts"
|
|
},
|
|
{
|
|
"uiComponent": "page.system.files",
|
|
"uiLabel": "Dateien",
|
|
"uiPath": "/basedata/files",
|
|
"order": 20,
|
|
"objectKey": "ui.system.files"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"type": "static",
|
|
"id": "admin",
|
|
"title": "ADMINISTRATION",
|
|
"order": 200,
|
|
"items": [
|
|
{
|
|
"uiComponent": "page.admin.users",
|
|
"uiLabel": "Benutzer",
|
|
"uiPath": "/admin/users",
|
|
"order": 10,
|
|
"objectKey": "ui.admin.users"
|
|
},
|
|
{
|
|
"uiComponent": "page.admin.mandates",
|
|
"uiLabel": "Mandanten",
|
|
"uiPath": "/admin/mandates",
|
|
"order": 20,
|
|
"objectKey": "ui.admin.mandates"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"type": "dynamic",
|
|
"id": "features",
|
|
"title": "MEINE FEATURES",
|
|
"order": 100,
|
|
"mandates": [
|
|
{
|
|
"id": "5ce04434-c4ce-4269-9861-19ff7ebc2a1d",
|
|
"uiLabel": "SOHA Treuhand AG",
|
|
"order": 10,
|
|
"features": [
|
|
{
|
|
"uiComponent": "feature.trustee",
|
|
"uiLabel": "Treuhand",
|
|
"order": 10,
|
|
"instances": [
|
|
{
|
|
"id": "2fc48f66-ad1b-4581-aa87-6aaa1e7c16e0",
|
|
"uiLabel": "ValueOn AG 2026",
|
|
"order": 10,
|
|
"views": [
|
|
{
|
|
"uiComponent": "page.feature.trustee.dashboard",
|
|
"uiLabel": "Übersicht",
|
|
"uiPath": "/mandates/5ce04434-c4ce-4269-9861-19ff7ebc2a1d/trustee/2fc48f66-ad1b-4581-aa87-6aaa1e7c16e0/dashboard",
|
|
"order": 10,
|
|
"objectKey": "ui.feature.trustee.dashboard"
|
|
},
|
|
{
|
|
"uiComponent": "page.feature.trustee.positions",
|
|
"uiLabel": "Positionen",
|
|
"uiPath": "/mandates/5ce04434-c4ce-4269-9861-19ff7ebc2a1d/trustee/2fc48f66-ad1b-4581-aa87-6aaa1e7c16e0/positions",
|
|
"order": 20,
|
|
"objectKey": "ui.feature.trustee.positions"
|
|
},
|
|
{
|
|
"uiComponent": "page.feature.trustee.documents",
|
|
"uiLabel": "Dokumente",
|
|
"uiPath": "/mandates/5ce04434-c4ce-4269-9861-19ff7ebc2a1d/trustee/2fc48f66-ad1b-4581-aa87-6aaa1e7c16e0/documents",
|
|
"order": 30,
|
|
"objectKey": "ui.feature.trustee.documents"
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"id": "abc123-mandate-2",
|
|
"uiLabel": "Partner AG",
|
|
"order": 20,
|
|
"features": [
|
|
{
|
|
"uiComponent": "feature.realestate",
|
|
"uiLabel": "Immobilien",
|
|
"order": 10,
|
|
"instances": [
|
|
{
|
|
"id": "def456-instance",
|
|
"uiLabel": "Objektstudien ZH",
|
|
"order": 10,
|
|
"views": [
|
|
{
|
|
"uiComponent": "page.feature.realestate.dashboard",
|
|
"uiLabel": "Übersicht",
|
|
"uiPath": "/mandates/abc123-mandate-2/realestate/def456-instance/dashboard",
|
|
"order": 10,
|
|
"objectKey": "ui.feature.realestate.dashboard"
|
|
},
|
|
{
|
|
"uiComponent": "page.feature.realestate.projects",
|
|
"uiLabel": "Projekte",
|
|
"uiPath": "/mandates/abc123-mandate-2/realestate/def456-instance/projects",
|
|
"order": 20,
|
|
"objectKey": "ui.feature.realestate.projects"
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Vorteile dieses Ansatzes
|
|
|
|
1. **Single Source of Truth**: Gateway definiert, was sichtbar ist
|
|
2. **Keine Permission-Logik im UI**: Alles was kommt, wird gerendert
|
|
3. **Flexibles UI**: Verschiedene UIs können dieselben Daten anders darstellen
|
|
4. **Fehlertoleranz**: Synchronisationsprobleme werden sofort sichtbar
|
|
5. **Saubere Trennung**: Daten (Gateway) vs. Darstellung (UI)
|
|
6. **Testbarkeit**: API-Response kann einfach getestet werden
|
|
7. **Dokumentation**: Codes und ObjectKeys sind selbstdokumentierend
|
|
8. **Personalisierung**: User können Navigation individuell sortieren
|
|
9. **Konsistente Sortierung**: Gateway sortiert zentral, UI respektiert die Reihenfolge
|
|
|
|
---
|
|
|
|
## Offene Fragen (Beantwortet)
|
|
|
|
1. Soll der `dynamic` Block vor oder nach den `static` Blocks kommen? → **Diese initiale Reihenfolge: System, `<dynamic>`, workflows, basisdaten, migrate to features, administration**
|
|
2. Braucht es eine Versionierung der API für Breaking Changes? → **Nein**
|
|
3. Wie werden Feature-spezifische Sub-Navigationen behandelt (z.B. Tabs innerhalb einer View)? → **Tabs innerhalb einer View werden als separate Views behandelt**
|
|
4. Soll der Default-Order-Abstand 10 sein (erlaubt Einfügen zwischen Elementen)? → **Ja**
|
|
5. Soll die Order-Personalisierung auch für Admins pro Mandate/Feature-Instanz möglich sein (System-Default vs. User-Override)? → **Ja**
|
|
|
|
---
|
|
|
|
## Code-Analyse: IST-Zustand
|
|
|
|
### Gateway (Backend)
|
|
|
|
#### 1. `mainSystem.py` - Statische Navigation
|
|
|
|
```python
|
|
# IST: Items haben id, label, path, icon, objectKey
|
|
# NEU: Brauchen uiComponent, uiLabel, uiPath, order (ohne icon)
|
|
NAVIGATION_SECTIONS = [
|
|
{
|
|
"id": "system",
|
|
"order": 10, # ✓ Vorhanden
|
|
"items": [
|
|
{
|
|
"id": "home",
|
|
"objectKey": "ui.system.home", # ✓ Korrekt
|
|
"label": {"en": "Home", "de": "Übersicht"},
|
|
"icon": "FaHome", # ✗ Muss entfernt werden
|
|
"path": "/",
|
|
},
|
|
]
|
|
}
|
|
]
|
|
```
|
|
|
|
**Änderungen:**
|
|
- `icon` entfernen (UI mappt selbst)
|
|
- Item-Level `order` hinzufügen
|
|
- Felder umbenennen für API Response
|
|
|
|
#### 2. `routeSystem.py` - Navigation Endpoint
|
|
|
|
```python
|
|
# IST: GET /api/system/navigation
|
|
# Gibt zurück: { "sections": [...], "language": "de" }
|
|
|
|
# NEU: GET /api/navigation
|
|
# Soll zurückgeben: { "blocks": [...], "language": "de" }
|
|
```
|
|
|
|
**Änderungen:**
|
|
- Neuer kombinierter Endpoint
|
|
- Static Blocks + Dynamic Block zusammenführen
|
|
- Response-Struktur gemäss Konzept
|
|
|
|
#### 3. `mainTrustee.py` - Feature Views
|
|
|
|
```python
|
|
# IST: TEMPLATE_ROLES verwendet Kurznamen
|
|
TEMPLATE_ROLES = [
|
|
{
|
|
"roleLabel": "trustee-client",
|
|
"accessRules": [
|
|
{"context": "UI", "item": "dashboard", "view": True}, # ✗ Kurzname!
|
|
{"context": "UI", "item": "positions", "view": True}, # ✗ Kurzname!
|
|
]
|
|
}
|
|
]
|
|
|
|
# ABER: UI_OBJECTS verwendet vollqualifizierte Namen
|
|
UI_OBJECTS = [
|
|
{"objectKey": "ui.feature.trustee.dashboard", ...}, # ✓ Vollständig
|
|
]
|
|
```
|
|
|
|
**Inkonsistenz gefunden!** `item` in AccessRules muss `objectKey` entsprechen.
|
|
|
|
#### 4. `routeAdminFeatures.py` - Permission Logic
|
|
|
|
```python
|
|
# IST: _deriveViewPermissions ist TRUSTEE-spezifisch!
|
|
def _deriveViewPermissions(permissions):
|
|
# Hard-coded: TrusteePosition → positions view
|
|
# Hard-coded: TrusteeDocument → documents view
|
|
```
|
|
|
|
**Problem:** Nicht generisch, funktioniert nur für Trustee.
|
|
|
|
### Frontend (UI)
|
|
|
|
#### 1. `MandateNavigation.tsx`
|
|
|
|
```typescript
|
|
// IST: Zwei separate Datenquellen
|
|
const { sections } = useNavigation(); // Static von API
|
|
const mandates = useMandates(); // Dynamic von API
|
|
|
|
// IST: FEATURE_ICONS definiert im Frontend
|
|
const FEATURE_ICONS = { trustee: <FaBriefcase /> };
|
|
```
|
|
|
|
**Problem:** Navigation wird im Frontend zusammengebaut, nicht vom Backend.
|
|
|
|
#### 2. `mandate.ts` - FEATURE_REGISTRY
|
|
|
|
```typescript
|
|
// IST: Doppelte Definition von Views!
|
|
export const FEATURE_REGISTRY = {
|
|
trustee: {
|
|
views: [
|
|
{ code: 'dashboard', label: {...}, path: 'dashboard' }, // ✗ Kurzname
|
|
{ code: 'positions', label: {...}, path: 'positions' }, // ✗ Kurzname
|
|
]
|
|
}
|
|
};
|
|
```
|
|
|
|
**Duplizierung:** Views sind in `mainTrustee.py` UND `mandate.ts` definiert!
|
|
|
|
---
|
|
|
|
## Impact-Analyse
|
|
|
|
### Phase 1: Gateway - Navigation Endpoint
|
|
|
|
| Datei | Änderungstyp | Aufwand | Breaking |
|
|
|-------|--------------|---------|----------|
|
|
| `routeSystem.py` | Neu/Refactor | Hoch | Ja |
|
|
| `mainSystem.py` | Update | Mittel | Nein |
|
|
| `routeAdminFeatures.py` | Integrieren | Hoch | Nein |
|
|
|
|
**Konkrete Aufgaben:**
|
|
1. Neuen Endpoint `GET /api/navigation` erstellen
|
|
2. Static Blocks aus `NAVIGATION_SECTIONS` generieren
|
|
3. Dynamic Block aus `_getMyFeatureInstances`-Logik generieren
|
|
4. Permission-Filter auf beide anwenden
|
|
5. `icon` aus Response entfernen
|
|
6. Felder umbenennen: `label→uiLabel`, `path→uiPath`, neu `uiComponent`
|
|
7. `order` auf Item-Level hinzufügen
|
|
|
|
### Phase 2: Gateway - Access Rules Migration
|
|
|
|
| Datei | Änderungstyp | Aufwand | Breaking |
|
|
|-------|--------------|---------|----------|
|
|
| `mainTrustee.py` | Update | Mittel | Ja (DB) |
|
|
| `mainRealEstate.py` | Update | Mittel | Ja (DB) |
|
|
| Migration Script | Neu | Mittel | - |
|
|
|
|
**Konkrete Aufgaben:**
|
|
1. `TEMPLATE_ROLES.item` auf vollqualifizierte ObjectKeys umstellen
|
|
2. Migration: Bestehende AccessRules in DB aktualisieren
|
|
3. Permission-Check in `_checkUiPermission` anpassen
|
|
|
|
### Phase 3: Gateway - User Settings
|
|
|
|
| Datei | Änderungstyp | Aufwand | Breaking |
|
|
|-------|--------------|---------|----------|
|
|
| `datamodelUam.py` | Erweitern | Gering | Nein |
|
|
| `routeSystem.py` | Endpoint | Gering | Nein |
|
|
|
|
**Konkrete Aufgaben:**
|
|
1. `UserSettings` Tabelle oder JSON-Feld in User
|
|
2. `PUT /api/user/settings/navOrder` Endpoint
|
|
3. Merge-Logik in Navigation-Endpoint
|
|
|
|
### Phase 4: Frontend
|
|
|
|
| Datei | Änderungstyp | Aufwand | Breaking |
|
|
|-------|--------------|---------|----------|
|
|
| `pageRegistry.ts` | Neu | Hoch | - |
|
|
| `useNavigation.ts` | Refactor | Mittel | Ja |
|
|
| `MandateNavigation.tsx` | Refactor | Hoch | Ja |
|
|
| `mandate.ts` | Cleanup | Gering | Ja |
|
|
|
|
**Konkrete Aufgaben:**
|
|
1. `PAGE_REGISTRY` erstellen mit uiComponent → Component Mapping
|
|
2. `FEATURE_REGISTRY` mit `feature.*` Codes
|
|
3. `useNavigation` für neuen Endpoint anpassen
|
|
4. `MandateNavigation` für Blocks-Struktur refactoren
|
|
5. Error-Handling für unbekannte uiComponents
|
|
|
|
---
|
|
|
|
## Kritische Punkte
|
|
|
|
### 1. Breaking Changes
|
|
Die Umstellung erfordert **simultane Änderungen** in Gateway UND Frontend. Empfehlung:
|
|
- Feature-Flag für neuen Endpoint
|
|
- Alte Endpoints temporär behalten
|
|
- Frontend schrittweise migrieren
|
|
|
|
### 2. Datenbank-Migration
|
|
Bestehende AccessRules haben `item="dashboard"`, neu soll `item="ui.feature.trustee.dashboard"` sein.
|
|
```sql
|
|
-- Beispiel-Migration
|
|
UPDATE access_rules
|
|
SET item = 'ui.feature.trustee.' || item
|
|
WHERE role_id IN (SELECT id FROM roles WHERE feature_code = 'trustee')
|
|
AND context = 'UI'
|
|
AND item IS NOT NULL;
|
|
```
|
|
|
|
### 3. Performance
|
|
Navigation mit vielen Instanzen kann langsam werden. Empfehlungen:
|
|
- Response cachen pro User
|
|
- Cache invalidieren bei Permission-Änderungen
|
|
- Lazy-Loading für sehr grosse Deployments prüfen
|
|
|
|
### 4. Feature-spezifische View-Ableitung
|
|
`_deriveViewPermissions` ist Trustee-spezifisch. Optionen:
|
|
- **A)** Jedes Feature definiert seine Ableitungslogik
|
|
- **B)** Keine Ableitung, nur explizite UI-Rules
|
|
- **C)** Generische Mapping-Konfiguration pro Feature
|
|
|
|
**Empfehlung:** Option B - Explizite UI-Rules sind klarer und testbarer.
|
|
|
|
---
|
|
|
|
## Empfohlene Reihenfolge
|
|
|
|
1. **Schritt 1:** `PAGE_REGISTRY` im Frontend erstellen (unabhängig)
|
|
2. **Schritt 2:** Neuen `/api/navigation` Endpoint mit Feature-Flag
|
|
3. **Schritt 3:** Frontend auf neuen Endpoint umstellen
|
|
4. **Schritt 4:** AccessRules-Migration
|
|
5. **Schritt 5:** User-Settings für Order
|
|
6. **Schritt 6:** Alte Endpoints/Code entfernen
|