| import { Authorization } from '@/constants/authorization'; | |
| import { useSetModalState } from '@/hooks/common-hooks'; | |
| import { useSetSelectedRecord } from '@/hooks/logic-hooks'; | |
| import { useHandleSubmittable } from '@/hooks/login-hooks'; | |
| import { IModalProps } from '@/interfaces/common'; | |
| import api from '@/utils/api'; | |
| import { getAuthorization } from '@/utils/authorization-util'; | |
| import { InboxOutlined } from '@ant-design/icons'; | |
| import { | |
| Button, | |
| Drawer, | |
| Flex, | |
| Form, | |
| FormItemProps, | |
| Input, | |
| InputNumber, | |
| Select, | |
| Switch, | |
| Upload, | |
| } from 'antd'; | |
| import { pick } from 'lodash'; | |
| import { Link2, Trash2 } from 'lucide-react'; | |
| import { useCallback } from 'react'; | |
| import { useTranslation } from 'react-i18next'; | |
| import { BeginQueryType } from '../constant'; | |
| import { | |
| useGetBeginNodeDataQuery, | |
| useSaveGraphBeforeOpeningDebugDrawer, | |
| } from '../hooks'; | |
| import { BeginQuery } from '../interface'; | |
| import useGraphStore from '../store'; | |
| import { getDrawerWidth } from '../utils'; | |
| import { PopoverForm } from './popover-form'; | |
| import styles from './index.less'; | |
| const RunDrawer = ({ | |
| hideModal, | |
| showModal: showChatModal, | |
| }: IModalProps<any>) => { | |
| const { t } = useTranslation(); | |
| const [form] = Form.useForm(); | |
| const updateNodeForm = useGraphStore((state) => state.updateNodeForm); | |
| const { | |
| visible, | |
| hideModal: hidePopover, | |
| switchVisible, | |
| showModal: showPopover, | |
| } = useSetModalState(); | |
| const { setRecord, currentRecord } = useSetSelectedRecord<number>(); | |
| const { submittable } = useHandleSubmittable(form); | |
| const handleShowPopover = useCallback( | |
| (idx: number) => () => { | |
| setRecord(idx); | |
| showPopover(); | |
| }, | |
| [setRecord, showPopover], | |
| ); | |
| const handleRemoveUrl = useCallback( | |
| (key: number, index: number) => () => { | |
| const list: any[] = form.getFieldValue(key); | |
| form.setFieldValue( | |
| key, | |
| list.filter((_, idx) => idx !== index), | |
| ); | |
| }, | |
| [form], | |
| ); | |
| const getBeginNodeDataQuery = useGetBeginNodeDataQuery(); | |
| const query: BeginQuery[] = getBeginNodeDataQuery(); | |
| const normFile = (e: any) => { | |
| if (Array.isArray(e)) { | |
| return e; | |
| } | |
| return e?.fileList; | |
| }; | |
| const renderWidget = useCallback( | |
| (q: BeginQuery, idx: number) => { | |
| const props: FormItemProps & { key: number } = { | |
| key: idx, | |
| label: q.name, | |
| name: idx, | |
| }; | |
| if (q.optional === false) { | |
| props.rules = [{ required: true }]; | |
| } | |
| const urlList: { url: string; result: string }[] = | |
| form.getFieldValue(idx) || []; | |
| const BeginQueryTypeMap = { | |
| [BeginQueryType.Line]: ( | |
| <Form.Item {...props}> | |
| <Input></Input> | |
| </Form.Item> | |
| ), | |
| [BeginQueryType.Paragraph]: ( | |
| <Form.Item {...props}> | |
| <Input.TextArea rows={4}></Input.TextArea> | |
| </Form.Item> | |
| ), | |
| [BeginQueryType.Options]: ( | |
| <Form.Item {...props}> | |
| <Select | |
| allowClear | |
| options={q.options?.map((x) => ({ label: x, value: x })) ?? []} | |
| ></Select> | |
| </Form.Item> | |
| ), | |
| [BeginQueryType.File]: ( | |
| <Form.Item | |
| {...props} | |
| valuePropName="fileList" | |
| getValueFromEvent={normFile} | |
| > | |
| <Upload.Dragger | |
| name="file" | |
| action={api.parse} | |
| multiple | |
| headers={{ [Authorization]: getAuthorization() }} | |
| > | |
| <p className="ant-upload-drag-icon"> | |
| <InboxOutlined /> | |
| </p> | |
| <p className="ant-upload-text">{t('fileManager.uploadTitle')}</p> | |
| <p className="ant-upload-hint"> | |
| {t('fileManager.uploadDescription')} | |
| </p> | |
| </Upload.Dragger> | |
| </Form.Item> | |
| ), | |
| [BeginQueryType.Integer]: ( | |
| <Form.Item {...props}> | |
| <InputNumber></InputNumber> | |
| </Form.Item> | |
| ), | |
| [BeginQueryType.Boolean]: ( | |
| <Form.Item valuePropName={'checked'} {...props}> | |
| <Switch></Switch> | |
| </Form.Item> | |
| ), | |
| [BeginQueryType.Url]: ( | |
| <> | |
| <Form.Item | |
| {...pick(props, ['key', 'label', 'rules'])} | |
| required={!q.optional} | |
| className={urlList.length > 0 ? 'mb-1' : ''} | |
| > | |
| <PopoverForm visible={visible} switchVisible={switchVisible}> | |
| <Button | |
| onClick={handleShowPopover(idx)} | |
| className="text-buttonBlueText" | |
| > | |
| {t('flow.pasteFileLink')} | |
| </Button> | |
| </PopoverForm> | |
| </Form.Item> | |
| <Form.Item name={idx} noStyle {...pick(props, ['rules'])} /> | |
| <Form.Item | |
| noStyle | |
| shouldUpdate={(prevValues, curValues) => | |
| prevValues[idx] !== curValues[idx] | |
| } | |
| > | |
| {({ getFieldValue }) => { | |
| const urlInfo: { url: string; result: string }[] = | |
| getFieldValue(idx) || []; | |
| return urlInfo.length ? ( | |
| <Flex vertical gap={8} className="mb-3"> | |
| {urlInfo.map((u, index) => ( | |
| <div | |
| key={index} | |
| className="flex items-center justify-between gap-2 hover:bg-slate-100 group" | |
| > | |
| <Link2 className="size-5"></Link2> | |
| <span className="flex-1 truncate"> {u.url}</span> | |
| <Trash2 | |
| className="size-4 invisible group-hover:visible cursor-pointer" | |
| onClick={handleRemoveUrl(idx, index)} | |
| /> | |
| </div> | |
| ))} | |
| </Flex> | |
| ) : null; | |
| }} | |
| </Form.Item> | |
| </> | |
| ), | |
| }; | |
| return BeginQueryTypeMap[q.type as BeginQueryType]; | |
| }, | |
| [form, handleRemoveUrl, handleShowPopover, switchVisible, t, visible], | |
| ); | |
| const { handleRun } = useSaveGraphBeforeOpeningDebugDrawer(showChatModal!); | |
| const handleRunAgent = useCallback( | |
| (nextValues: Record<string, any>) => { | |
| const currentNodes = updateNodeForm('begin', nextValues, ['query']); | |
| handleRun(currentNodes); | |
| hideModal?.(); | |
| }, | |
| [handleRun, hideModal, updateNodeForm], | |
| ); | |
| const onOk = useCallback(async () => { | |
| const values = await form.validateFields(); | |
| const nextValues = Object.entries(values).map(([key, value]) => { | |
| const item = query[Number(key)]; | |
| let nextValue = value; | |
| if (Array.isArray(value)) { | |
| nextValue = ``; | |
| value.forEach((x, idx) => { | |
| if (x?.originFileObj instanceof File) { | |
| if (idx === 0) { | |
| nextValue += `${x.name}\n\n${x.response.data}\n\n`; | |
| } else { | |
| nextValue += `${x.response.data}\n\n`; | |
| } | |
| } else { | |
| if (idx === 0) { | |
| nextValue += `${x.url}\n\n${x.result}\n\n`; | |
| } else { | |
| nextValue += `${x.result}\n\n`; | |
| } | |
| } | |
| }); | |
| } | |
| return { ...item, value: nextValue }; | |
| }); | |
| handleRunAgent(nextValues); | |
| }, [form, handleRunAgent, query]); | |
| return ( | |
| <Drawer | |
| title={t('flow.testRun')} | |
| placement="right" | |
| onClose={hideModal} | |
| open | |
| getContainer={false} | |
| width={getDrawerWidth()} | |
| mask={false} | |
| > | |
| <section className={styles.formWrapper}> | |
| <Form.Provider | |
| onFormFinish={(name, { values, forms }) => { | |
| if (name === 'urlForm') { | |
| const { basicForm } = forms; | |
| const urlInfo = basicForm.getFieldValue(currentRecord) || []; | |
| basicForm.setFieldsValue({ | |
| [currentRecord]: [...urlInfo, values], | |
| }); | |
| hidePopover(); | |
| } | |
| }} | |
| > | |
| <Form | |
| name="basicForm" | |
| autoComplete="off" | |
| layout={'vertical'} | |
| form={form} | |
| > | |
| {query.map((x, idx) => { | |
| return renderWidget(x, idx); | |
| })} | |
| </Form> | |
| </Form.Provider> | |
| </section> | |
| <Button type={'primary'} block onClick={onOk} disabled={!submittable}> | |
| {t('common.next')} | |
| </Button> | |
| </Drawer> | |
| ); | |
| }; | |
| export default RunDrawer; | |