Workflow Analysen V1.1 #73
|
|
@ -8,10 +8,11 @@ import { Handle, Position } from 'reactflow'
|
||||||
* - selected: Boolean (Node ist ausgewählt)
|
* - selected: Boolean (Node ist ausgewählt)
|
||||||
*/
|
*/
|
||||||
export function StartNode({ data, selected }) {
|
export function StartNode({ data, selected }) {
|
||||||
|
const title = (data.analysis_title || '').trim()
|
||||||
return (
|
return (
|
||||||
<div className={`workflow-node start-node ${selected ? 'selected' : ''}`}>
|
<div className={`workflow-node start-node ${selected ? 'selected' : ''}`}>
|
||||||
<div className="node-icon">🚀</div>
|
<div className="node-icon">🚀</div>
|
||||||
<div className="node-label">{data.label || 'Start'}</div>
|
<div className="node-label">{title || data.label || 'Start'}</div>
|
||||||
|
|
||||||
{/* Nur Source Handle (kein Target, da Einstiegspunkt) */}
|
{/* Nur Source Handle (kein Target, da Einstiegspunkt) */}
|
||||||
<Handle
|
<Handle
|
||||||
|
|
|
||||||
21
frontend/src/config/analysisCategories.js
Normal file
21
frontend/src/config/analysisCategories.js
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
/** DB `ai_prompts.category` – Reihenfolge der Gruppen in der KI-Analyse-Navigation */
|
||||||
|
|
||||||
|
export const ANALYSIS_CATEGORY_ORDER = [
|
||||||
|
'körper',
|
||||||
|
'ernährung',
|
||||||
|
'training',
|
||||||
|
'schlaf',
|
||||||
|
'vitalwerte',
|
||||||
|
'ziele',
|
||||||
|
'ganzheitlich',
|
||||||
|
]
|
||||||
|
|
||||||
|
export const ANALYSIS_CATEGORY_LABELS = {
|
||||||
|
körper: 'Körper',
|
||||||
|
ernährung: 'Ernährung',
|
||||||
|
training: 'Training',
|
||||||
|
schlaf: 'Schlaf',
|
||||||
|
vitalwerte: 'Vitalwerte',
|
||||||
|
ziele: 'Ziele',
|
||||||
|
ganzheitlich: 'Ganzheitlich',
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,10 @@ import { Brain, Trash2, ChevronDown, ChevronUp } from 'lucide-react'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import { api } from '../utils/api'
|
import { api } from '../utils/api'
|
||||||
import { getWorkflowDisplayContent } from '../utils/workflowDisplay'
|
import { getWorkflowDisplayContent } from '../utils/workflowDisplay'
|
||||||
|
import {
|
||||||
|
ANALYSIS_CATEGORY_ORDER,
|
||||||
|
ANALYSIS_CATEGORY_LABELS,
|
||||||
|
} from '../config/analysisCategories'
|
||||||
import { useAuth } from '../context/AuthContext'
|
import { useAuth } from '../context/AuthContext'
|
||||||
import Markdown from '../utils/Markdown'
|
import Markdown from '../utils/Markdown'
|
||||||
import UsageBadge from '../components/UsageBadge'
|
import UsageBadge from '../components/UsageBadge'
|
||||||
|
|
@ -15,19 +19,6 @@ const SLUG_LABELS = {
|
||||||
pipeline: '🔬 Mehrstufige Gesamtanalyse'
|
pipeline: '🔬 Mehrstufige Gesamtanalyse'
|
||||||
}
|
}
|
||||||
|
|
||||||
/** DB `ai_prompts.category` – Reihenfolge der Gruppen in der Analyse-Navigation */
|
|
||||||
const ANALYSIS_CATEGORY_ORDER = ['körper', 'ernährung', 'training', 'schlaf', 'vitalwerte', 'ziele', 'ganzheitlich']
|
|
||||||
|
|
||||||
const ANALYSIS_CATEGORY_LABELS = {
|
|
||||||
körper: 'Körper',
|
|
||||||
ernährung: 'Ernährung',
|
|
||||||
training: 'Training',
|
|
||||||
schlaf: 'Schlaf',
|
|
||||||
vitalwerte: 'Vitalwerte',
|
|
||||||
ziele: 'Ziele',
|
|
||||||
ganzheitlich: 'Ganzheitlich',
|
|
||||||
}
|
|
||||||
|
|
||||||
function sortAnalysisCategoryKeys(keys) {
|
function sortAnalysisCategoryKeys(keys) {
|
||||||
return [...keys].sort((a, b) => {
|
return [...keys].sort((a, b) => {
|
||||||
const na = String(a).toLowerCase()
|
const na = String(a).toLowerCase()
|
||||||
|
|
@ -87,7 +78,8 @@ function InsightCard({ ins, onDelete, defaultOpen=false, prompts=[] }) {
|
||||||
|
|
||||||
// Find matching prompt to get display_name
|
// Find matching prompt to get display_name
|
||||||
const prompt = prompts.find(p => p.slug === ins.scope)
|
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
|
// Use already-parsed metadata
|
||||||
const metadata = metadataRaw
|
const metadata = metadataRaw
|
||||||
|
|
@ -546,9 +538,10 @@ export default function Analysis() {
|
||||||
{canUseAI && pipelinePrompts.length > 0 && (
|
{canUseAI && pipelinePrompts.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<p style={{fontSize:13,color:'var(--text2)',marginBottom:14,lineHeight:1.6}}>
|
<p style={{fontSize:13,color:'var(--text2)',marginBottom:14,lineHeight:1.6}}>
|
||||||
Zuerst die <strong>Kategorie</strong> wählen (Chip-Leiste bzw. Seitenleiste). Alle Pipeline-Analysen
|
Zuerst die <strong>Kategorie</strong> wählen (Chip-Leiste bzw. Seitenleiste). Alle{' '}
|
||||||
dieser Kategorie erscheinen im Detailbereich (rechts auf Desktop, darunter auf Mobil).
|
<strong>Pipeline- und Workflow-Auswertungen</strong> dieser Kategorie erscheinen im Detailbereich
|
||||||
Kategorien kommen aus dem Feld „Kategorie“ beim jeweiligen Prompt im Admin.
|
(rechts auf Desktop, darunter auf Mobil). Bei Workflows legst du Kategorie, Titel und Kurztext in der{' '}
|
||||||
|
<strong>Start-Node</strong> des Workflow-Editors fest; bei Pipelines im Admin unter KI-Prompts.
|
||||||
</p>
|
</p>
|
||||||
<div className="analysis-split">
|
<div className="analysis-split">
|
||||||
<div className="analysis-split__nav-wrap">
|
<div className="analysis-split__nav-wrap">
|
||||||
|
|
@ -649,9 +642,9 @@ export default function Analysis() {
|
||||||
|
|
||||||
{canUseAI && pipelinePrompts.length === 0 && (
|
{canUseAI && pipelinePrompts.length === 0 && (
|
||||||
<div className="empty-state">
|
<div className="empty-state">
|
||||||
<p>Keine aktiven Pipeline-Prompts verfügbar.</p>
|
<p>Keine aktiven Pipeline- oder Workflow-Auswertungen verfügbar.</p>
|
||||||
<p style={{fontSize:12,color:'var(--text3)',marginTop:8}}>
|
<p style={{fontSize:12,color:'var(--text3)',marginTop:8}}>
|
||||||
Erstelle Pipeline-Prompts im Admin-Bereich unter Admin → KI-Prompts.
|
Pipelines und Workflows werden im Admin unter KI-Prompts bzw. Workflow-Editor angelegt.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -675,7 +668,10 @@ export default function Analysis() {
|
||||||
onClick={() => setHistoryScopePick(scope)}
|
onClick={() => setHistoryScopePick(scope)}
|
||||||
aria-current={activeHistoryScope === scope ? 'page' : undefined}
|
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
|
||||||
|
})()}
|
||||||
<span className="muted" style={{ fontSize: 12 }}> ({grouped[scope].length})</span>
|
<span className="muted" style={{ fontSize: 12 }}> ({grouped[scope].length})</span>
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,28 @@ import { InlineTemplateEditor } from '../components/workflow/panels/InlineTempla
|
||||||
import { Toast } from '../components/Toast'
|
import { Toast } from '../components/Toast'
|
||||||
import { ConfirmDialog } from '../components/ConfirmDialog'
|
import { ConfirmDialog } from '../components/ConfirmDialog'
|
||||||
import '../styles/workflowEditor.css'
|
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
|
// Node-Type Mapping
|
||||||
const nodeTypes = {
|
const nodeTypes = {
|
||||||
|
|
@ -45,7 +67,6 @@ export default function WorkflowEditorPage() {
|
||||||
const selectedNode = selectedNodeId ? nodes.find(n => n.id === selectedNodeId) : null
|
const selectedNode = selectedNodeId ? nodes.find(n => n.id === selectedNodeId) : null
|
||||||
const [currentPrompt, setCurrentPrompt] = useState(null)
|
const [currentPrompt, setCurrentPrompt] = useState(null)
|
||||||
const [workflowName, setWorkflowName] = useState('Neuer Workflow')
|
const [workflowName, setWorkflowName] = useState('Neuer Workflow')
|
||||||
const [workflowDescription, setWorkflowDescription] = useState('')
|
|
||||||
const [validationErrors, setValidationErrors] = useState([])
|
const [validationErrors, setValidationErrors] = useState([])
|
||||||
const [validationWarnings, setValidationWarnings] = useState([])
|
const [validationWarnings, setValidationWarnings] = useState([])
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
|
|
@ -103,16 +124,26 @@ export default function WorkflowEditorPage() {
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const handleAddNode = (nodeType) => {
|
const handleAddNode = (nodeType) => {
|
||||||
const newNode = {
|
const base = {
|
||||||
id: `node_${nodeIdCounter++}`,
|
id: `node_${nodeIdCounter++}`,
|
||||||
type: nodeType,
|
type: nodeType,
|
||||||
position: { x: 250, y: 100 + nodes.length * 100 },
|
position: { x: 250, y: 100 + nodes.length * 100 },
|
||||||
data: {
|
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, base])
|
||||||
setNodes((nds) => [...nds, newNode])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleNodeUpdate = (nodeId, updates) => {
|
const handleNodeUpdate = (nodeId, updates) => {
|
||||||
|
|
@ -152,13 +183,20 @@ export default function WorkflowEditorPage() {
|
||||||
})
|
})
|
||||||
console.log('📊 Serialized graph_data:', graph_data)
|
console.log('📊 Serialized graph_data:', graph_data)
|
||||||
|
|
||||||
|
const { display_name, description, category } = getWorkflowAnalysisPublishFields(
|
||||||
|
nodes,
|
||||||
|
workflowName
|
||||||
|
)
|
||||||
|
|
||||||
if (currentPrompt) {
|
if (currentPrompt) {
|
||||||
// Update existing
|
// Update existing
|
||||||
console.log('📝 Updating existing workflow:', currentPrompt.id)
|
console.log('📝 Updating existing workflow:', currentPrompt.id)
|
||||||
await api.updateUnifiedPrompt(currentPrompt.id, {
|
await api.updateUnifiedPrompt(currentPrompt.id, {
|
||||||
type: 'workflow',
|
type: 'workflow',
|
||||||
name: workflowName,
|
name: workflowName,
|
||||||
description: workflowDescription,
|
display_name,
|
||||||
|
description: description ?? undefined,
|
||||||
|
category,
|
||||||
graph_data
|
graph_data
|
||||||
})
|
})
|
||||||
setToast({ message: '✅ Workflow gespeichert!', type: 'success' })
|
setToast({ message: '✅ Workflow gespeichert!', type: 'success' })
|
||||||
|
|
@ -168,7 +206,9 @@ export default function WorkflowEditorPage() {
|
||||||
const result = await api.createUnifiedPrompt({
|
const result = await api.createUnifiedPrompt({
|
||||||
type: 'workflow',
|
type: 'workflow',
|
||||||
name: workflowName,
|
name: workflowName,
|
||||||
description: workflowDescription,
|
display_name,
|
||||||
|
description: description ?? undefined,
|
||||||
|
category,
|
||||||
graph_data
|
graph_data
|
||||||
})
|
})
|
||||||
console.log('✅ Workflow created:', result)
|
console.log('✅ Workflow created:', result)
|
||||||
|
|
@ -203,11 +243,30 @@ export default function WorkflowEditorPage() {
|
||||||
const { nodes: loadedNodes, edges: loadedEdges } = deserializeFromWorkflowGraph(prompt.graph_data)
|
const { nodes: loadedNodes, edges: loadedEdges } = deserializeFromWorkflowGraph(prompt.graph_data)
|
||||||
console.log('🎯 Deserialized:', { nodes: loadedNodes, edges: loadedEdges })
|
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)
|
setEdges(loadedEdges)
|
||||||
setCurrentPrompt(prompt)
|
setCurrentPrompt(prompt)
|
||||||
setWorkflowName(prompt.name)
|
setWorkflowName(prompt.name)
|
||||||
setWorkflowDescription(prompt.description || '')
|
|
||||||
|
|
||||||
// nodeIdCounter aktualisieren
|
// nodeIdCounter aktualisieren
|
||||||
const maxId = Math.max(
|
const maxId = Math.max(
|
||||||
|
|
@ -242,7 +301,6 @@ export default function WorkflowEditorPage() {
|
||||||
setEdges([])
|
setEdges([])
|
||||||
setCurrentPrompt(null)
|
setCurrentPrompt(null)
|
||||||
setWorkflowName('Neuer Workflow')
|
setWorkflowName('Neuer Workflow')
|
||||||
setWorkflowDescription('')
|
|
||||||
setSelectedNodeId(null)
|
setSelectedNodeId(null)
|
||||||
navigate('/workflow-editor/new')
|
navigate('/workflow-editor/new')
|
||||||
}
|
}
|
||||||
|
|
@ -342,7 +400,8 @@ export default function WorkflowEditorPage() {
|
||||||
type="text"
|
type="text"
|
||||||
value={workflowName}
|
value={workflowName}
|
||||||
onChange={(e) => setWorkflowName(e.target.value)}
|
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)' }}
|
style={{ flex: 1, padding: '8px', borderRadius: '4px', border: '1px solid var(--border)' }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
@ -495,9 +554,88 @@ export default function WorkflowEditorPage() {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Start-Node: Metadaten für KI-Analyse (wie Pipeline: display_name, description, category) */}
|
||||||
|
{selectedNode.type === 'start' && (
|
||||||
|
<div className="config-section" style={{ marginBottom: 20 }}>
|
||||||
|
<h3 style={{ margin: '0 0 10px', fontSize: 15 }}>Anzeige in KI-Analyse</h3>
|
||||||
|
<p style={{ fontSize: 12, color: 'var(--text3)', lineHeight: 1.5, marginBottom: 12 }}>
|
||||||
|
Titel, Kurzbeschreibung und Kategorie erscheinen auf der Analyse-Seite, im Verlauf und in der
|
||||||
|
Kategorie-Navigation – analog zu Pipeline-Prompts im Admin.
|
||||||
|
</p>
|
||||||
|
<label style={{ display: 'block', marginBottom: 6, fontWeight: 600, fontSize: 13 }}>
|
||||||
|
Titel (sichtbar für Nutzer)
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={selectedNode.data.analysis_title ?? ''}
|
||||||
|
onChange={(e) =>
|
||||||
|
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,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<label style={{ display: 'block', marginBottom: 6, fontWeight: 600, fontSize: 13 }}>
|
||||||
|
Kurzbeschreibung (optional)
|
||||||
|
</label>
|
||||||
|
<textarea
|
||||||
|
value={selectedNode.data.analysis_description ?? ''}
|
||||||
|
onChange={(e) =>
|
||||||
|
handleNodeUpdate(selectedNode.id, { analysis_description: e.target.value })
|
||||||
|
}
|
||||||
|
placeholder="Ein Satz, worum es bei dieser Auswertung geht …"
|
||||||
|
rows={4}
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
padding: '8px',
|
||||||
|
borderRadius: '4px',
|
||||||
|
border: '1px solid var(--border)',
|
||||||
|
background: 'var(--surface)',
|
||||||
|
color: 'var(--text1)',
|
||||||
|
fontSize: '13px',
|
||||||
|
lineHeight: 1.5,
|
||||||
|
marginBottom: 12,
|
||||||
|
resize: 'vertical',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<label style={{ display: 'block', marginBottom: 6, fontWeight: 600, fontSize: 13 }}>
|
||||||
|
Kategorie
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
value={selectedNode.data.analysis_category || 'ganzheitlich'}
|
||||||
|
onChange={(e) =>
|
||||||
|
handleNodeUpdate(selectedNode.id, { analysis_category: e.target.value })
|
||||||
|
}
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
padding: '8px',
|
||||||
|
borderRadius: '4px',
|
||||||
|
border: '1px solid var(--border)',
|
||||||
|
background: 'var(--surface)',
|
||||||
|
color: 'var(--text1)',
|
||||||
|
fontSize: '14px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{ANALYSIS_CATEGORY_ORDER.map((key) => (
|
||||||
|
<option key={key} value={key}>
|
||||||
|
{ANALYSIS_CATEGORY_LABELS[key] || key}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Basis-Konfiguration */}
|
{/* Basis-Konfiguration */}
|
||||||
<div className="config-section">
|
<div className="config-section">
|
||||||
<label>Node-Name</label>
|
<label>Node-Name {selectedNode.type === 'start' && '(auf dem Canvas)'}</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={selectedNode.data.label || ''}
|
value={selectedNode.data.label || ''}
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,12 @@ export function serializeToWorkflowGraph(nodes, edges, metadata = {}) {
|
||||||
...(node.type === 'end' && {
|
...(node.type === 'end' && {
|
||||||
output_mode: node.data.output_mode || 'auto',
|
output_mode: node.data.output_mode || 'auto',
|
||||||
template: node.data.template || null
|
template: node.data.template || null
|
||||||
|
}),
|
||||||
|
|
||||||
|
...(node.type === 'start' && {
|
||||||
|
analysis_title: node.data.analysis_title || '',
|
||||||
|
analysis_description: node.data.analysis_description || '',
|
||||||
|
analysis_category: node.data.analysis_category || 'ganzheitlich',
|
||||||
})
|
})
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
|
@ -105,6 +111,12 @@ export function deserializeFromWorkflowGraph(jsonbData) {
|
||||||
...(node.type === 'end' && {
|
...(node.type === 'end' && {
|
||||||
output_mode: node.output_mode || 'auto',
|
output_mode: node.output_mode || 'auto',
|
||||||
template: node.template || null
|
template: node.template || null
|
||||||
|
}),
|
||||||
|
|
||||||
|
...(node.type === 'start' && {
|
||||||
|
analysis_title: node.analysis_title || '',
|
||||||
|
analysis_description: node.analysis_description || '',
|
||||||
|
analysis_category: node.analysis_category || 'ganzheitlich',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user