Create.tsx 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. import { makeStyles, Theme } from '@material-ui/core';
  2. import { FC, useContext, useMemo, useState } from 'react';
  3. import { useTranslation } from 'react-i18next';
  4. import DialogTemplate from '../../components/customDialog/DialogTemplate';
  5. import CustomInput from '../../components/customInput/CustomInput';
  6. import CustomSelector from '../../components/customSelector/CustomSelector';
  7. import { ITextfieldConfig } from '../../components/customInput/Types';
  8. import { rootContext } from '../../context/Root';
  9. import { useFormValidation } from '../../hooks/Form';
  10. import { formatForm } from '../../utils/Form';
  11. import { TypeEnum } from '../../utils/Validation';
  12. import CreateFields from './CreateFields';
  13. import {
  14. CollectionCreateParam,
  15. CollectionCreateProps,
  16. DataTypeEnum,
  17. ConsistencyLevelEnum,
  18. Field,
  19. } from './Types';
  20. import { CONSISTENCY_LEVEL_OPTIONS } from './Constants';
  21. const useStyles = makeStyles((theme: Theme) => ({
  22. fieldset: {
  23. width: '100%',
  24. display: 'flex',
  25. alignItems: 'center',
  26. marginBottom: '16px',
  27. gap: '8px',
  28. '&:nth-last-child(2)': {
  29. flexDirection: 'column',
  30. alignItems: 'flex-start',
  31. },
  32. '& legend': {
  33. marginBottom: theme.spacing(1),
  34. color: `#82838e`,
  35. lineHeight: '20px',
  36. fontSize: '14px',
  37. },
  38. },
  39. input: {
  40. width: '100%',
  41. },
  42. select: {
  43. width: '160px',
  44. '&:first-child': {
  45. marginLeft: 0,
  46. },
  47. },
  48. dialog: {
  49. minWidth: '100%',
  50. },
  51. }));
  52. const CreateCollection: FC<CollectionCreateProps> = ({ handleCreate }) => {
  53. const classes = useStyles();
  54. const { handleCloseDialog } = useContext(rootContext);
  55. const { t: collectionTrans } = useTranslation('collection');
  56. const { t: btnTrans } = useTranslation('btn');
  57. const { t: warningTrans } = useTranslation('warning');
  58. const [form, setForm] = useState({
  59. collection_name: '',
  60. description: '',
  61. autoID: true,
  62. });
  63. const [consistencyLevel, setConsistencyLevel] =
  64. useState<ConsistencyLevelEnum>(ConsistencyLevelEnum.Session); // Session is the default value of consistency level
  65. const [fields, setFields] = useState<Field[]>([
  66. {
  67. data_type: DataTypeEnum.Int64,
  68. is_primary_key: true,
  69. name: null, // we need hide helpertext at first time, so we use null to detect user enter input or not.
  70. description: '',
  71. isDefault: true,
  72. max_length: null,
  73. id: '1',
  74. },
  75. {
  76. data_type: DataTypeEnum.FloatVector,
  77. is_primary_key: false,
  78. name: null,
  79. dimension: '128',
  80. description: '',
  81. isDefault: true,
  82. id: '2',
  83. },
  84. ]);
  85. const [fieldsValidation, setFieldsValidation] = useState<
  86. {
  87. [x: string]: string | boolean;
  88. }[]
  89. >([
  90. { id: '1', name: false },
  91. { id: '2', name: false, dimension: true },
  92. ]);
  93. const allFieldsValid = useMemo(() => {
  94. return fieldsValidation.every(v => Object.keys(v).every(key => !!v[key]));
  95. }, [fieldsValidation]);
  96. const checkedForm = useMemo(() => {
  97. const { collection_name } = form;
  98. return formatForm({ collection_name });
  99. }, [form]);
  100. const { validation, checkIsValid, disabled } = useFormValidation(checkedForm);
  101. const changeIsAutoID = (value: boolean) => {
  102. setForm({
  103. ...form,
  104. autoID: value,
  105. });
  106. };
  107. const handleInputChange = (key: string, value: string) => {
  108. setForm(v => ({ ...v, [key]: value }));
  109. };
  110. const generalInfoConfigs: ITextfieldConfig[] = [
  111. {
  112. label: collectionTrans('name'),
  113. key: 'collection_name',
  114. value: form.collection_name,
  115. onChange: (value: string) => handleInputChange('collection_name', value),
  116. variant: 'filled',
  117. validations: [
  118. // cannot be empty
  119. {
  120. rule: 'require',
  121. errorText: warningTrans('requiredOnly'),
  122. },
  123. // length <= 255
  124. {
  125. rule: 'range',
  126. extraParam: {
  127. max: 255,
  128. type: 'string',
  129. },
  130. errorText: collectionTrans('nameLengthWarning'),
  131. },
  132. // name can only be combined with letters, number or underscores
  133. {
  134. rule: 'collectionName',
  135. errorText: collectionTrans('nameContentWarning'),
  136. },
  137. // name can not start with number
  138. {
  139. rule: 'firstCharacter',
  140. extraParam: {
  141. invalidTypes: [TypeEnum.number],
  142. },
  143. errorText: collectionTrans('nameFirstLetterWarning'),
  144. },
  145. ],
  146. InputLabelProps: {
  147. shrink: true,
  148. },
  149. size: 'small',
  150. className: classes.input,
  151. },
  152. {
  153. label: collectionTrans('description'),
  154. key: 'description',
  155. value: form.description,
  156. onChange: (value: string) => handleInputChange('description', value),
  157. variant: 'filled',
  158. validations: [],
  159. size: 'small',
  160. className: classes.input,
  161. InputLabelProps: {
  162. shrink: true,
  163. },
  164. },
  165. ];
  166. const handleCreateCollection = () => {
  167. const vectorType = [DataTypeEnum.BinaryVector, DataTypeEnum.FloatVector];
  168. const param: CollectionCreateParam = {
  169. ...form,
  170. fields: fields.map(v => {
  171. const data: any = {
  172. name: v.name,
  173. description: v.description,
  174. is_primary_key: v.is_primary_key,
  175. data_type: v.data_type,
  176. dimension: vectorType.includes(v.data_type) ? v.dimension : undefined,
  177. max_length: v.max_length,
  178. };
  179. v.is_primary_key && (data.autoID = form.autoID);
  180. return data;
  181. }),
  182. consistency_level: consistencyLevel,
  183. };
  184. handleCreate(param);
  185. };
  186. return (
  187. <DialogTemplate
  188. title={collectionTrans('createTitle')}
  189. handleClose={() => {
  190. handleCloseDialog();
  191. }}
  192. confirmLabel={btnTrans('create')}
  193. handleConfirm={handleCreateCollection}
  194. confirmDisabled={disabled || !allFieldsValid}
  195. dialogClass={classes.dialog}
  196. >
  197. <form>
  198. <fieldset className={classes.fieldset}>
  199. <legend>{collectionTrans('general')}</legend>
  200. {generalInfoConfigs.map(config => (
  201. <CustomInput
  202. key={config.key}
  203. type="text"
  204. textConfig={config}
  205. checkValid={checkIsValid}
  206. validInfo={validation}
  207. />
  208. ))}
  209. </fieldset>
  210. <fieldset className={classes.fieldset}>
  211. <legend>{collectionTrans('schema')}</legend>
  212. <CreateFields
  213. fields={fields}
  214. setFields={setFields}
  215. setFieldsValidation={setFieldsValidation}
  216. autoID={form.autoID}
  217. setAutoID={changeIsAutoID}
  218. />
  219. </fieldset>
  220. <fieldset className={classes.fieldset}>
  221. <legend>{collectionTrans('consistency')}</legend>
  222. <CustomSelector
  223. wrapperClass={classes.select}
  224. size="small"
  225. options={CONSISTENCY_LEVEL_OPTIONS}
  226. onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
  227. setConsistencyLevel(e.target.value as ConsistencyLevelEnum);
  228. }}
  229. hiddenLabel={true}
  230. value={consistencyLevel}
  231. variant="filled"
  232. />
  233. </fieldset>
  234. </form>
  235. </DialogTemplate>
  236. );
  237. };
  238. export default CreateCollection;