Partitions.tsx 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. import { makeStyles, Theme } from '@material-ui/core';
  2. import { useContext, useEffect, useState } from 'react';
  3. import { useSearchParams, useParams } from 'react-router-dom';
  4. import Highlighter from 'react-highlight-words';
  5. import AttuGrid from '@/components/grid/Grid';
  6. import { ColDefinitionsType, ToolBarConfig } from '@/components/grid/Types';
  7. import { useTranslation } from 'react-i18next';
  8. import { usePaginationHook, useInsertDialogHook } from '@/hooks';
  9. import icons from '@/components/icons/Icons';
  10. import CustomToolTip from '@/components/customToolTip/CustomToolTip';
  11. import { rootContext } from '@/context';
  12. import { Collection, Partition, FieldHttp, DataService } from '@/http';
  13. import InsertContainer from '../dialogs/insert/Dialog';
  14. import { InsertDataParam } from '../collections/Types';
  15. import CreatePartitionDialog from '../dialogs/CreatePartitionDialog';
  16. import DropPartitionDialog from '../dialogs/DropPartitionDialog';
  17. import { PartitionView } from './Types';
  18. const useStyles = makeStyles((theme: Theme) => ({
  19. wrapper: {
  20. height: `calc(100vh - 160px)`,
  21. },
  22. icon: {
  23. fontSize: '20px',
  24. marginLeft: theme.spacing(0.5),
  25. },
  26. highlight: {
  27. color: theme.palette.primary.main,
  28. backgroundColor: 'transparent',
  29. },
  30. }));
  31. let timer: NodeJS.Timeout | null = null;
  32. const Partitions = () => {
  33. const { collectionName = '' } = useParams<{ collectionName: string }>();
  34. const classes = useStyles();
  35. const { t } = useTranslation('partition');
  36. const { t: successTrans } = useTranslation('success');
  37. const { t: btnTrans } = useTranslation('btn');
  38. const [searchParams] = useSearchParams();
  39. const [search, setSearch] = useState<string>(
  40. (searchParams.get('search') as string) || ''
  41. );
  42. const InfoIcon = icons.info;
  43. const { handleInsertDialog } = useInsertDialogHook();
  44. const [selectedPartitions, setSelectedPartitions] = useState<PartitionView[]>(
  45. []
  46. );
  47. const [partitions, setPartitions] = useState<PartitionView[]>([]);
  48. const [searchedPartitions, setSearchedPartitions] = useState<PartitionView[]>(
  49. []
  50. );
  51. const {
  52. pageSize,
  53. handlePageSize,
  54. currentPage,
  55. handleCurrentPage,
  56. total,
  57. data: partitionList,
  58. order,
  59. orderBy,
  60. handleGridSort,
  61. } = usePaginationHook(searchedPartitions);
  62. const [loading, setLoading] = useState<boolean>(true);
  63. const { setDialog, openSnackBar } = useContext(rootContext);
  64. const fetchPartitions = async (collectionName: string) => {
  65. try {
  66. const res = await Partition.getPartitions(collectionName);
  67. setLoading(false);
  68. setPartitions(res);
  69. } catch (err) {
  70. setLoading(false);
  71. }
  72. };
  73. const fetchCollectionDetail = async (name: string) => {
  74. const res = await Collection.getCollectionInfo(name);
  75. return res;
  76. };
  77. useEffect(() => {
  78. fetchPartitions(collectionName);
  79. }, [collectionName]);
  80. useEffect(() => {
  81. if (timer) {
  82. clearTimeout(timer);
  83. }
  84. // add loading manually
  85. setLoading(true);
  86. timer = setTimeout(() => {
  87. const searchWords = [search];
  88. const list = search
  89. ? partitions.filter(p => p.partitionName.includes(search))
  90. : partitions;
  91. const highlightList = list.map(c => {
  92. Object.assign(c, {
  93. _nameElement: (
  94. <Highlighter
  95. textToHighlight={c.partitionName}
  96. searchWords={searchWords}
  97. highlightClassName={classes.highlight}
  98. />
  99. ),
  100. });
  101. return c;
  102. });
  103. setLoading(false);
  104. setSearchedPartitions(highlightList);
  105. }, 300);
  106. }, [search, partitions]);
  107. const onDelete = () => {
  108. openSnackBar(successTrans('delete', { name: t('partition') }));
  109. fetchPartitions(collectionName);
  110. };
  111. const handleSearch = (value: string) => {
  112. setSearch(value);
  113. };
  114. const handleInsert = async (
  115. collectionName: string,
  116. partitionName: string,
  117. fieldData: any[]
  118. ): Promise<{ result: boolean; msg: string }> => {
  119. const param: InsertDataParam = {
  120. partition_name: partitionName,
  121. fields_data: fieldData,
  122. };
  123. try {
  124. await DataService.insertData(collectionName, param);
  125. await DataService.flush(collectionName);
  126. // update partitions
  127. fetchPartitions(collectionName);
  128. return { result: true, msg: '' };
  129. } catch (err: any) {
  130. const {
  131. response: {
  132. data: { message },
  133. },
  134. } = err;
  135. return { result: false, msg: message || '' };
  136. }
  137. };
  138. const toolbarConfigs: ToolBarConfig[] = [
  139. {
  140. label: t('create'),
  141. onClick: () => {
  142. setDialog({
  143. open: true,
  144. type: 'custom',
  145. params: {
  146. component: (
  147. <CreatePartitionDialog
  148. collectionName={collectionName}
  149. onCreate={onCreate}
  150. />
  151. ),
  152. },
  153. });
  154. },
  155. icon: 'add',
  156. },
  157. {
  158. type: 'button',
  159. btnVariant: 'text',
  160. btnColor: 'secondary',
  161. label: btnTrans('insert'),
  162. onClick: async () => {
  163. const collection = await fetchCollectionDetail(collectionName);
  164. const schema = collection.schema.fields.map(f => new FieldHttp(f));
  165. handleInsertDialog(
  166. <InsertContainer
  167. schema={schema}
  168. defaultSelectedCollection={collectionName}
  169. defaultSelectedPartition={
  170. selectedPartitions.length === 1
  171. ? selectedPartitions[0].partitionName
  172. : ''
  173. }
  174. partitions={partitions}
  175. handleInsert={handleInsert}
  176. />
  177. );
  178. },
  179. /**
  180. * insert validation:
  181. * 1. At least 1 available partition
  182. * 2. selected partition quantity shouldn't over 1
  183. */
  184. disabled: () => partitions.length === 0 || selectedPartitions.length > 1,
  185. },
  186. {
  187. icon: 'delete',
  188. type: 'button',
  189. btnVariant: 'text',
  190. btnColor: 'secondary',
  191. onClick: () => {
  192. setDialog({
  193. open: true,
  194. type: 'custom',
  195. params: {
  196. component: (
  197. <DropPartitionDialog
  198. partitions={selectedPartitions}
  199. collectionName={collectionName}
  200. onDelete={onDelete}
  201. />
  202. ),
  203. },
  204. });
  205. },
  206. label: btnTrans('drop'),
  207. // can't delete default partition
  208. disabled: () =>
  209. selectedPartitions.length === 0 ||
  210. selectedPartitions.some(p => p.name === '_default'),
  211. tooltip: selectedPartitions.some(p => p.name === '_default')
  212. ? t('deletePartitionError')
  213. : '',
  214. },
  215. {
  216. label: 'Search',
  217. icon: 'search',
  218. searchText: search,
  219. onSearch: (value: string) => {
  220. handleSearch(value);
  221. },
  222. },
  223. ];
  224. const colDefinitions: ColDefinitionsType[] = [
  225. {
  226. id: '_nameElement',
  227. align: 'left',
  228. disablePadding: false,
  229. label: t('name'),
  230. },
  231. {
  232. id: 'createdAt',
  233. align: 'left',
  234. disablePadding: false,
  235. label: t('createdTime'),
  236. },
  237. // {
  238. // id: '_statusElement',
  239. // align: 'left',
  240. // disablePadding: false,
  241. // label: t('status'),
  242. // },
  243. {
  244. id: 'entityCount',
  245. align: 'left',
  246. disablePadding: false,
  247. label: (
  248. <span className="flex-center">
  249. {t('rowCount')}
  250. <CustomToolTip title={t('tooltip')}>
  251. <InfoIcon classes={{ root: classes.icon }} />
  252. </CustomToolTip>
  253. </span>
  254. ),
  255. },
  256. // {
  257. // id: 'action',
  258. // align: 'center',
  259. // disablePadding: false,
  260. // label: '',
  261. // showActionCell: true,
  262. // isHoverAction: true,
  263. // actionBarConfigs: [
  264. // {
  265. // onClick: (e: React.MouseEvent, row: PartitionView) => {
  266. // const cb =
  267. // row._status === StatusEnum.unloaded ? handleLoad : handleRelease;
  268. // handleAction(row, cb);
  269. // },
  270. // icon: 'load',
  271. // label: 'load',
  272. // showIconMethod: 'renderFn',
  273. // getLabel: (row: PartitionView) =>
  274. // row._status === StatusEnum.loaded ? 'release' : 'load',
  275. // renderIconFn: (row: PartitionView) =>
  276. // row._status === StatusEnum.loaded ? <ReleaseIcon /> : <LoadIcon />,
  277. // },
  278. // ],
  279. // },
  280. ];
  281. const handleSelectChange = (value: PartitionView[]) => {
  282. setSelectedPartitions(value);
  283. };
  284. const handlePageChange = (e: any, page: number) => {
  285. handleCurrentPage(page);
  286. setSelectedPartitions([]);
  287. };
  288. const onCreate = () => {
  289. openSnackBar(successTrans('create', { name: t('partition') }));
  290. // refresh partitions
  291. fetchPartitions(collectionName);
  292. setSelectedPartitions([]);
  293. };
  294. return (
  295. <section className={classes.wrapper}>
  296. <AttuGrid
  297. toolbarConfigs={toolbarConfigs}
  298. colDefinitions={colDefinitions}
  299. rows={partitionList}
  300. rowCount={total}
  301. primaryKey="id"
  302. selected={selectedPartitions}
  303. setSelected={handleSelectChange}
  304. page={currentPage}
  305. onPageChange={handlePageChange}
  306. rowsPerPage={pageSize}
  307. setRowsPerPage={handlePageSize}
  308. isLoading={loading}
  309. order={order}
  310. orderBy={orderBy}
  311. handleSort={handleGridSort}
  312. />
  313. </section>
  314. );
  315. };
  316. export default Partitions;