diff --git a/configmap/dev/simple-workflow-editor-secrets-dev.yaml b/configmap/dev/veloflow-editor-secrets-dev.yaml similarity index 100% rename from configmap/dev/simple-workflow-editor-secrets-dev.yaml rename to configmap/dev/veloflow-editor-secrets-dev.yaml diff --git a/src/page/WorkflowsPage.tsx b/src/page/WorkflowsPage.tsx index d0a044cc861137990e77e124ccd2c07dd3fda6c8..cb056167238245c5c6914476923a4cbe4b95838d 100644 --- a/src/page/WorkflowsPage.tsx +++ b/src/page/WorkflowsPage.tsx @@ -1,4 +1,4 @@ -import {useCallback, useEffect, useMemo, useState} from "react"; +import {useCallback, useEffect, useMemo, useRef, useState} from "react"; import {WorkflowDto, WorkflowForm, emptyWorkflowForm} from "../types/WorkflowTypes"; import {workflowService} from "../services/workflowService"; import {useAppContext} from "../elements/AppContextProvider.tsx"; @@ -16,6 +16,7 @@ export default function WorkflowsPage() { const [form, setForm] = useState(emptyWorkflowForm); const [submitting, setSubmitting] = useState(false); const [isModalOpen, setIsModalOpen] = useState(false); + const fileInputRef = useRef(null); const load = useCallback(async () => { setLoading(true); @@ -60,6 +61,43 @@ export default function WorkflowsPage() { }; + const handleImportBpmn = async (e: React.ChangeEvent) => { + const file = e.target.files?.[0]; + if (!file) return; + + showLoader(); + try { + const bpmnXml = await file.text(); + await workflowService.importFromBpmn(bpmnXml); + await load(); + } catch (err: any) { + pushError(err?.message || "Import failed"); + } finally { + hideLoader(); + if (fileInputRef.current) fileInputRef.current.value = ""; + } + }; + + const handleExportBpmn = async (wf: WorkflowDto) => { + showLoader(); + try { + const bpmnXml = await workflowService.exportToBpmn(wf.id); + const blob = new Blob([bpmnXml], { type: "application/xml" }); + const url = URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + a.download = `${wf.name || 'workflow'}.bpmn`; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); + } catch (err: any) { + pushError(err?.message || "Export failed"); + } finally { + hideLoader(); + } + }; + const sortedItems = useMemo(() => [...items].sort((a, b) => b.id.localeCompare(a.id)), [items]); return ( @@ -71,6 +109,19 @@ export default function WorkflowsPage() { > Tasks + + + diff --git a/src/services/httpClient.ts b/src/services/httpClient.ts index e9d933db0ccd9475a7d112ddbc5c5db60ce5bbb4..e0ac7983030beef5c9a96038094610c9a5bcae93 100644 --- a/src/services/httpClient.ts +++ b/src/services/httpClient.ts @@ -17,7 +17,7 @@ export async function request(url: string, options: HttpOptions = { throw new Error(text || `HTTP ${response.status}`); } - if (!expectJson) return undefined as T; + if (!expectJson) return (await response.text()) as T; return (await response.json()) as T; } diff --git a/src/services/workflowService.ts b/src/services/workflowService.ts index 4ddb5e0190e5eaf3402a69cb1325fb2cc7e31e91..4b966e63b09b386369db2cf101d85da295ff8fdd 100644 --- a/src/services/workflowService.ts +++ b/src/services/workflowService.ts @@ -37,6 +37,20 @@ class WorkflowService { return await request(`${baseUrl}/${id}`); } + async importFromBpmn(bpmnXml: string): Promise { + return await request(`${baseUrl}/import/bpmn`, { + method: "POST", + headers: {"Content-Type": "application/json"}, + body: JSON.stringify(bpmnXml), + }); + } + + async exportToBpmn(id: string): Promise { + return await request(`${baseUrl}/${id}/export/bpmn`, { + expectJson: false, + }); + } + } export const workflowService = new WorkflowService(); \ No newline at end of file