| '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 { | |
| RiAlertLine, | |
| RiCloseLine, | |
| } from '@remixicon/react' | |
| import { WORKFLOW_DATA_UPDATE } from './constants' | |
| import { | |
| SupportUploadFileTypes, | |
| } from './types' | |
| import { | |
| initialEdges, | |
| initialNodes, | |
| } from './utils' | |
| 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 { updateWorkflowDraftFromDSL } from '@/service/workflow' | |
| 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<File>() | |
| const [fileContent, setFileContent] = useState<string>() | |
| const [loading, setLoading] = useState(false) | |
| const { eventEmitter } = useEventEmitterContextContext() | |
| 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 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 { | |
| graph, | |
| features, | |
| hash, | |
| } = await updateWorkflowDraftFromDSL(appDetail.id, fileContent) | |
| 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) | |
| if (onImport) | |
| onImport() | |
| notify({ type: 'success', message: t('workflow.common.importSuccess') }) | |
| setLoading(false) | |
| onCancel() | |
| } | |
| } | |
| catch (e) { | |
| setLoading(false) | |
| notify({ type: 'error', message: t('workflow.common.importFailure') }) | |
| } | |
| isCreatingRef.current = false | |
| }, [currentFile, fileContent, onCancel, notify, t, eventEmitter, appDetail, onImport]) | |
| return ( | |
| <Modal | |
| className='p-6 w-[520px] rounded-2xl' | |
| isShow={true} | |
| onClose={() => {}} | |
| > | |
| <div className='flex items-center justify-between mb-6'> | |
| <div className='text-2xl font-semibold text-[#101828]'>{t('workflow.common.importDSL')}</div> | |
| <div className='flex items-center justify-center w-[22px] h-[22px] cursor-pointer' onClick={onCancel}> | |
| <RiCloseLine className='w-5 h-5 text-gray-500' /> | |
| </div> | |
| </div> | |
| <div className='flex mb-4 px-4 py-3 bg-[#FFFAEB] rounded-xl border border-[#FEDF89]'> | |
| <RiAlertLine className='shrink-0 mt-0.5 mr-2 w-4 h-4 text-[#F79009]' /> | |
| <div> | |
| <div className='mb-2 text-sm font-medium text-[#354052]'>{t('workflow.common.importDSLTip')}</div> | |
| <Button | |
| variant='secondary-accent' | |
| onClick={onBackup} | |
| > | |
| {t('workflow.common.backupCurrentDraft')} | |
| </Button> | |
| </div> | |
| </div> | |
| <div className='mb-8'> | |
| <div className='mb-1 text-[13px] font-semibold text-[#354052]'> | |
| {t('workflow.common.chooseDSL')} | |
| </div> | |
| <Uploader | |
| file={currentFile} | |
| updateFile={handleFile} | |
| className='!mt-0' | |
| /> | |
| </div> | |
| <div className='flex justify-end'> | |
| <Button className='mr-2' onClick={onCancel}>{t('app.newApp.Cancel')}</Button> | |
| <Button | |
| disabled={!currentFile || loading} | |
| variant='warning' | |
| onClick={handleImport} | |
| loading={loading} | |
| > | |
| {t('workflow.common.overwriteAndImport')} | |
| </Button> | |
| </div> | |
| </Modal> | |
| ) | |
| } | |
| export default memo(UpdateDSLModal) | |