Mirror of https://github.com/roostorg/osprey github.com/roostorg/osprey
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 212 lines 6.6 kB view raw
1import React, { useState } from 'react'; 2import { Modal, Form, Input, Select, Upload, Button } from 'antd'; 3import { UploadOutlined } from '@ant-design/icons'; 4import type { UploadFile, UploadProps } from 'antd/es/upload/interface'; 5import type { FormInstance } from 'antd/es/form'; 6import styles from './BulkActionStartModal.module.css'; 7import { max, set } from 'lodash'; 8import useBulkActionStore from '../../stores/BulkActionStore'; 9 10const { TextArea } = Input; 11const { Option } = Select; 12 13interface WorkflowOption { 14 value: string; 15 label: string; 16} 17 18interface JobFormValues { 19 jobName: string; 20 jobDescription: string; 21 workflow: string; 22 customWorkflowInput?: string; 23 file: File; 24 entityType: string; 25} 26 27interface JobUploadModalProps { 28 visible: boolean; 29 onCancel: () => void; 30 onSubmit: (values: JobFormValues) => void; 31 initialValues?: Partial<JobFormValues>; 32} 33 34const workflowOptions: WorkflowOption[] = [ 35 { value: 'workflow1', label: 'Test Workflow' }, 36 { value: 'custom', label: 'Custom Workflow' }, 37]; 38 39const BulkActionStartModal: React.FC<JobUploadModalProps> = ({ visible, onCancel, onSubmit, initialValues }) => { 40 const [form] = Form.useForm<JobFormValues>(); 41 const [customWorkflow, setCustomWorkflow] = useState<boolean>(false); 42 const [fileSelected, setFileSelected] = useState<File | null>(null); 43 44 const handleWorkflowChange = (value: string): void => { 45 setCustomWorkflow(value === 'custom'); 46 }; 47 48 const handleSubmit = async (): Promise<void> => { 49 try { 50 const values: JobFormValues = (await form.validateFields()) as JobFormValues; 51 if (fileSelected != null) { 52 values.file = fileSelected; 53 } 54 onSubmit(values as JobFormValues); 55 56 form.resetFields(); 57 setFileSelected(null); 58 setCustomWorkflow(false); 59 } catch (error) { 60 console.error('Validation failed:', error); 61 } 62 }; 63 64 const normFile = (e: any): UploadFile[] => { 65 if (Array.isArray(e)) { 66 return e; 67 } 68 return e?.fileList || []; 69 }; 70 71 // Custom validation rules 72 const validationRules = { 73 jobName: [ 74 { required: true, message: 'Please enter job name' }, 75 { max: 100, message: 'Job name cannot exceed 100 characters' }, 76 ], 77 jobDescription: [ 78 { required: true, message: 'Please enter job description' }, 79 { max: 500, message: 'Job description cannot exceed 500 characters' }, 80 ], 81 workflow: [{ required: true, message: 'Please select or enter workflow' }], 82 customWorkflowInput: [ 83 { required: true, message: 'Please enter custom workflow' }, 84 { max: 100, message: 'Custom workflow cannot exceed 100 characters' }, 85 ], 86 file: [], 87 entityType: [{ required: true, message: 'Please select entity type' }], 88 }; 89 90 // File upload configuration 91 const uploadProps: UploadProps = { 92 beforeUpload: (file) => { 93 // ~10 million rows of bigint entity-ids in a CSV file would be around 250MB 94 const isValidSize = file.size ? file.size / 1024 / 1024 / 1024 < 250 : false; // 250MB limit 95 if (!isValidSize) { 96 Modal.error({ 97 title: 'File too large', 98 content: 'File size must be less than 250MB', 99 }); 100 } 101 setFileSelected(file); 102 return false; // Prevent automatic upload 103 }, 104 accept: '.csv', // Restrict file types, 105 multiple: false, 106 showUploadList: false, 107 type: 'drag', 108 }; 109 110 return ( 111 <Modal 112 title="Create New Job" 113 visible={visible} 114 onCancel={() => { 115 form.resetFields(); 116 setFileSelected(null); 117 setCustomWorkflow(false); 118 onCancel(); 119 }} 120 onOk={handleSubmit} 121 width={600} 122 className={styles.modal} 123 > 124 <Form<JobFormValues> form={form} layout="vertical" requiredMark="optional" initialValues={initialValues}> 125 <Form.Item name="jobName" label="Job Name" rules={validationRules.jobName}> 126 <Input placeholder="Enter job name" /> 127 </Form.Item> 128 129 <Form.Item name="jobDescription" label="Job Description" rules={validationRules.jobDescription}> 130 <TextArea placeholder="Enter job description" rows={4} /> 131 </Form.Item> 132 133 <Form.Item name="entityType" label="Entity Type" rules={validationRules.entityType}> 134 <Select placeholder="Select entity type"> 135 <Option value="user">User</Option> 136 <Option value="guild">Guild</Option> 137 </Select> 138 </Form.Item> 139 140 <Form.Item name="workflow" label="Workflow" rules={validationRules.workflow}> 141 <Select onChange={handleWorkflowChange} placeholder="Select workflow"> 142 {workflowOptions.map((option) => ( 143 <Option key={option.value} value={option.value}> 144 {option.label} 145 </Option> 146 ))} 147 </Select> 148 </Form.Item> 149 150 {customWorkflow && ( 151 <Form.Item name="customWorkflowInput" label="Custom Workflow" rules={validationRules.customWorkflowInput}> 152 <Input placeholder="Enter custom workflow" /> 153 </Form.Item> 154 )} 155 156 <Form.Item name="file" label="Upload File" getValueFromEvent={normFile} rules={validationRules.file}> 157 <Upload {...uploadProps}> 158 <Button icon={<UploadOutlined />}>Click to Upload</Button> 159 </Upload> 160 <div>{fileSelected?.name}</div> 161 </Form.Item> 162 </Form> 163 </Modal> 164 ); 165}; 166 167const BulkActionStartModalContainer = () => { 168 const [visible, setVisible] = useState(false); 169 const bulkActions = useBulkActionStore(); 170 171 const handleOpenModal = () => { 172 setVisible(true); 173 }; 174 175 const handleCloseModal = () => { 176 setVisible(false); 177 }; 178 179 const handleSubmit = async (values: JobFormValues) => { 180 const workflow = values.workflow === 'custom' ? values.customWorkflowInput : values.workflow; 181 if (!workflow) { 182 Modal.error({ 183 title: 'Workflow is required', 184 content: 'Please select or enter a workflow', 185 }); 186 return; 187 } 188 await bulkActions.startBulkAction({ 189 job_name: values.jobName, 190 job_description: values.jobDescription, 191 workflow_name: workflow, 192 file_name: values.file.name, 193 file: values.file, 194 entity_type: values.entityType, 195 }); 196 197 setVisible(false); 198 199 await bulkActions.getJobs(); 200 }; 201 202 return ( 203 <> 204 <Button type="primary" onClick={handleOpenModal}> 205 Create New Job 206 </Button> 207 <BulkActionStartModal visible={visible} onCancel={handleCloseModal} onSubmit={handleSubmit} /> 208 </> 209 ); 210}; 211 212export default BulkActionStartModalContainer;