2
0

Partitions.tsx 9.0 KB

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