LoadCollectionDialog.tsx 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. import { useEffect, useState, useContext, useMemo } from 'react';
  2. import { Typography, Theme, Switch, FormControlLabel } from '@mui/material';
  3. import { useTranslation } from 'react-i18next';
  4. import { authContext, rootContext, dataContext } from '@/context';
  5. import { MilvusService } from '@/http';
  6. import { useFormValidation } from '@/hooks';
  7. import { formatForm, parseJson, getNode } from '@/utils';
  8. import { MILVUS_NODE_TYPE, MILVUS_DEPLOY_MODE } from '@/consts';
  9. import CustomInput from '@/components/customInput/CustomInput';
  10. import { ITextfieldConfig } from '@/components/customInput/Types';
  11. import DialogTemplate from '@/components/customDialog/DialogTemplate';
  12. import CustomToolTip from '@/components/customToolTip/CustomToolTip';
  13. import icons from '@/components/icons/Icons';
  14. import { CollectionObject } from '@server/types';
  15. import { makeStyles } from '@mui/styles';
  16. const useStyles = makeStyles((theme: Theme) => ({
  17. desc: {
  18. marginBottom: theme.spacing(2),
  19. maxWidth: 480,
  20. },
  21. replicaDesc: {
  22. marginBottom: theme.spacing(2),
  23. maxWidth: 480,
  24. },
  25. toggle: {
  26. marginBottom: theme.spacing(2),
  27. },
  28. icon: {
  29. fontSize: '14px',
  30. marginLeft: theme.spacing(0.5),
  31. },
  32. }));
  33. const LoadCollectionDialog = (props: {
  34. collection: CollectionObject;
  35. onLoad?: (collection: CollectionObject) => void;
  36. isModifyReplica?: boolean;
  37. }) => {
  38. const { loadCollection } = useContext(dataContext);
  39. const classes = useStyles();
  40. const { collection, onLoad, isModifyReplica } = props;
  41. const { t: dialogTrans } = useTranslation('dialog');
  42. const { t: collectionTrans } = useTranslation('collection');
  43. const { t: successTrans } = useTranslation('success');
  44. const { t: btnTrans } = useTranslation('btn');
  45. const { t: warningTrans } = useTranslation('warning');
  46. const { handleCloseDialog, openSnackBar } = useContext(rootContext);
  47. const [form, setForm] = useState({
  48. replica: collection.replicas?.length || 1,
  49. });
  50. const { isManaged } = useContext(authContext);
  51. const [enableRelica, setEnableRelica] = useState(false);
  52. const [replicaToggle, setReplicaToggle] = useState(
  53. collection.replicas!.length > 1
  54. );
  55. const [maxQueryNode, setMaxQueryNode] = useState(1);
  56. const [btnDisabled, setBtnDisabled] = useState(false);
  57. // check if it is cluster
  58. useEffect(() => {
  59. async function fetchData() {
  60. try {
  61. const res = await MilvusService.getMetrics();
  62. const parsedJson = parseJson(res);
  63. // get root cord
  64. const rootCoords = getNode(
  65. parsedJson.workingNodes,
  66. MILVUS_NODE_TYPE.ROOTCOORD
  67. );
  68. // get query nodes
  69. const queryNodes = getNode(
  70. parsedJson.workingNodes,
  71. MILVUS_NODE_TYPE.QUERYNODE
  72. );
  73. const rootCoord = rootCoords[0];
  74. // should we show replic toggle
  75. const enableRelica =
  76. rootCoord.infos.system_info.deploy_mode ===
  77. MILVUS_DEPLOY_MODE.DISTRIBUTED;
  78. // only show replica toggle in distributed mode && query node > 1
  79. if (enableRelica && queryNodes.length > 1 && !isManaged) {
  80. setMaxQueryNode(queryNodes.length);
  81. setEnableRelica(enableRelica);
  82. }
  83. } catch (error) {}
  84. }
  85. fetchData();
  86. }, []);
  87. // input state change
  88. const handleInputChange = (value: number) => {
  89. setForm({ replica: value });
  90. };
  91. // confirm action
  92. const handleConfirm = async () => {
  93. let params;
  94. if (enableRelica) {
  95. params = { replica_number: Number(form.replica) };
  96. }
  97. try {
  98. setBtnDisabled(true);
  99. // load collection request
  100. await loadCollection(collection.collection_name, params);
  101. // show success message
  102. openSnackBar(
  103. successTrans(isModifyReplica ? 'modifyReplica' : 'load', {
  104. name: collectionTrans('collection'),
  105. })
  106. );
  107. // callback
  108. if (onLoad) {
  109. await onLoad(collection);
  110. }
  111. // close dialog
  112. handleCloseDialog();
  113. } catch (error) {
  114. } finally {
  115. setBtnDisabled(false);
  116. }
  117. };
  118. // validator
  119. const checkedForm = useMemo(() => {
  120. return enableRelica ? [] : formatForm(form);
  121. }, [form, enableRelica]);
  122. // validate
  123. const { validation, checkIsValid, disabled } = useFormValidation(checkedForm);
  124. // input config
  125. const inputConfig: ITextfieldConfig = {
  126. label: collectionTrans('replicaNum'),
  127. type: 'number',
  128. key: 'replica',
  129. onChange: handleInputChange,
  130. variant: 'filled',
  131. placeholder: collectionTrans('replicaNum'),
  132. fullWidth: true,
  133. validations: [],
  134. required: enableRelica,
  135. defaultValue: form.replica,
  136. };
  137. // if replica is enabled, add validation
  138. if (enableRelica) {
  139. inputConfig.validations?.push(
  140. {
  141. rule: 'require',
  142. errorText: warningTrans('required', {
  143. name: collectionTrans('replicaNum'),
  144. }),
  145. },
  146. {
  147. rule: 'range',
  148. errorText: warningTrans('range', { min: 1, max: maxQueryNode }),
  149. extraParam: {
  150. min: 1,
  151. max: maxQueryNode,
  152. type: 'number',
  153. },
  154. }
  155. );
  156. }
  157. // toggle enbale replica
  158. const handleChange = () => {
  159. setReplicaToggle(!replicaToggle);
  160. if (!replicaToggle === false) {
  161. setForm({ replica: 1 });
  162. }
  163. };
  164. const QuestionIcon = icons.question;
  165. return (
  166. <DialogTemplate
  167. title={dialogTrans(isModifyReplica ? 'modifyReplicaTitle' : 'loadTitle', {
  168. type: collection.collection_name,
  169. })}
  170. handleClose={handleCloseDialog}
  171. children={
  172. <>
  173. <Typography variant="body1" component="p" className={classes.desc}>
  174. {collectionTrans(isModifyReplica ? 'replicaDes' : 'loadContent')}
  175. </Typography>
  176. {enableRelica ? (
  177. <>
  178. <FormControlLabel
  179. control={
  180. <Switch checked={replicaToggle} onChange={handleChange} />
  181. }
  182. label={
  183. <CustomToolTip title={collectionTrans('replicaDes')}>
  184. <>
  185. {collectionTrans('enableRepica')}
  186. <QuestionIcon classes={{ root: classes.icon }} />
  187. </>
  188. </CustomToolTip>
  189. }
  190. className={classes.toggle}
  191. />
  192. </>
  193. ) : null}
  194. {replicaToggle ? (
  195. <>
  196. <CustomInput
  197. type="text"
  198. textConfig={inputConfig}
  199. checkValid={checkIsValid}
  200. validInfo={validation}
  201. />
  202. </>
  203. ) : null}
  204. </>
  205. }
  206. confirmLabel={btnTrans(isModifyReplica ? 'confirm' : 'load')}
  207. handleConfirm={handleConfirm}
  208. confirmDisabled={replicaToggle ? disabled || btnDisabled : btnDisabled}
  209. />
  210. );
  211. };
  212. export default LoadCollectionDialog;