save-flow-modal.tsx 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. import { addFlow, apiInterceptors, updateFlowById } from '@/client/api';
  2. import { IFlowData, IFlowUpdateParam } from '@/types/flow';
  3. import { mapHumpToUnderline } from '@/utils/flow';
  4. import { Button, Checkbox, Form, Input, Modal, message } from 'antd';
  5. import { useRouter } from 'next/router';
  6. import { useEffect, useState } from 'react';
  7. import { useTranslation } from 'react-i18next';
  8. import { ReactFlowInstance } from 'reactflow';
  9. const { TextArea } = Input;
  10. type Props = {
  11. reactFlow: ReactFlowInstance<any, any>;
  12. flowInfo?: IFlowUpdateParam;
  13. isSaveFlowModalOpen: boolean;
  14. setIsSaveFlowModalOpen: (value: boolean) => void;
  15. };
  16. export const SaveFlowModal: React.FC<Props> = ({
  17. reactFlow,
  18. isSaveFlowModalOpen,
  19. flowInfo,
  20. setIsSaveFlowModalOpen,
  21. }) => {
  22. const { t } = useTranslation();
  23. const router = useRouter();
  24. const [form] = Form.useForm<IFlowUpdateParam>();
  25. const [messageApi, contextHolder] = message.useMessage();
  26. const [deploy, setDeploy] = useState(false);
  27. const [id, setId] = useState(router.query.id || '');
  28. useEffect(() => {
  29. setId(router.query.id || '');
  30. }, [router.query.id]);
  31. function onLabelChange(e: React.ChangeEvent<HTMLInputElement>) {
  32. const label = e.target.value;
  33. // replace spaces with underscores, convert uppercase letters to lowercase, remove characters other than digits, letters, _, and -.
  34. const result = label
  35. .replace(/\s+/g, '_')
  36. .replace(/[^a-z0-9_-]/g, '')
  37. .toLowerCase();
  38. form.setFieldsValue({ name: result });
  39. }
  40. async function onSaveFlow() {
  41. const { name, label, description = '', editable = false, state = 'deployed' } = form.getFieldsValue();
  42. const reactFlowObject = mapHumpToUnderline(reactFlow.toObject() as IFlowData);
  43. if (id) {
  44. const [, , res] = await apiInterceptors(
  45. updateFlowById(id.toString(), {
  46. name,
  47. label,
  48. description,
  49. editable,
  50. uid: id.toString(),
  51. flow_data: reactFlowObject,
  52. state,
  53. variables: flowInfo?.variables,
  54. }),
  55. );
  56. if (res?.success) {
  57. messageApi.success(t('save_flow_success'));
  58. } else if (res?.err_msg) {
  59. messageApi.error(res?.err_msg);
  60. }
  61. } else {
  62. const [_, res] = await apiInterceptors(
  63. addFlow({
  64. name,
  65. label,
  66. description,
  67. editable,
  68. flow_data: reactFlowObject,
  69. state,
  70. variables: flowInfo?.variables,
  71. }),
  72. );
  73. if (res?.uid) {
  74. messageApi.success(t('save_flow_success'));
  75. router.push(`/construct/flow/canvas?id=${res.uid}`, undefined, { shallow: true });
  76. }
  77. }
  78. setIsSaveFlowModalOpen(false);
  79. }
  80. return (
  81. <>
  82. <Modal
  83. title={t('flow_modal_title')}
  84. open={isSaveFlowModalOpen}
  85. onCancel={() => setIsSaveFlowModalOpen(false)}
  86. footer={[
  87. <Button key='cancel' onClick={() => setIsSaveFlowModalOpen(false)}>
  88. {t('cancel')}
  89. </Button>,
  90. <Button key='submit' type='primary' onClick={() => form.submit()}>
  91. {t('verify')}
  92. </Button>,
  93. ]}
  94. >
  95. <Form
  96. name='flow_form'
  97. form={form}
  98. labelCol={{ span: 6 }}
  99. wrapperCol={{ span: 16 }}
  100. className='mt-6 max-w-2xl'
  101. initialValues={{ remember: true }}
  102. onFinish={onSaveFlow}
  103. autoComplete='off'
  104. >
  105. <Form.Item
  106. label='Title'
  107. name='label'
  108. initialValue={flowInfo?.label}
  109. rules={[{ required: true, message: 'Please input flow title!' }]}
  110. >
  111. <Input onChange={onLabelChange} />
  112. </Form.Item>
  113. <Form.Item
  114. label='Name'
  115. name='name'
  116. initialValue={flowInfo?.name}
  117. rules={[
  118. { required: true, message: 'Please input flow name!' },
  119. () => ({
  120. validator(_, value) {
  121. // eslint-disable-next-line no-useless-escape
  122. const regex = /^[a-zA-Z0-9_\-]+$/;
  123. if (!regex.test(value)) {
  124. return Promise.reject('Can only contain numbers, letters, underscores, and dashes');
  125. }
  126. return Promise.resolve();
  127. },
  128. }),
  129. ]}
  130. >
  131. <Input />
  132. </Form.Item>
  133. <Form.Item label='Description' initialValue={flowInfo?.description} name='description'>
  134. <TextArea rows={3} />
  135. </Form.Item>
  136. <Form.Item label='Editable' name='editable' initialValue={flowInfo?.editable || true} valuePropName='checked'>
  137. <Checkbox />
  138. </Form.Item>
  139. <Form.Item hidden name='state'>
  140. <Input />
  141. </Form.Item>
  142. <Form.Item label='Deploy'>
  143. <Checkbox
  144. defaultChecked={flowInfo?.state === 'deployed' || flowInfo?.state === 'running'}
  145. checked={deploy}
  146. onChange={e => {
  147. const val = e.target.checked;
  148. form.setFieldValue('state', val ? 'deployed' : 'developing');
  149. setDeploy(val);
  150. }}
  151. />
  152. </Form.Item>
  153. </Form>
  154. </Modal>
  155. {contextHolder}
  156. </>
  157. );
  158. };