CreateCollectionDialog.tsx 7.7 KB

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