wiki/z-archive/implementation/Refactor Nyla UI basic components with new mandate SaaS model/analysis_and_implementation_plan.md

26 KiB

Refactor Nyla UI - Analyse und Implementation Plan

Datum: 23. Januar 2026


Executive Summary

Die Seiten Chat Playground, Workflows, Automations, Prompts, Files und Connections im neuen UI (frontend_nyla) sind inkomplett implementiert. Sie nutzen:

  • Eigene, vereinfachte Table-Komponenten statt des etablierten FormGenerator-Systems
  • Fehlende Dashboard- und Chat-Elemente (PlaygroundPage)
  • Keine RBAC-Berechtigungsprüfung
  • Keine Backend-Pagination/Sorting/Filtering
  • Keine standardisierten CRUD-Modals

Design-Prinzipien für die Implementation

WICHTIG: Das bestehende Look & Feel von Nyla MUSS beibehalten werden!

  • Keine neuen Styles oder Design-Patterns einführen
  • Bestehende CSS-Module und Komponenten-Styles wiederverwenden
  • Nur fehlende Logik ergänzen, nicht das visuelle Design ändern
  • Neue Komponenten müssen sich nahtlos in das bestehende Design einfügen

1. IST-Analyse

1.1 Chat Playground (PlaygroundPage.tsx)

Aktueller Zustand: Die PlaygroundPage ist extrem vereinfacht und hat fast alle Funktionen aus dem alten UI (frontend_agents) verloren.

Was FEHLT (Vergleich zu frontend_agents/public/htmlparts/part_workflow.html):

Element Alt (frontend_agents) Neu (frontend_nyla) Ziel
Layout 70/30 Spalten (Chat/Dashboard) Single-Column, kein Dashboard Resizable Spalten mit Drag-Divider
Dashboard Hierarchisches Dashboard mit Rounds → Operations → Logs Komplett fehlt Logik portieren, Nyla-Styles
Progress-Tracking Progress-Bars pro Operation Komplett fehlt Logik portieren, Nyla-Styles
Collapse/Expand Für Rounds, Operations, Logs Komplett fehlt Logik portieren
Prompt-Auswahl Dropdown mit Prompts aus API Fehlt Bestehende DropdownSelect nutzen
Workflow-Modus Dropdown mit Enum aus Backend Fehlt Bestehende DropdownSelect nutzen
Datei-Upload Button + Drag & Drop, mehrere Dateien Fehlt Bestehende DragDropOverlay nutzen
Voice Recording Mikrofon-Button mit STT Fehlt Logik portieren, Nyla Button-Styles
Stop/Reset Buttons Stop, Reset, Refresh Tokens Nur Läuft... Button Bestehende Button-Komponenten
Data Statistics Sent/Received/Time/Tokens Fehlt Logik portieren, Nyla-Styles
File Preview Modal Vorschau, Download, Copy, TTS Fehlt Bestehende Popup-Komponente nutzen
Unified Content Area Kombinierte Logs + Messages chronologisch Getrennte Listen Bestehende Log/Messages nutzen
Auto-Scroll Automatisches Scrollen Fehlt Bestehende AutoScroll nutzen
Empty State Mit Link zur Einführung Einfacher Text Nyla-Styles

Vorhandene Hooks im frontend_nyla/src/hooks/playground/:

- useDashboardInputForm.ts
- useDashboardLogTree.ts
- useWorkflowLifecycle.ts
- useWorkflowOperations.ts
- useWorkflowPolling.ts
- useWorkflows.ts
- playgroundUtils.ts

Problem: Die Hooks sind vorhanden, aber die UI-Komponenten nutzen sie nicht vollständig!

1.2 Workflows, Automations, Prompts, Files, Connections

Aktueller Zustand: Diese Seiten haben eigene, einfache Table-Implementierungen statt FormGeneratorTable.

Vergleich:

Feature AdminUsersPage (korrekt) WorkflowsPage (falsch)
FormGeneratorTable Verwendet Eigene <table>
FormGeneratorForm Für Create/Edit Modals Keine Modals
attributes vom Backend Über Hook Hardcoded Columns
columns aus attributes Dynamisch generiert Hardcoded
Backend-Pagination Via hookData Client-seitig
Backend-Sorting Via hookData Client-seitig
Backend-Filtering Via hookData Client-seitig
Inline-Editing Für Boolean-Felder Nicht unterstützt
RBAC Permissions permissions?.create !== 'n' Keine Prüfung
Action Buttons Standard + Custom Nur Delete
Loading State Overlay Einfacher Text
Empty State Mit Icon und CTA Einfacher Text
Error State Mit Retry-Button Einfacher Text

2. Vorhandene Ressourcen im frontend_nyla

2.1 FormGenerator-System (vollständig implementiert)

Komponenten:

  • FormGeneratorTable - Tabelle mit Pagination, Sorting, Filtering
  • FormGeneratorForm - Backend-driven Formulare
  • FormGeneratorList - Listenansicht
  • FormGeneratorControls - Such- und Filter-Controls
  • Action Buttons: Edit, Delete, View, Copy, Custom

Features:

  • FK-Resolution (Foreign Keys anzeigen als Labels)
  • TextMultilingual-Support
  • Inline-Editing für Boolean-Felder
  • Column-Resizing mit LocalStorage-Persistenz
  • Multi-Column-Sorting

2.2 Vorhandene Hooks

Workflows:

  • useWorkflows.ts - useUserWorkflows(), useWorkflowOperations()

Automations:

  • useAutomations.ts - useAutomations(), useAutomationOperations()

Prompts:

  • usePrompts.ts - usePrompts(), usePromptOperations()

Files:

  • useFiles.ts - useUserFiles(), useFileOperations()

Connections:

  • useConnections.ts - useConnections() (vollständig)

Playground-spezifisch:

  • hooks/playground/useDashboardInputForm.ts
  • hooks/playground/useDashboardLogTree.ts
  • hooks/playground/useWorkflowLifecycle.ts
  • hooks/playground/useWorkflowOperations.ts
  • hooks/playground/useWorkflowPolling.ts

2.3 Bestehende Nyla UI-Komponenten (WIEDERVERWENDEN!)

WICHTIG: Diese Komponenten definieren das Nyla Look & Feel und sollten bei der Implementation wiederverwendet werden!

UiComponents (in components/UiComponents/):

  • Button/ - Standard-Buttons, CreateButton, UploadButton
  • TextField/ - Input-Felder
  • DropdownSelect/ - Select-Komponente
  • DragDropOverlay/ - Für File-Upload mit Drag & Drop
  • Log/ - Log-Darstellung mit LogMessage
  • Messages/ - Chat-Nachrichten
  • Popup/ - Modal-Dialoge
  • Toast/ - Notifications
  • Tabs/ - Tab-Navigation
  • AutoScroll/ - Auto-Scroll-Funktionalität

Diese Komponenten-Styles übernehmen:

  • Farben (CSS-Variablen)
  • Typografie
  • Abstände/Padding
  • Border-Radien
  • Hover-/Focus-States

3. Implementation Plan

Phase 1: Chat Playground (Priorität HOCH)

3.1.1 Layout-Refactoring

Datei: frontend_nyla/src/pages/workflows/PlaygroundPage.tsx

Design-Prinzip: Bestehendes Nyla Look & Feel beibehalten, nur fehlende Logik ergänzen!

  1. Resizable Spalten-Layout mit Drag-Divider:

Statt eines fixen 70/30-Layouts wird ein dynamisch anpassbares Layout implementiert:

  • Default: 70% Chat / 30% Dashboard
  • Benutzer kann die Trennlinie mit der Maus verschieben
  • Min/Max-Grenzen: Chat min 40%, Dashboard min 20%
  • Spaltenbreite wird in LocalStorage persistiert
import { useResizablePanels } from '../../hooks/useResizablePanels';

const PlaygroundPage: React.FC = () => {
  // Hook für resizable panels mit LocalStorage-Persistenz
  const {
    leftWidth,        // in Prozent (default 70)
    isDragging,
    handleMouseDown,  // Auf Divider anwenden
  } = useResizablePanels({
    storageKey: 'playground-panel-width',
    defaultLeftWidth: 70,
    minLeftWidth: 40,
    maxLeftWidth: 80,
  });

  return (
    <div className={styles.playgroundContainer}>
      <div className={styles.chatSection}>
        <div className={styles.chatColumns}>
          {/* Links - Chat Messages (dynamische Breite) */}
          <div 
            className={styles.chatLeft}
            style={{ width: `${leftWidth}%` }}
          >
            <UnifiedContentArea />
          </div>
          
          {/* Resizable Divider */}
          <div 
            className={`${styles.resizeDivider} ${isDragging ? styles.dragging : ''}`}
            onMouseDown={handleMouseDown}
          >
            <div className={styles.dividerHandle} />
          </div>
          
          {/* Rechts - Dashboard (dynamische Breite) */}
          <div 
            className={styles.chatRight}
            style={{ width: `${100 - leftWidth}%` }}
          >
            <WorkflowDashboard />
          </div>
        </div>
      </div>
      
      <div className={styles.workflowFooter}>
        <UserInputArea />
        <DataStatistics />
      </div>
    </div>
  );
};

3.1.1.1 Hook: useResizablePanels

Zu erstellen: frontend_nyla/src/hooks/useResizablePanels.ts

import { useState, useCallback, useEffect, useRef } from 'react';

interface UseResizablePanelsOptions {
  storageKey: string;
  defaultLeftWidth: number;   // in %
  minLeftWidth: number;       // in %
  maxLeftWidth: number;       // in %
}

export const useResizablePanels = ({
  storageKey,
  defaultLeftWidth,
  minLeftWidth,
  maxLeftWidth,
}: UseResizablePanelsOptions) => {
  // Initialer Wert aus LocalStorage oder Default
  const [leftWidth, setLeftWidth] = useState<number>(() => {
    const stored = localStorage.getItem(storageKey);
    if (stored) {
      const parsed = parseFloat(stored);
      if (!isNaN(parsed) && parsed >= minLeftWidth && parsed <= maxLeftWidth) {
        return parsed;
      }
    }
    return defaultLeftWidth;
  });
  
  const [isDragging, setIsDragging] = useState(false);
  const containerRef = useRef<HTMLElement | null>(null);
  
  // Mouse-Event-Handler
  const handleMouseDown = useCallback((e: React.MouseEvent) => {
    e.preventDefault();
    setIsDragging(true);
    containerRef.current = (e.target as HTMLElement).closest('.chatColumns');
  }, []);
  
  useEffect(() => {
    if (!isDragging) return;
    
    const handleMouseMove = (e: MouseEvent) => {
      if (!containerRef.current) return;
      
      const containerRect = containerRef.current.getBoundingClientRect();
      const newLeftWidth = ((e.clientX - containerRect.left) / containerRect.width) * 100;
      
      // Clamp zwischen min und max
      const clampedWidth = Math.max(minLeftWidth, Math.min(maxLeftWidth, newLeftWidth));
      setLeftWidth(clampedWidth);
    };
    
    const handleMouseUp = () => {
      setIsDragging(false);
      // In LocalStorage speichern
      localStorage.setItem(storageKey, leftWidth.toString());
    };
    
    document.addEventListener('mousemove', handleMouseMove);
    document.addEventListener('mouseup', handleMouseUp);
    
    return () => {
      document.removeEventListener('mousemove', handleMouseMove);
      document.removeEventListener('mouseup', handleMouseUp);
    };
  }, [isDragging, leftWidth, storageKey, minLeftWidth, maxLeftWidth]);
  
  return {
    leftWidth,
    isDragging,
    handleMouseDown,
    setLeftWidth,
  };
};

3.1.1.2 CSS für Resizable Divider

In Playground.module.css hinzufügen:

/* Resizable Divider - passend zum Nyla Design */
.resizeDivider {
  width: 6px;
  cursor: col-resize;
  background-color: transparent;
  position: relative;
  flex-shrink: 0;
  z-index: 10;
  transition: background-color 0.15s ease;
}

.resizeDivider:hover,
.resizeDivider.dragging {
  background-color: var(--color-border-hover, rgba(255, 255, 255, 0.1));
}

.dividerHandle {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 4px;
  height: 40px;
  border-radius: 2px;
  background-color: var(--color-text-muted, rgba(255, 255, 255, 0.3));
  opacity: 0;
  transition: opacity 0.15s ease;
}

.resizeDivider:hover .dividerHandle,
.resizeDivider.dragging .dividerHandle {
  opacity: 1;
}

/* Verhindert Text-Selektion während Drag */
.chatColumns.dragging {
  user-select: none;
}

3.1.2 Neue Komponenten erstellen

Design-Prinzip: Alle neuen Komponenten müssen das bestehende Nyla Look & Feel nutzen!

  • Bestehende CSS-Variablen verwenden (aus :root oder Theme)
  • Vorhandene Button-/Input-Styles wiederverwenden
  • Keine neuen Farbschemata oder Typografie einführen
  • Komponenten-Styles in Module-CSS kapseln

Zu erstellen in frontend_nyla/src/components/Playground/:

Komponente Beschreibung Logik basiert auf Styles
WorkflowDashboard.tsx Hierarchisches Dashboard workflowUiRendererDashboard.js Nyla-Styles
UnifiedContentArea.tsx Kombinierte Logs + Messages workflowUiRenderer.js Nyla-Styles
UserInputArea.tsx Prompt-Auswahl, Input, Buttons part_workflow.html Bestehende Nyla Input-Styles
DataStatistics.tsx Sent/Received/Time/Tokens part_workflow.html Nyla-Styles
FilePreviewModal.tsx Datei-Vorschau Existiert teilweise Bestehende Modal-Styles
WorkflowControls.tsx Stop, Reset, Start Buttons workflowUiControls.js Bestehende Button-Styles

Wichtig: Diese Komponenten sollen die Logik aus dem alten frontend_agents übernehmen, aber das visuelle Design von Nyla beibehalten. Das heisst:

  • Hooks und State-Management aus frontend_agents portieren
  • CSS-Styles aus den bestehenden Nyla-Komponenten verwenden/erweitern

3.1.3 Dashboard Implementation

State-Struktur:

interface DashboardLogTree {
  rounds: Map<number, {
    operations: Map<string, Operation>;
    rootOperations: string[];
    expanded: boolean;
    isCompleted: boolean;
  }>;
  operations: Map<string, {
    logs: Map<string, LogEntry>;
    parentId: string | null;
    expanded: boolean;
    latestProgress: number | null;
    latestStatus: string | null;
    roundNumber: number;
  }>;
  logExpandedStates: Map<string, boolean>;
  currentRound: number | null;
}

Verwendung von existierendem Hook:

import { useDashboardLogTree } from '../../hooks/playground/useDashboardLogTree';

const {
  dashboardLogTree,
  processDashboardLogs,
  toggleRoundExpanded,
  toggleOperationExpanded,
  clearDashboard,
} = useDashboardLogTree();

3.1.4 Input-Bereich Implementation

Neue Elemente:

  • Prompt-Dropdown (aus usePrompts())
  • Workflow-Modus-Dropdown (aus /api/options/workflow.mode)
  • File-Upload mit Drag & Drop
  • Voice-Recording-Button
  • Stop/Reset/Start-Buttons

Phase 2-6: Data-Seiten (Workflows, Automations, Prompts, Files, Connections)

Design-Prinzip für alle Data-Seiten:

  • FormGeneratorTable und FormGeneratorForm verwenden (bereits Nyla-konform)
  • Bestehende Page-Header-Styles aus Admin-Seiten übernehmen
  • Keine neuen Layouts/Designs einführen
  • Nur fehlende Hooks/Logik ergänzen

Phase 2: Workflows Page (Priorität HOCH)

3.2.1 Hook erweitern

Datei: frontend_nyla/src/hooks/useWorkflows.ts

Erweitern um:

export const useWorkflowsAdmin = () => {
  const { data, loading, error, refetch } = useApi<WorkflowResponse>('/api/workflows/');
  
  // Attributes vom Backend
  const { data: attributesResponse } = useApi<AttributesResponse>('/api/attributes/ChatWorkflow');
  
  return {
    data: data?.items || [],
    attributes: attributesResponse?.attributes || [],
    columns: generateColumns(attributesResponse?.attributes),
    permissions: data?.permissions,
    pagination: data?.pagination,
    loading,
    error,
    refetch,
    // Operations
    handleDelete: async (id: string) => { /* ... */ },
    handleInlineUpdate: async (id: string, data: Partial<Workflow>) => { /* ... */ },
  };
};

3.2.2 Page umschreiben

Datei: frontend_nyla/src/pages/workflows/WorkflowsPage.tsx

Nach Pattern von AdminUsersPage.tsx:

import { FormGeneratorTable } from '../../components/FormGenerator';
import { useWorkflowsAdmin } from '../../hooks/useWorkflows';

export const WorkflowsPage: React.FC = () => {
  const {
    data: workflows,
    attributes,
    columns,
    permissions,
    pagination,
    loading,
    error,
    refetch,
    handleDelete,
    handleInlineUpdate,
  } = useWorkflowsAdmin();
  
  return (
    <div className={styles.page}>
      <PageHeader title="Workflows" onRefresh={refetch} />
      <FormGeneratorTable
        data={workflows}
        columns={columns}
        loading={loading}
        pagination={true}
        actionButtons={[
          { type: 'edit', onAction: handleContinue },
          { type: 'delete' },
        ]}
        customActions={[
          {
            id: 'continue',
            icon: <FaPlay />,
            onClick: handleContinue,
            title: 'Workflow fortsetzen',
          }
        ]}
        onDelete={handleDelete}
        hookData={{
          refetch,
          permissions,
          pagination,
          handleDelete,
          handleInlineUpdate,
        }}
      />
    </div>
  );
};

Phase 3: Automations Page

Analog zu Workflows Page:

  1. Hook erweitern: useAutomationsAdmin()
  2. Page umschreiben mit FormGeneratorTable
  3. Custom Actions: Execute, Toggle Active

Phase 4: Prompts Page

Spezifische Anpassungen:

  1. Hook erweitern: usePromptsAdmin()
  2. Page umschreiben mit FormGeneratorTable
  3. Create/Edit Modal mit FormGeneratorForm
  4. Content-Feld als Textarea

Phase 5: Files Page

Spezifische Anpassungen:

  1. Hook erweitern: useFilesAdmin()
  2. Page umschreiben mit FormGeneratorTable
  3. Custom Actions: Download, Preview
  4. Upload-Button mit UploadButton-Komponente

Phase 6: Connections Page

Spezifische Anpassungen:

  1. Hook erweitern: useConnectionsAdmin()
  2. Page umschreiben mit FormGeneratorTable
  3. Custom Actions: Connect, Refresh Token
  4. Provider-spezifische Buttons (Google, Microsoft)

4. Detaillierte Änderungsliste

4.1 Zu erstellende Dateien

frontend_nyla/src/
├── hooks/
│   └── useResizablePanels.ts          # NEU: Hook für resizable panels
├── components/
│   └── Playground/
│       ├── index.ts
│       ├── Playground.module.css       # NEU: Layout-Styles inkl. Divider
│       ├── WorkflowDashboard/
│       │   ├── index.ts
│       │   ├── WorkflowDashboard.tsx   # Logik aus workflowUiRendererDashboard.js
│       │   ├── WorkflowDashboard.module.css
│       │   ├── DashboardRound.tsx
│       │   ├── DashboardOperation.tsx
│       │   └── DashboardProgressBar.tsx
│       ├── UnifiedContentArea/
│       │   ├── index.ts
│       │   ├── UnifiedContentArea.tsx  # Logik aus workflowUiRenderer.js
│       │   ├── UnifiedContentArea.module.css
│       │   ├── MessageItem.tsx
│       │   └── LogItem.tsx
│       ├── UserInputArea/
│       │   ├── index.ts
│       │   ├── UserInputArea.tsx       # Logik aus workflow.js + part_workflow.html
│       │   ├── UserInputArea.module.css
│       │   ├── PromptSelector.tsx
│       │   ├── WorkflowModeSelector.tsx
│       │   └── VoiceRecordButton.tsx
│       ├── WorkflowControls/
│       │   ├── index.ts
│       │   ├── WorkflowControls.tsx    # Logik aus workflowUiControls.js
│       │   └── WorkflowControls.module.css
│       └── DataStatistics/
│           ├── index.ts
│           ├── DataStatistics.tsx
│           └── DataStatistics.module.css

Hinweis: Alle .module.css Dateien sollen bestehende Nyla CSS-Variablen nutzen und sich nahtlos ins bestehende Design einfügen!

4.2 Zu ändernde Dateien

Datei Änderung
pages/workflows/PlaygroundPage.tsx Kompletter Rewrite mit neuen Komponenten
pages/workflows/WorkflowsPage.tsx Umschreiben auf FormGeneratorTable
pages/workflows/AutomationsPage.tsx Umschreiben auf FormGeneratorTable
pages/basedata/PromptsPage.tsx Umschreiben auf FormGeneratorTable
pages/basedata/FilesPage.tsx Umschreiben auf FormGeneratorTable
pages/basedata/ConnectionsPage.tsx Umschreiben auf FormGeneratorTable
hooks/useWorkflows.ts Admin-Hook hinzufügen
hooks/useAutomations.ts Admin-Hook hinzufügen
hooks/usePrompts.ts Admin-Hook hinzufügen
hooks/useFiles.ts Admin-Hook hinzufügen
hooks/useConnections.ts Admin-Hook hinzufügen

4.3 CSS-Anpassungen

Neue CSS-Module:

  • Playground.module.css - Hauptlayout
  • WorkflowDashboard.module.css - Dashboard-Styles
  • UnifiedContentArea.module.css - Content-Styles

Bestehende zu aktualisieren:

  • WorkflowPages.module.css - Für alle Workflow-Seiten

5. API-Endpunkte (bereits vorhanden)

Endpunkt Verwendung
/api/workflows/ Liste, CRUD
/api/workflows/{id} Einzelner Workflow
/api/workflows/{id}/chat-data Unified Chat-Daten
/api/automations/ Liste, CRUD
/api/prompts/ Liste, CRUD
/api/files/ Liste, CRUD
/api/connections/ Liste, CRUD
/api/attributes/{EntityType} Attribute-Definitionen
/api/options/workflow.mode Workflow-Modus-Enum

6. Migrations-Checkliste

Chat Playground

  • useResizablePanels Hook erstellen
  • Resizable Spalten-Layout implementieren (mit Drag-Divider)
  • WorkflowDashboard-Komponente erstellen (Logik aus frontend_agents, Nyla-Styles)
  • UnifiedContentArea-Komponente erstellen (Logik aus frontend_agents, Nyla-Styles)
  • UserInputArea mit allen Features erstellen (Nyla Input-Styles)
  • DataStatistics-Komponente erstellen (Nyla-Styles)
  • Polling integrieren (bestehende Hooks nutzen)
  • File-Upload mit Drag & Drop (bestehende DragDropOverlay nutzen)
  • Voice-Recording integrieren
  • LocalStorage-Persistenz für Panel-Breite

Workflows Page

  • Hook useWorkflowsAdmin erstellen
  • Page auf FormGeneratorTable umstellen
  • Action Buttons konfigurieren
  • RBAC-Permissions implementieren

Automations Page

  • Hook useAutomationsAdmin erstellen
  • Page auf FormGeneratorTable umstellen
  • Toggle Active implementieren
  • Execute-Action implementieren

Prompts Page

  • Hook usePromptsAdmin erstellen
  • Page auf FormGeneratorTable umstellen
  • Create/Edit Modals mit FormGeneratorForm
  • Content-Textarea korrekt anzeigen

Files Page

  • Hook useFilesAdmin erstellen
  • Page auf FormGeneratorTable umstellen
  • Download/Preview Actions
  • Upload-Button integrieren

Connections Page

  • Hook useConnectionsAdmin erstellen
  • Page auf FormGeneratorTable umstellen
  • Connect/Refresh Actions
  • Provider-spezifische Buttons

7. Schätzung Aufwand

Phase Aufwand Priorität
Phase 1: Chat Playground Hoch KRITISCH
Phase 2: Workflows Page Mittel Hoch
Phase 3: Automations Page Mittel Hoch
Phase 4: Prompts Page Niedrig Mittel
Phase 5: Files Page Niedrig Mittel
Phase 6: Connections Page Niedrig Mittel

8. Anhang: Pattern-Referenz

Standard-Page-Struktur

import React, { useState, useMemo } from 'react';
import { FormGeneratorTable, FormGeneratorForm } from '../../components/FormGenerator';
import { useEntityAdmin, useEntityOperations } from '../../hooks/useEntity';
import styles from './Page.module.css';

interface Entity {
  id: string;
  [key: string]: any;
}

export const EntityPage: React.FC = () => {
  // Hooks
  const {
    data,
    attributes,
    columns,
    permissions,
    pagination,
    loading,
    error,
    refetch,
    fetchById,
    handleInlineUpdate,
    updateOptimistically,
  } = useEntityAdmin();
  
  const {
    handleCreate,
    handleUpdate,
    handleDelete,
  } = useEntityOperations();
  
  // State
  const [showCreateModal, setShowCreateModal] = useState(false);
  const [editingItem, setEditingItem] = useState<Entity | null>(null);
  
  // Permissions
  const canCreate = permissions?.create !== 'n';
  const canUpdate = permissions?.update !== 'n';
  const canDelete = permissions?.delete !== 'n';
  
  // Form Attributes
  const formAttributes = useMemo(() => {
    return (attributes || []).filter(attr => !['id'].includes(attr.name));
  }, [attributes]);
  
  // Handlers
  const handleEditClick = async (item: Entity) => {
    const fullItem = await fetchById(item.id);
    if (fullItem) setEditingItem(fullItem);
  };
  
  const handleCreateSubmit = async (data: Partial<Entity>) => {
    const result = await handleCreate(data);
    if (result.success) {
      setShowCreateModal(false);
      refetch();
    }
  };
  
  const handleEditSubmit = async (data: Partial<Entity>) => {
    if (!editingItem) return;
    const result = await handleUpdate(editingItem.id, data);
    if (result.success) {
      setEditingItem(null);
      refetch();
    }
  };
  
  // Render
  return (
    <div className={styles.page}>
      <PageHeader 
        title="Entities"
        onRefresh={refetch}
        onCreate={canCreate ? () => setShowCreateModal(true) : undefined}
      />
      
      <FormGeneratorTable
        data={data}
        columns={columns}
        loading={loading}
        pagination={true}
        actionButtons={[
          ...(canUpdate ? [{ type: 'edit', onAction: handleEditClick }] : []),
          ...(canDelete ? [{ type: 'delete' }] : []),
        ]}
        onDelete={handleDelete}
        hookData={{
          refetch,
          permissions,
          pagination,
          handleDelete,
          handleInlineUpdate,
          updateOptimistically,
        }}
      />
      
      {/* Create Modal */}
      {showCreateModal && (
        <Modal onClose={() => setShowCreateModal(false)}>
          <FormGeneratorForm
            attributes={formAttributes}
            mode="create"
            onSubmit={handleCreateSubmit}
            onCancel={() => setShowCreateModal(false)}
          />
        </Modal>
      )}
      
      {/* Edit Modal */}
      {editingItem && (
        <Modal onClose={() => setEditingItem(null)}>
          <FormGeneratorForm
            attributes={formAttributes}
            data={editingItem}
            mode="edit"
            onSubmit={handleEditSubmit}
            onCancel={() => setEditingItem(null)}
          />
        </Modal>
      )}
    </div>
  );
};

9. Design-Konsistenz-Checklist

Bei jeder Implementation prüfen:

  • CSS-Variablen: Werden bestehende Nyla CSS-Variablen verwendet? (Farben, Abstände, etc.)
  • Komponenten: Werden bestehende UiComponents wiederverwendet wo möglich?
  • Styles: Sind neue Styles konsistent mit bestehendem Nyla-Design?
  • Keine Neuerungen: Wurden keine neuen Design-Patterns eingeführt?
  • Dark/Light Mode: Funktioniert die Komponente in beiden Modi?
  • Responsive: Funktioniert die Komponente auf verschiedenen Bildschirmgrössen?

Dokumentation erstellt am 23. Januar 2026 Aktualisiert: Resizable Panels und Design-Prinzipien hinzugefügt