diff --git a/frontend/src/components/workflow/nodes/StartNode.jsx b/frontend/src/components/workflow/nodes/StartNode.jsx index 327475c..3a4907f 100644 --- a/frontend/src/components/workflow/nodes/StartNode.jsx +++ b/frontend/src/components/workflow/nodes/StartNode.jsx @@ -8,10 +8,11 @@ import { Handle, Position } from 'reactflow' * - selected: Boolean (Node ist ausgewählt) */ export function StartNode({ data, selected }) { + const title = (data.analysis_title || '').trim() return (
🚀
-
{data.label || 'Start'}
+
{title || data.label || 'Start'}
{/* Nur Source Handle (kein Target, da Einstiegspunkt) */} { const na = String(a).toLowerCase() @@ -87,7 +78,8 @@ function InsightCard({ ins, onDelete, defaultOpen=false, prompts=[] }) { // Find matching prompt to get display_name const prompt = prompts.find(p => p.slug === ins.scope) - const displayName = prompt?.display_name || SLUG_LABELS[ins.scope] || ins.scope + const displayName = + prompt?.display_name || prompt?.name || SLUG_LABELS[ins.scope] || ins.scope // Use already-parsed metadata const metadata = metadataRaw @@ -546,9 +538,10 @@ export default function Analysis() { {canUseAI && pipelinePrompts.length > 0 && ( <>

- Zuerst die Kategorie wählen (Chip-Leiste bzw. Seitenleiste). Alle Pipeline-Analysen - dieser Kategorie erscheinen im Detailbereich (rechts auf Desktop, darunter auf Mobil). - Kategorien kommen aus dem Feld „Kategorie“ beim jeweiligen Prompt im Admin. + Zuerst die Kategorie wählen (Chip-Leiste bzw. Seitenleiste). Alle{' '} + Pipeline- und Workflow-Auswertungen dieser Kategorie erscheinen im Detailbereich + (rechts auf Desktop, darunter auf Mobil). Bei Workflows legst du Kategorie, Titel und Kurztext in der{' '} + Start-Node des Workflow-Editors fest; bei Pipelines im Admin unter KI-Prompts.

@@ -649,9 +642,9 @@ export default function Analysis() { {canUseAI && pipelinePrompts.length === 0 && (
-

Keine aktiven Pipeline-Prompts verfügbar.

+

Keine aktiven Pipeline- oder Workflow-Auswertungen verfügbar.

- Erstelle Pipeline-Prompts im Admin-Bereich unter Admin → KI-Prompts. + Pipelines und Workflows werden im Admin unter KI-Prompts bzw. Workflow-Editor angelegt.

)} @@ -675,7 +668,10 @@ export default function Analysis() { onClick={() => setHistoryScopePick(scope)} aria-current={activeHistoryScope === scope ? 'page' : undefined} > - {prompts.find(pr => pr.slug === scope)?.display_name || SLUG_LABELS[scope] || scope} + {(() => { + const pr = prompts.find((p) => p.slug === scope) + return pr?.display_name || pr?.name || SLUG_LABELS[scope] || scope + })()} ({grouped[scope].length}) ))} diff --git a/frontend/src/pages/WorkflowEditorPage.jsx b/frontend/src/pages/WorkflowEditorPage.jsx index c85f23b..42a875a 100644 --- a/frontend/src/pages/WorkflowEditorPage.jsx +++ b/frontend/src/pages/WorkflowEditorPage.jsx @@ -22,6 +22,28 @@ import { InlineTemplateEditor } from '../components/workflow/panels/InlineTempla import { Toast } from '../components/Toast' import { ConfirmDialog } from '../components/ConfirmDialog' import '../styles/workflowEditor.css' +import { + ANALYSIS_CATEGORY_ORDER, + ANALYSIS_CATEGORY_LABELS, +} from '../config/analysisCategories' + +/** Aus Start-Node → Felder für Analyse-UI / ai_prompts (display_name, description, category) */ +function getWorkflowAnalysisPublishFields(nodes, fallbackName) { + const start = nodes.find((n) => n.type === 'start') + const d = start?.data || {} + const title = + String(d.analysis_title ?? '').trim() || + String(fallbackName ?? '').trim() || + 'Workflow' + const description = String(d.analysis_description ?? '').trim() + const category = + String(d.analysis_category ?? 'ganzheitlich').toLowerCase().trim() || 'ganzheitlich' + return { + display_name: title, + description: description || null, + category, + } +} // Node-Type Mapping const nodeTypes = { @@ -45,7 +67,6 @@ export default function WorkflowEditorPage() { const selectedNode = selectedNodeId ? nodes.find(n => n.id === selectedNodeId) : null const [currentPrompt, setCurrentPrompt] = useState(null) const [workflowName, setWorkflowName] = useState('Neuer Workflow') - const [workflowDescription, setWorkflowDescription] = useState('') const [validationErrors, setValidationErrors] = useState([]) const [validationWarnings, setValidationWarnings] = useState([]) const [loading, setLoading] = useState(false) @@ -103,16 +124,26 @@ export default function WorkflowEditorPage() { }, []) const handleAddNode = (nodeType) => { - const newNode = { + const base = { id: `node_${nodeIdCounter++}`, type: nodeType, position: { x: 250, y: 100 + nodes.length * 100 }, data: { - label: `${nodeType.charAt(0).toUpperCase() + nodeType.slice(1)} ${nodeIdCounter - 1}` + label: `${nodeType.charAt(0).toUpperCase() + nodeType.slice(1)} ${nodeIdCounter - 1}`, + }, + } + if (nodeType === 'start') { + base.data = { + label: 'Start', + analysis_title: + workflowName && workflowName !== 'Neuer Workflow' + ? workflowName + : '', + analysis_description: '', + analysis_category: 'ganzheitlich', } } - - setNodes((nds) => [...nds, newNode]) + setNodes((nds) => [...nds, base]) } const handleNodeUpdate = (nodeId, updates) => { @@ -152,13 +183,20 @@ export default function WorkflowEditorPage() { }) console.log('📊 Serialized graph_data:', graph_data) + const { display_name, description, category } = getWorkflowAnalysisPublishFields( + nodes, + workflowName + ) + if (currentPrompt) { // Update existing console.log('📝 Updating existing workflow:', currentPrompt.id) await api.updateUnifiedPrompt(currentPrompt.id, { type: 'workflow', name: workflowName, - description: workflowDescription, + display_name, + description: description ?? undefined, + category, graph_data }) setToast({ message: '✅ Workflow gespeichert!', type: 'success' }) @@ -168,7 +206,9 @@ export default function WorkflowEditorPage() { const result = await api.createUnifiedPrompt({ type: 'workflow', name: workflowName, - description: workflowDescription, + display_name, + description: description ?? undefined, + category, graph_data }) console.log('✅ Workflow created:', result) @@ -203,11 +243,30 @@ export default function WorkflowEditorPage() { const { nodes: loadedNodes, edges: loadedEdges } = deserializeFromWorkflowGraph(prompt.graph_data) console.log('🎯 Deserialized:', { nodes: loadedNodes, edges: loadedEdges }) - setNodes(loadedNodes) + const mergedNodes = loadedNodes.map((n) => { + if (n.type !== 'start') return n + const hasStoredTitle = Object.prototype.hasOwnProperty.call(n.data || {}, 'analysis_title') + return { + ...n, + data: { + label: n.data?.label || 'Start', + ...n.data, + analysis_title: hasStoredTitle + ? n.data.analysis_title + : (prompt.display_name || prompt.name || ''), + analysis_description: Object.prototype.hasOwnProperty.call(n.data || {}, 'analysis_description') + ? n.data.analysis_description + : (prompt.description || ''), + analysis_category: + (n.data?.analysis_category || prompt.category || 'ganzheitlich').toLowerCase(), + }, + } + }) + + setNodes(mergedNodes) setEdges(loadedEdges) setCurrentPrompt(prompt) setWorkflowName(prompt.name) - setWorkflowDescription(prompt.description || '') // nodeIdCounter aktualisieren const maxId = Math.max( @@ -242,7 +301,6 @@ export default function WorkflowEditorPage() { setEdges([]) setCurrentPrompt(null) setWorkflowName('Neuer Workflow') - setWorkflowDescription('') setSelectedNodeId(null) navigate('/workflow-editor/new') } @@ -342,7 +400,8 @@ export default function WorkflowEditorPage() { type="text" value={workflowName} onChange={(e) => setWorkflowName(e.target.value)} - placeholder="Workflow-Name" + placeholder="Interner Workflow-Name (Slug-Basis)" + title="Technischer Name in der Datenbank. Den sichtbaren Titel für die KI-Analyse setzt du in der Start-Node." style={{ flex: 1, padding: '8px', borderRadius: '4px', border: '1px solid var(--border)' }} /> @@ -495,9 +554,88 @@ export default function WorkflowEditorPage() {
)} + {/* Start-Node: Metadaten für KI-Analyse (wie Pipeline: display_name, description, category) */} + {selectedNode.type === 'start' && ( +
+

Anzeige in KI-Analyse

+

+ Titel, Kurzbeschreibung und Kategorie erscheinen auf der Analyse-Seite, im Verlauf und in der + Kategorie-Navigation – analog zu Pipeline-Prompts im Admin. +

+ + + handleNodeUpdate(selectedNode.id, { analysis_title: e.target.value }) + } + placeholder="z. B. Ganzheitliche Auswertung" + style={{ + width: '100%', + padding: '8px', + borderRadius: '4px', + border: '1px solid var(--border)', + background: 'var(--surface)', + color: 'var(--text1)', + fontSize: '14px', + marginBottom: 12, + }} + /> + +