/** * GraphicalEditorPage * * Thin wrapper: passes instance context to FlowEditor which now owns the full layout * including the Workspace panel (Chats/Dateien/Quellen) on the left. */ import React, { useState, useCallback, useEffect, useRef } from 'react'; import { useSearchParams } from 'react-router-dom'; 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 api from '../../../api'; 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 workflowIdFromUrl = searchParams.get('workflowId'); const [activeWorkflowId, setActiveWorkflowId] = useState(workflowIdFromUrl); const prevWorkflowIdRef = useRef(workflowIdFromUrl); useEffect(() => { if (workflowIdFromUrl && workflowIdFromUrl !== prevWorkflowIdRef.current) { prevWorkflowIdRef.current = workflowIdFromUrl; setActiveWorkflowId(workflowIdFromUrl); } }, [workflowIdFromUrl]); const { t, currentLanguage } = useLanguage(); const language = (currentLanguage?.slice(0, 2) || 'de') as string; const [pendingFiles, setPendingFiles] = useState([]); const [dataSources, setDataSources] = useState([]); const [featureDataSources, setFeatureDataSources] = useState([]); 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 (

{t('Keine Feature-Instanz gefunden')}

); } return (
); };