852 lines
26 KiB
Markdown
852 lines
26 KiB
Markdown
# 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
|
|
|
|
```tsx
|
|
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`
|
|
|
|
```typescript
|
|
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:**
|
|
|
|
```css
|
|
/* 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:**
|
|
```typescript
|
|
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:**
|
|
```typescript
|
|
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:
|
|
```typescript
|
|
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`:
|
|
|
|
```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
|
|
|
|
```tsx
|
|
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*
|