/** * MandateNavigation * * Hierarchische Navigation für das Multi-Tenant-System. * Verwendet TreeNavigation für flexible Baumstruktur. * * Navigation wird vollständig vom Backend geladen (/api/navigation). * Backend liefert Blocks-Struktur mit Static und Dynamic Blocks. * UI mappt uiComponent zu Icons via pageRegistry. * * TREE STRUCTURE (alles collapsible): * ▼ Meine Sicht * - Übersicht, Einstellungen, Prompts, Dateien, Verbindungen, Billing * ───────────── * ▼ Mandant 1 * - 🎯 Instanz 1 (Feature-Icon + Instanz-Name) * - 💼 Instanz 2 (Feature-Icon + Instanz-Name) * ───────────── * ▶ Administration * - Users, Mandates, Roles, ... */ import React, { useMemo } from 'react'; import { useNavigation } from '../../hooks/useNavigation'; import type { DynamicBlock, NavigationItem, NavigationMandate, FeatureInstance, FeatureView } from '../../hooks/useNavigation'; import { getPageIcon } from '../../config/pageRegistry'; import { FaSpinner } from 'react-icons/fa'; import { TreeNavigation, type TreeItem, type TreeNodeItem } from './TreeNavigation'; import styles from './MandateNavigation.module.css'; // ============================================================================= // HELPER FUNCTIONS - Convert API blocks to TreeItems // ============================================================================= /** * Convert a NavigationItem (from static block) to TreeNodeItem */ function navigationItemToTreeNode(item: NavigationItem): TreeNodeItem { return { id: item.objectKey, label: item.uiLabel, icon: getPageIcon(item.uiComponent), path: item.uiPath, }; } /** * Convert a list of NavigationItems into a collapsible TreeNodeItem container. * Used for grouping static items under "Meine Sicht" and "Administration". */ function _staticItemsToTreeNode( id: string, label: string, items: NavigationItem[], defaultExpanded: boolean = true, ): TreeNodeItem { return { id, label, children: items.map(navigationItemToTreeNode), defaultExpanded, }; } /** * Convert a FeatureView to TreeNodeItem */ function featureViewToTreeNode(view: FeatureView): TreeNodeItem { return { id: view.objectKey, label: view.uiLabel, path: view.uiPath, }; } /** * Convert a FeatureInstance to TreeNodeItem (with feature icon) * Instance node gets path to first view so clicking the instance name navigates to dashboard. * Shows the feature icon next to the instance name for visual distinction. */ function featureInstanceToTreeNode(instance: FeatureInstance, featureUiComponent: string): TreeNodeItem { const children = instance.views.map(featureViewToTreeNode); return { id: instance.id, label: instance.uiLabel, icon: getPageIcon(featureUiComponent), // Use feature icon for instance path: instance.views.length > 0 ? instance.views[0].uiPath : undefined, children, defaultExpanded: false, }; } /** * Convert a NavigationMandate to TreeNodeItem * * FLAT STRUCTURE: Instances are listed directly under mandate (no feature grouping). * Each instance shows the feature's icon for visual distinction. * * Before: Mandate → Feature → Instance → Views * Now: Mandate → Instance (with feature icon) → Views */ function navigationMandateToTreeNode(mandate: NavigationMandate): TreeNodeItem | null { if (mandate.features.length === 0) { return null; } // Flatten: collect all instances from all features directly under mandate const instanceNodes: TreeNodeItem[] = []; for (const feature of mandate.features) { for (const instance of feature.instances) { instanceNodes.push(featureInstanceToTreeNode(instance, feature.uiComponent)); } } if (instanceNodes.length === 0) { return null; } return { id: mandate.id, label: mandate.uiLabel, children: instanceNodes, defaultExpanded: true, }; } /** * Convert a DynamicBlock to array of TreeNodeItems (mandate nodes) */ function dynamicBlockToTreeNodes(block: DynamicBlock): TreeNodeItem[] { return block.mandates .map(navigationMandateToTreeNode) .filter((node): node is TreeNodeItem => node !== null); } // ============================================================================= // LOADING STATE // ============================================================================= const LoadingState: React.FC = () => (
Keine Feature-Instanzen verfügbar.
Kontaktiere einen Administrator, um Zugriff zu erhalten.