88} from "react" ;
99import { v4 as uuidv4 } from "uuid" ;
1010import WorkflowListInner from "../WorkflowListInner" ;
11- import { aevatarAI } from "../../utils" ;
11+ import { aevatarAI , validateWorkflowData } from "../../utils" ;
1212import type {
1313 IAgentInfoDetail ,
1414 IWorkflowCoordinatorState ,
@@ -24,6 +24,7 @@ import Loading from "../../assets/svg/loading.svg?react";
2424import { IS_NULL_ID } from "../../constants" ;
2525import type { IWorkflowConfigurationState } from "../WorkflowConfiguration" ;
2626import { useToastLoading } from "../../hooks/useToastLoading" ;
27+ import dayjs from "dayjs" ;
2728
2829export interface IWorkflowListProps {
2930 className ?: string ;
@@ -260,10 +261,9 @@ export default forwardRef(function WorkflowList(
260261 }
261262 } , [ onDelete , toast ] ) ;
262263
263- const onDuplicateWorkflow = useCallback (
264- async ( workflow : IWorkflowCoordinatorState & IAgentInfoDetail ) => {
265- console . log ( "workflowId===" , workflow ) ;
266- const randomId = uuidv4 ( ) . replace ( / - / g, '' ) . substring ( 0 , 6 )
264+ const copyWorkflowViewData = useCallback (
265+ ( workflow : IWorkflowCoordinatorState & IAgentInfoDetail ) => {
266+ const randomId = uuidv4 ( ) . replace ( / - / g, "" ) . substring ( 0 , 6 ) ;
267267 const workflowName = `${ workflow . name } _${ randomId } ` ;
268268 const workflowNodeList = workflow . properties ?. workflowNodeList ?. map (
269269 ( item ) => {
@@ -282,6 +282,16 @@ export default forwardRef(function WorkflowList(
282282 name : workflowName ,
283283 } ,
284284 } ;
285+ return workflowViewData ;
286+ } ,
287+ [ ]
288+ ) ;
289+
290+ const onDuplicateWorkflow = useCallback (
291+ async ( workflow : IWorkflowCoordinatorState & IAgentInfoDetail ) => {
292+ console . log ( "workflowId===" , workflow ) ;
293+
294+ const workflowViewData = copyWorkflowViewData ( workflow ) ;
285295 const { dismiss } = toastLoading ( "Copying workflow" ) ;
286296
287297 try {
@@ -297,7 +307,120 @@ export default forwardRef(function WorkflowList(
297307 } ) ;
298308 }
299309 } ,
300- [ onEditWorkflow , toastLoading , toast ]
310+ [ onEditWorkflow , toastLoading , toast , copyWorkflowViewData ]
311+ ) ;
312+
313+ const onExportWorkflow = useCallback (
314+ async ( workflow : IWorkflowCoordinatorState & IAgentInfoDetail ) => {
315+ const workflowViewData = copyWorkflowViewData ( workflow ) ;
316+
317+ try {
318+ // Convert workflow data to JSON string
319+ const jsonString = JSON . stringify ( workflowViewData , null , 2 ) ;
320+
321+ // Create Blob and download link
322+ const blob = new Blob ( [ jsonString ] , { type : "application/json" } ) ;
323+ const url = URL . createObjectURL ( blob ) ;
324+ const link = document . createElement ( "a" ) ;
325+ link . href = url ;
326+
327+ // Generate filename with workflow name and timestamp
328+ const timestamp = dayjs ( ) . format ( "YYYYMMDDHHmmss" ) ;
329+ const filename = `${ workflow . name } _${ timestamp } .json` ;
330+ link . download = filename ;
331+
332+ // Trigger download
333+ document . body . appendChild ( link ) ;
334+ link . click ( ) ;
335+
336+ // Cleanup
337+ document . body . removeChild ( link ) ;
338+ URL . revokeObjectURL ( url ) ;
339+
340+ // Show success toast
341+ toast ( {
342+ description : `Workflow exported successfully as ${ filename } ` ,
343+ duration : 3000 ,
344+ } ) ;
345+ } catch ( error ) {
346+ toast ( {
347+ title : "error" ,
348+ description : handleErrorMessage ( error , "Failed to export workflow." ) ,
349+ duration : 3000 ,
350+ } ) ;
351+ }
352+ } ,
353+ [ copyWorkflowViewData , toast ]
354+ ) ;
355+
356+ const onImportWorkflow = useCallback (
357+ async ( file : File ) => {
358+ try {
359+ // Read and parse file
360+ const text = await file . text ( ) ;
361+ let workflowData : any ;
362+
363+ try {
364+ workflowData = JSON . parse ( text ) ;
365+ } catch {
366+ toast ( {
367+ title : "error" ,
368+ description : "Invalid JSON file format. Please ensure the file is valid JSON." ,
369+ duration : 3000 ,
370+ } ) ;
371+ return ;
372+ }
373+
374+ // Validate workflow data structure
375+ const validation = validateWorkflowData ( workflowData ) ;
376+ if ( ! validation . isValid ) {
377+ toast ( {
378+ title : "error" ,
379+ description : validation . error || "Invalid workflow file format." ,
380+ duration : 3000 ,
381+ } ) ;
382+ return ;
383+ }
384+
385+ // Show loading toast
386+ const { dismiss } = toastLoading ( "Importing workflow" ) ;
387+
388+ try {
389+ // Create workflow from imported data
390+ const result = await aevatarAI . services . workflow . createWorkflowViewData (
391+ workflowData as IWorkflowViewDataParams
392+ ) ;
393+ dismiss ( ) ;
394+
395+ // Show success toast and refresh list
396+ toast ( {
397+ description : `Workflow imported successfully as ${ workflowData . name } ` ,
398+ duration : 3000 ,
399+ } ) ;
400+
401+ // Refresh workflow list
402+ await sleep ( 500 ) ;
403+ getWorkflowsLoop ( ) ;
404+
405+ // Navigate to edit page if onEditWorkflow is provided
406+ if ( result . id ) onEditWorkflow ?.( result . id ) ;
407+ } catch ( error ) {
408+ dismiss ( ) ;
409+ toast ( {
410+ title : "error" ,
411+ description : handleErrorMessage ( error , "Failed to import workflow." ) ,
412+ duration : 3000 ,
413+ } ) ;
414+ }
415+ } catch ( error ) {
416+ toast ( {
417+ title : "error" ,
418+ description : handleErrorMessage ( error , "Failed to import workflow." ) ,
419+ duration : 3000 ,
420+ } ) ;
421+ }
422+ } ,
423+ [ toast , toastLoading , getWorkflowsLoop , onEditWorkflow ]
301424 ) ;
302425
303426 return (
@@ -310,6 +433,8 @@ export default forwardRef(function WorkflowList(
310433 onEditWorkflow = { onEditWorkflow }
311434 onDeleteWorkflow = { onDeleteWorkflow }
312435 onDuplicateWorkflow = { onDuplicateWorkflow }
436+ onExportWorkflow = { onExportWorkflow }
437+ onImportWorkflow = { onImportWorkflow }
313438 />
314439 < DeleteGAevatarConfirm
315440 open = { deleteOpen }
0 commit comments