/** * GraphicalEditorPage * * Layout: [UDB sidebar (collapsible)] [FlowEditor (flex)] [Chat/Tracing (inside FlowEditor)] * UDB provides access to Files & Sources while configuring nodes. * AI Chat and Tracing panels are managed by the FlowEditor's CanvasHeader. * * File/Source attachment UX mirrors the Workspace: * - Files: click in UDB FilesTab → added as pendingFile chip in chat input * - Data Sources: 🔗 picker button in chat input (loaded from UDB API) */ import React, { useState, useMemo, useRef, useCallback, useEffect } from 'react'; import { useSearchParams } from 'react-router-dom'; import { FaDatabase, FaChevronLeft } from 'react-icons/fa'; import { useInstanceId, useMandateId } from '../../../hooks/useCurrentInstance'; import { useLanguage } from '../../../providers/language/LanguageContext'; import { Automation2FlowEditor as FlowEditor } from '../../../components/FlowEditor'; import type { PendingFile, EditorDataSource, EditorFeatureDataSource } from '../../../components/FlowEditor'; import { UnifiedDataBar } from '../../../components/UnifiedDataBar'; import type { UdbContext, UdbTab } from '../../../components/UnifiedDataBar'; import api from '../../../api'; import styles from '../../FeatureView.module.css'; interface GraphicalEditorPageProps { persistentInstanceId?: string; persistentMandateId?: string; } export const GraphicalEditorPage: React.FC = ({ persistentInstanceId, persistentMandateId, }) => { const urlInstanceId = useInstanceId(); const urlMandateId = useMandateId(); const instanceId = persistentInstanceId || urlInstanceId; const mandateId = persistentMandateId || urlMandateId; const [searchParams] = useSearchParams(); const initialWorkflowIdRef = useRef(searchParams.get('workflowId')); const { currentLanguage } = useLanguage(); const language = (currentLanguage?.slice(0, 2) || 'de') as string; const [udbTab, setUdbTab] = useState('files'); const [udbOpen, setUdbOpen] = useState(true); const [pendingFiles, setPendingFiles] = useState([]); const [dataSources, setDataSources] = useState([]); const [featureDataSources, setFeatureDataSources] = useState([]); const udbContext: UdbContext = useMemo(() => ({ instanceId: instanceId || '', mandateId: mandateId || '', featureInstanceId: instanceId || '', }), [instanceId, mandateId]); useEffect(() => { if (!instanceId) return; api.get(`/api/workspace/${instanceId}/datasources`) .then(res => { const list = (res.data.dataSources || res.data || []).map((d: any) => ({ id: d.id, label: d.label || d.path || d.id, path: d.path, sourceType: d.sourceType, })); setDataSources(list); }) .catch(() => setDataSources([])); }, [instanceId]); useEffect(() => { if (!instanceId) return; api.get(`/api/workspace/${instanceId}/feature-datasources`) .then(res => { const list = (res.data.featureDataSources || res.data || []).map((d: any) => ({ id: d.id, featureInstanceId: d.featureInstanceId, featureCode: d.featureCode, tableName: d.tableName, label: d.label || d.tableName, })); setFeatureDataSources(list); }) .catch(() => setFeatureDataSources([])); }, [instanceId]); const _handleFileSelect = useCallback((fileId: string, fileName?: string) => { setPendingFiles(prev => { if (prev.some(f => f.fileId === fileId)) return prev; return [...prev, { fileId, fileName: fileName || fileId.slice(0, 12) }]; }); }, []); const _handleRemovePendingFile = useCallback((fileId: string) => { setPendingFiles(prev => prev.filter(f => f.fileId !== fileId)); }, []); const _handleSourcesChanged = useCallback(() => { if (!instanceId) return; api.get(`/api/workspace/${instanceId}/datasources`) .then(res => { setDataSources((res.data.dataSources || res.data || []).map((d: any) => ({ id: d.id, label: d.label || d.path || d.id, path: d.path, sourceType: d.sourceType, }))); }) .catch(() => {}); api.get(`/api/workspace/${instanceId}/feature-datasources`) .then(res => { setFeatureDataSources((res.data.featureDataSources || res.data || []).map((d: any) => ({ id: d.id, featureInstanceId: d.featureInstanceId, featureCode: d.featureCode, tableName: d.tableName, label: d.label || d.tableName, }))); }) .catch(() => {}); }, [instanceId]); if (!instanceId) { return (

Graphical Editor

Keine Feature-Instanz gefunden.

); } return (
{/* UDB Sidebar */} {udbOpen ? (
Daten
) : ( )} {/* FlowEditor */}
); };