github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/ui/src/components/CreateOrUpdateTask/SourceInfo.tsx (about) 1 import React, { useState } from 'react' 2 import { useTranslation } from 'react-i18next' 3 4 import { 5 Button, 6 Collapse, 7 Form, 8 Input, 9 InputNumber, 10 Select, 11 Card, 12 } from '~/uikit' 13 import { FileAddOutlined, CloseOutlined, RetweetOutlined } from '~/uikit/icons' 14 import { useDmapiGetSourceListQuery } from '~/models/source' 15 import { StepCompnent } from '~/components/CreateOrUpdateTask/shared' 16 import { TaskMigrateConsistencyLevel, TaskMode } from '~/models/task' 17 18 const formLayout = { 19 labelCol: { span: 6 }, 20 wrapperCol: { span: 18 }, 21 } 22 23 const itemLayout = { 24 labelCol: { span: 4 }, 25 wrapperCol: { span: 20 }, 26 } 27 28 const SourceInfo: StepCompnent = ({ prev, initialValues }) => { 29 const [t] = useTranslation() 30 const { data, isFetching } = useDmapiGetSourceListQuery({ 31 with_status: false, 32 }) 33 const disableGtidOrBinlog = initialValues?.task_mode !== TaskMode.INCREMENTAL 34 const [gtidOrBinlogStatus, setGtidOrBinlogStatus] = useState< 35 Map<number, boolean> 36 >(new Map()) 37 38 const toggle = (id: number) => { 39 setGtidOrBinlogStatus(prev => { 40 const newMap = new Map(prev) 41 newMap.set(id, !prev.get(id)) 42 return newMap 43 }) 44 } 45 46 return ( 47 <Form {...formLayout} name="sourceInfo" initialValues={initialValues}> 48 <Collapse> 49 <Collapse.Panel header={t('sync config')} key="1"> 50 <div className="flex"> 51 <div className="flex-1"> 52 <h3 className="max-w-[33%] text-right font-bold mb-8"> 53 {t('full migrate config')} 54 </h3> 55 <Form.Item 56 label={t('export concurrency')} 57 tooltip={t('create task export_threads tooltip')} 58 name={['source_config', 'full_migrate_conf', 'export_threads']} 59 > 60 <InputNumber className="!w-[100%]" placeholder="4" /> 61 </Form.Item> 62 <Form.Item 63 label={t('import concurrency')} 64 tooltip={t('create task import_threads tooltip')} 65 name={['source_config', 'full_migrate_conf', 'import_threads']} 66 > 67 <InputNumber className="!w-[100%]" placeholder="4" /> 68 </Form.Item> 69 70 <Form.Item 71 label={t('consistency requirement')} 72 name={['source_config', 'full_migrate_conf', 'consistency']} 73 > 74 <Select placeholder={TaskMigrateConsistencyLevel.Auto}> 75 {Object.values(TaskMigrateConsistencyLevel).map( 76 consistency => ( 77 <Select.Option key={consistency} value={consistency}> 78 {consistency} 79 </Select.Option> 80 ) 81 )} 82 </Select> 83 </Form.Item> 84 </div> 85 <div className="flex-1"> 86 <h3 className="max-w-[33%] text-right font-bold mb-8"> 87 {t('incremental migrate config')} 88 </h3> 89 <Form.Item 90 label={t('synchronous concurrency')} 91 tooltip={t('create task repl_threads tooltip')} 92 name={['source_config', 'incr_migrate_conf', 'repl_threads']} 93 > 94 <InputNumber className="!w-[100%]" placeholder="32" /> 95 </Form.Item> 96 <Form.Item 97 label={t('transaction batch')} 98 tooltip={t('create task repl_batch tooltip')} 99 name={['source_config', 'incr_migrate_conf', 'repl_batch']} 100 > 101 <InputNumber className="!w-[100%]" placeholder="100" /> 102 </Form.Item> 103 </div> 104 </div> 105 </Collapse.Panel> 106 </Collapse> 107 108 <div className="grid grid-cols-2 gap-4 auto-rows-fr my-4"> 109 <Form.List name={['source_config', 'source_conf']}> 110 {(fields, { add, remove }) => ( 111 <> 112 {fields.map(field => ( 113 <Card 114 hoverable 115 key={field.key} 116 className="!h-200px relative !border-[#d9d9d9] group" 117 > 118 <CloseOutlined 119 onClick={() => remove(field.name)} 120 className="!text-gray-500 absolute top-2 right-2 group-hover:opacity-100 opacity-0 transition" 121 /> 122 <Form.Item 123 {...itemLayout} 124 label={t('source name')} 125 name={[field.name, 'source_name']} 126 rules={[ 127 { required: true, message: t('source name is required') }, 128 ]} 129 > 130 <Select placeholder="mysql-01" loading={isFetching}> 131 {data?.data.map(source => ( 132 <Select.Option 133 key={source.source_name} 134 value={source.source_name} 135 > 136 {source.source_name} 137 </Select.Option> 138 ))} 139 </Select> 140 </Form.Item> 141 142 <div className="relative"> 143 <Form.Item 144 className="flex-1" 145 {...itemLayout} 146 hidden={gtidOrBinlogStatus.get(field.name)} 147 label={t('binlog')} 148 tooltip={t('create task binlog_name tooltip')} 149 > 150 <Input.Group compact> 151 <Form.Item noStyle name={[field.name, 'binlog_name']}> 152 <Input 153 className="!mr-4" 154 disabled={disableGtidOrBinlog} 155 style={{ maxWidth: '45%' }} 156 placeholder="name" 157 /> 158 </Form.Item> 159 160 <Form.Item noStyle name={[field.name, 'binlog_pos']}> 161 <InputNumber 162 style={{ width: '40%' }} 163 disabled={disableGtidOrBinlog} 164 placeholder="position" 165 /> 166 </Form.Item> 167 </Input.Group> 168 </Form.Item> 169 170 <Form.Item 171 className="flex-1" 172 {...itemLayout} 173 hidden={!gtidOrBinlogStatus.get(field.name)} 174 label={t('gtid')} 175 name={[field.name, 'binlog_gtid']} 176 > 177 <Input 178 className="max-w-[90%]" 179 disabled={disableGtidOrBinlog} 180 /> 181 </Form.Item> 182 183 <Button 184 className="!absolute ml-2 top-0 right-0" 185 onClick={() => toggle(field.name)} 186 > 187 <RetweetOutlined /> 188 </Button> 189 </div> 190 </Card> 191 ))} 192 193 <Button 194 className="!h-200px" 195 type="dashed" 196 icon={<FileAddOutlined />} 197 onClick={() => add()} 198 > 199 {t('add source config')} 200 </Button> 201 </> 202 )} 203 </Form.List> 204 </div> 205 206 <Form.Item> 207 <Button className="mr-4" onClick={prev}> 208 {t('previous')} 209 </Button> 210 <Button type="primary" htmlType="submit"> 211 {t('next')} 212 </Button> 213 </Form.Item> 214 </Form> 215 ) 216 } 217 218 export default SourceInfo