'use client' import type { MouseEventHandler } from 'react' import type { DSLImportWarning } from '@/models/app' import { Button } from '@langgenius/dify-ui/button' import { Dialog, DialogContent } from '@langgenius/dify-ui/dialog' import { toast } from '@langgenius/dify-ui/toast' import { RiAlertFill, RiCloseLine, RiFileDownloadLine } from '@remixicon/react' import { memo, useCallback, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import Uploader from '@/app/components/app/create-from-dsl-modal/uploader' import { useStore as useAppStore } from '@/app/components/app/store' import { usePluginDependencies } from '@/app/components/workflow/plugin-dependency/hooks' import { useEventEmitterContextContext } from '@/context/event-emitter' import { DSLImportMode, DSLImportStatus } from '@/models/app' import { importDSL, importDSLConfirm } from '@/service/apps' import { fetchWorkflowDraft } from '@/service/workflow' import { collaborationManager } from './collaboration/core/collaboration-manager' import { WORKFLOW_DATA_UPDATE } from './constants' import { getImportNotificationPayload, isImportCompleted, normalizeWorkflowFeatures, validateDSLContent, } from './update-dsl-modal.helpers' import { initialEdges, initialNodes } from './utils' type UpdateDSLModalProps = { onCancel: () => void onBackup: () => void onImport?: () => void } const UpdateDSLModal = ({ onCancel, onBackup, onImport }: UpdateDSLModalProps) => { const { t } = useTranslation() const appDetail = useAppStore((s) => s.appDetail) const [currentFile, setDSLFile] = useState() const [fileContent, setFileContent] = useState() const [loading, setLoading] = useState(false) const { eventEmitter } = useEventEmitterContextContext() const [show, setShow] = useState(true) const [showErrorModal, setShowErrorModal] = useState(false) const [versions, setVersions] = useState<{ importedVersion: string; systemVersion: string }>() const [importId, setImportId] = useState() const { handleCheckPluginDependencies } = usePluginDependencies() const readFile = (file: File) => { const reader = new FileReader() reader.onload = function (event) { const content = event.target?.result setFileContent(content as string) } reader.readAsText(file) } const handleFile = (file?: File) => { setDSLFile(file) if (file) readFile(file) if (!file) setFileContent('') } const handleWorkflowUpdate = useCallback( async (app_id: string) => { const { graph, features, hash, conversation_variables, environment_variables } = await fetchWorkflowDraft(`/apps/${app_id}/workflows/draft`) const { nodes, edges, viewport } = graph eventEmitter?.emit({ type: WORKFLOW_DATA_UPDATE, payload: { nodes: initialNodes(nodes, edges), edges: initialEdges(edges, nodes), viewport, features: normalizeWorkflowFeatures(features), hash, conversation_variables: conversation_variables || [], environment_variables: environment_variables || [], }, } as any) }, [eventEmitter], ) const isCreatingRef = useRef(false) const handleCompletedImport = useCallback( async (status: DSLImportStatus, appId?: string, warnings: DSLImportWarning[] = []) => { if (!appId) { toast.error(t(($) => $['common.importFailure'], { ns: 'workflow' })) return } await handleWorkflowUpdate(appId) collaborationManager.emitWorkflowUpdate(appId) onImport?.() const payload = getImportNotificationPayload(status, t, warnings) toast[payload.type]( payload.message, payload.children ? { description: payload.children } : undefined, ) await handleCheckPluginDependencies(appId) setLoading(false) onCancel() }, [handleCheckPluginDependencies, handleWorkflowUpdate, onCancel, onImport, t], ) const handlePendingImport = useCallback( (id: string, importedVersion?: string | null, currentVersion?: string | null) => { setShow(false) setTimeout(() => { setShowErrorModal(true) }, 300) setVersions({ importedVersion: importedVersion ?? '', systemVersion: currentVersion ?? '', }) setImportId(id) }, [], ) const handleImport: MouseEventHandler = useCallback(async () => { if (isCreatingRef.current) return isCreatingRef.current = true if (!currentFile) { isCreatingRef.current = false return } try { if (appDetail && fileContent && validateDSLContent(fileContent, appDetail.mode)) { setLoading(true) const response = await importDSL({ mode: DSLImportMode.YAML_CONTENT, yaml_content: fileContent, app_id: appDetail.id, }) const { id, status, app_id, imported_dsl_version, current_dsl_version, warnings } = response if (isImportCompleted(status)) { await handleCompletedImport(status, app_id, warnings) } else if (status === DSLImportStatus.PENDING) { handlePendingImport(id, imported_dsl_version, current_dsl_version) } else { setLoading(false) toast.error(t(($) => $['common.importFailure'], { ns: 'workflow' })) } } else if (fileContent) { toast.error(t(($) => $['common.importFailure'], { ns: 'workflow' })) } } catch { setLoading(false) toast.error(t(($) => $['common.importFailure'], { ns: 'workflow' })) } isCreatingRef.current = false }, [currentFile, fileContent, t, appDetail, handleCompletedImport, handlePendingImport]) const onUpdateDSLConfirm: MouseEventHandler = async () => { try { if (!importId) return const response = await importDSLConfirm({ import_id: importId, }) const { status, app_id, warnings } = response if (isImportCompleted(status)) { await handleCompletedImport(status, app_id, warnings) } else if (status === DSLImportStatus.FAILED) { setLoading(false) toast.error(t(($) => $['common.importFailure'], { ns: 'workflow' })) } } catch { setLoading(false) toast.error(t(($) => $['common.importFailure'], { ns: 'workflow' })) } } return ( <> { if (!open) onCancel() }} >
{t(($) => $.importApp, { ns: 'app' })}
{t(($) => $['common.importDSLTip'], { ns: 'workflow' })}
{t(($) => $['common.chooseDSL'], { ns: 'workflow' })}
{ if (!open) setShowErrorModal(false) }} >
{t(($) => $['newApp.appCreateDSLErrorTitle'], { ns: 'app' })}
{t(($) => $['newApp.appCreateDSLErrorPart1'], { ns: 'app' })}
{t(($) => $['newApp.appCreateDSLErrorPart2'], { ns: 'app' })}

{t(($) => $['newApp.appCreateDSLErrorPart3'], { ns: 'app' })} {versions?.importedVersion}
{t(($) => $['newApp.appCreateDSLErrorPart4'], { ns: 'app' })} {versions?.systemVersion}
) } export default memo(UpdateDSLModal)