'use client' import type { MouseEventHandler } from 'react' import { memo, useCallback, useRef, useState, } from 'react' import { useContext } from 'use-context-selector' import { useTranslation } from 'react-i18next' import { RiAlertFill, RiCloseLine, RiFileDownloadLine, } from '@remixicon/react' import { WORKFLOW_DATA_UPDATE } from './constants' import { SupportUploadFileTypes, } from './types' import { initialEdges, initialNodes, } from './utils' import { importDSL, importDSLConfirm, } from '@/service/apps' import { fetchWorkflowDraft } from '@/service/workflow' import { DSLImportMode, DSLImportStatus, } from '@/models/app' import Uploader from '@/app/components/app/create-from-dsl-modal/uploader' import Button from '@/app/components/base/button' import Modal from '@/app/components/base/modal' import { ToastContext } from '@/app/components/base/toast' import { useEventEmitterContextContext } from '@/context/event-emitter' import { useStore as useAppStore } from '@/app/components/app/store' import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants' type UpdateDSLModalProps = { onCancel: () => void onBackup: () => void onImport?: () => void } const UpdateDSLModal = ({ onCancel, onBackup, onImport, }: UpdateDSLModalProps) => { const { t } = useTranslation() const { notify } = useContext(ToastContext) 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 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 = async (app_id: string) => { const { graph, features, hash, } = await fetchWorkflowDraft(`/apps/${app_id}/workflows/draft`) const { nodes, edges, viewport } = graph const newFeatures = { file: { image: { enabled: !!features.file_upload?.image?.enabled, number_limits: features.file_upload?.image?.number_limits || 3, transfer_methods: features.file_upload?.image?.transfer_methods || ['local_file', 'remote_url'], }, enabled: !!(features.file_upload?.enabled || features.file_upload?.image?.enabled), allowed_file_types: features.file_upload?.allowed_file_types || [SupportUploadFileTypes.image], allowed_file_extensions: features.file_upload?.allowed_file_extensions || FILE_EXTS[SupportUploadFileTypes.image].map(ext => `.${ext}`), allowed_file_upload_methods: features.file_upload?.allowed_file_upload_methods || features.file_upload?.image?.transfer_methods || ['local_file', 'remote_url'], number_limits: features.file_upload?.number_limits || features.file_upload?.image?.number_limits || 3, }, opening: { enabled: !!features.opening_statement, opening_statement: features.opening_statement, suggested_questions: features.suggested_questions, }, suggested: features.suggested_questions_after_answer || { enabled: false }, speech2text: features.speech_to_text || { enabled: false }, text2speech: features.text_to_speech || { enabled: false }, citation: features.retriever_resource || { enabled: false }, moderation: features.sensitive_word_avoidance || { enabled: false }, } eventEmitter?.emit({ type: WORKFLOW_DATA_UPDATE, payload: { nodes: initialNodes(nodes, edges), edges: initialEdges(edges, nodes), viewport, features: newFeatures, hash, }, } as any) } const isCreatingRef = useRef(false) const handleImport: MouseEventHandler = useCallback(async () => { if (isCreatingRef.current) return isCreatingRef.current = true if (!currentFile) return try { if (appDetail && fileContent) { 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 } = response if (status === DSLImportStatus.COMPLETED || status === DSLImportStatus.COMPLETED_WITH_WARNINGS) { if (!app_id) { notify({ type: 'error', message: t('workflow.common.importFailure') }) return } handleWorkflowUpdate(app_id) if (onImport) onImport() notify({ type: status === DSLImportStatus.COMPLETED ? 'success' : 'warning', message: t(status === DSLImportStatus.COMPLETED ? 'workflow.common.importSuccess' : 'workflow.common.importWarning'), children: status === DSLImportStatus.COMPLETED_WITH_WARNINGS && t('workflow.common.importWarningDetails'), }) setLoading(false) onCancel() } else if (status === DSLImportStatus.PENDING) { setShow(false) setTimeout(() => { setShowErrorModal(true) }, 300) setVersions({ importedVersion: imported_dsl_version ?? '', systemVersion: current_dsl_version ?? '', }) setImportId(id) } else { setLoading(false) notify({ type: 'error', message: t('workflow.common.importFailure') }) } } } catch (e) { setLoading(false) notify({ type: 'error', message: t('workflow.common.importFailure') }) } isCreatingRef.current = false }, [currentFile, fileContent, onCancel, notify, t, eventEmitter, appDetail, onImport]) const onUpdateDSLConfirm: MouseEventHandler = async () => { try { if (!importId) return const response = await importDSLConfirm({ import_id: importId, }) const { status, app_id } = response if (status === DSLImportStatus.COMPLETED) { if (!app_id) { notify({ type: 'error', message: t('workflow.common.importFailure') }) return } handleWorkflowUpdate(app_id) if (onImport) onImport() notify({ type: 'success', message: t('workflow.common.importSuccess') }) setLoading(false) onCancel() } else if (status === DSLImportStatus.FAILED) { setLoading(false) notify({ type: 'error', message: t('workflow.common.importFailure') }) } } catch (e) { setLoading(false) notify({ type: 'error', message: t('workflow.common.importFailure') }) } } return ( <>
{t('workflow.common.importDSL')}
{t('workflow.common.importDSLTip')}
{t('workflow.common.chooseDSL')}
setShowErrorModal(false)} className='w-[480px]' >
{t('app.newApp.appCreateDSLErrorTitle')}
{t('app.newApp.appCreateDSLErrorPart1')}
{t('app.newApp.appCreateDSLErrorPart2')}

{t('app.newApp.appCreateDSLErrorPart3')}{versions?.importedVersion}
{t('app.newApp.appCreateDSLErrorPart4')}{versions?.systemVersion}
) } export default memo(UpdateDSLModal)