|
'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) |
|
|