Overview.tsx 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. import {
  2. makeStyles,
  3. Theme,
  4. Typography,
  5. Chip,
  6. Tooltip,
  7. } from '@material-ui/core';
  8. import { useContext } from 'react';
  9. import { useParams } from 'react-router-dom';
  10. import AttuGrid from '@/components/grid/Grid';
  11. import { ColDefinitionsType } from '@/components/grid/Types';
  12. import { useTranslation } from 'react-i18next';
  13. import icons from '@/components/icons/Icons';
  14. import { formatFieldType } from '@/utils';
  15. import { rootContext, dataContext } from '@/context';
  16. import IndexTypeElement from './IndexTypeElement';
  17. import { getLabelDisplayedRows } from '../../../search/Utils';
  18. import { LOADING_STATE } from '@/consts';
  19. import LoadCollectionDialog from '@/pages/dialogs/LoadCollectionDialog';
  20. import ReleaseCollectionDialog from '@/pages/dialogs/ReleaseCollectionDialog';
  21. import StatusAction from '@/pages/databases/collections/StatusAction';
  22. import CustomButton from '@/components/customButton/CustomButton';
  23. const useStyles = makeStyles((theme: Theme) => ({
  24. wrapper: {
  25. display: 'flex',
  26. flexDirection: 'column',
  27. flexGrow: 1,
  28. height: `100%`,
  29. overflow: 'auto',
  30. '& h5': {
  31. color: theme.palette.attuGrey.dark,
  32. marginBottom: theme.spacing(0.5),
  33. fontSize: '14px',
  34. fontWeight: 400,
  35. },
  36. },
  37. infoWrapper: {
  38. marginBottom: theme.spacing(2),
  39. paddingTop: theme.spacing(0.5),
  40. },
  41. block: {
  42. '& *': {
  43. fontSize: '14px',
  44. lineHeight: 1.5,
  45. },
  46. paddingBottom: theme.spacing(2),
  47. },
  48. icon: {
  49. fontSize: '20px',
  50. marginLeft: theme.spacing(0.5),
  51. },
  52. primaryKeyChip: {
  53. fontSize: '8px',
  54. position: 'relative',
  55. top: '3px',
  56. color: 'grey',
  57. },
  58. chip: {
  59. marginLeft: theme.spacing(0.5),
  60. marginRight: theme.spacing(0.5),
  61. fontSize: '12px',
  62. background: 'rgba(0, 0, 0, 0.04)',
  63. border: 'none',
  64. },
  65. featureChip: {
  66. marginLeft: 0,
  67. border: 'none',
  68. },
  69. nameWrapper: {
  70. display: 'flex',
  71. alignItems: 'center',
  72. '& .key': {
  73. width: '16px',
  74. height: '16px',
  75. marginLeft: theme.spacing(0.5),
  76. },
  77. },
  78. refreshBtn: {
  79. color: theme.palette.attuGrey.main,
  80. cursor: 'pointer',
  81. minWidth: 0,
  82. minHeight: 0,
  83. padding: theme.spacing(0.5),
  84. alignSelf: 'center',
  85. '& svg': {
  86. width: 15,
  87. },
  88. },
  89. paramWrapper: {
  90. // set min width to prevent other table cell stretching
  91. minWidth: 180,
  92. '& .param': {
  93. marginRight: theme.spacing(2),
  94. '& .key': {
  95. color: theme.palette.attuGrey.dark,
  96. display: 'inline-block',
  97. marginRight: theme.spacing(0.5),
  98. },
  99. '& .value': {
  100. color: theme.palette.attuDark.main,
  101. },
  102. },
  103. },
  104. gridWrapper: {
  105. paddingBottom: theme.spacing(2),
  106. },
  107. }));
  108. const Overview = () => {
  109. const { setDialog } = useContext(rootContext);
  110. const { fetchCollection, collections, loading } = useContext(dataContext);
  111. const { collectionName = '' } = useParams<{ collectionName: string }>();
  112. const classes = useStyles();
  113. const { t: collectionTrans } = useTranslation('collection');
  114. const { t: indexTrans } = useTranslation('index');
  115. const { t: commonTrans } = useTranslation();
  116. const gridTrans = commonTrans('grid');
  117. const consistencyTooltipsMap: Record<string, string> = {
  118. Strong: collectionTrans('consistencyStrongTooltip'),
  119. Bounded: collectionTrans('consistencyBoundedTooltip'),
  120. Session: collectionTrans('consistencySessionTooltip'),
  121. Eventually: collectionTrans('consistencyEventuallyTooltip'),
  122. };
  123. // get collection
  124. const collection = collections.find(
  125. c => c.collection_name === collectionName
  126. );
  127. // get fields
  128. const fields = collection?.schema?.fields || [];
  129. const KeyIcon = icons.key;
  130. const EnabledIcon = icons.check;
  131. const RefreshIcon = icons.refresh;
  132. // refresh collection
  133. const refreshCollection = async () => {
  134. await fetchCollection(collectionName);
  135. };
  136. const colDefinitions: ColDefinitionsType[] = [
  137. {
  138. id: 'name',
  139. align: 'left',
  140. disablePadding: true,
  141. formatter(f) {
  142. return (
  143. <div className={classes.nameWrapper}>
  144. {f.name}
  145. {f.is_primary_key ? (
  146. <div
  147. className={classes.primaryKeyChip}
  148. title={collectionTrans('idFieldName')}
  149. >
  150. <KeyIcon classes={{ root: 'key' }} />
  151. </div>
  152. ) : null}
  153. {f.is_partition_key ? (
  154. <Chip
  155. className={classes.chip}
  156. size="small"
  157. label="Partition key"
  158. variant="outlined"
  159. />
  160. ) : null}
  161. {f.autoID ? (
  162. <Chip
  163. className={classes.chip}
  164. size="small"
  165. label="auto id"
  166. variant="outlined"
  167. />
  168. ) : null}
  169. </div>
  170. );
  171. },
  172. label: collectionTrans('fieldName'),
  173. sortBy: 'name',
  174. },
  175. {
  176. id: 'data_type',
  177. align: 'left',
  178. disablePadding: false,
  179. formatter(f) {
  180. return formatFieldType(f);
  181. },
  182. label: collectionTrans('fieldType'),
  183. },
  184. {
  185. id: 'name',
  186. align: 'left',
  187. disablePadding: true,
  188. label: indexTrans('indexName'),
  189. formatter(f) {
  190. return f.index?.index_name;
  191. },
  192. },
  193. {
  194. id: 'name',
  195. align: 'left',
  196. disablePadding: true,
  197. label: indexTrans('type'),
  198. notSort: true,
  199. formatter(f) {
  200. return (
  201. <IndexTypeElement
  202. field={f}
  203. collectionName={collectionName}
  204. cb={async () => {
  205. await fetchCollection(collectionName);
  206. }}
  207. />
  208. );
  209. },
  210. },
  211. {
  212. id: 'name',
  213. align: 'left',
  214. disablePadding: false,
  215. label: indexTrans('param'),
  216. notSort: true,
  217. formatter(f) {
  218. return f.index ? (
  219. <div className={classes.paramWrapper}>
  220. {f.index.indexParameterPairs.length > 0 ? (
  221. f.index.indexParameterPairs.map((p: any) =>
  222. p.value ? (
  223. <span key={p.key + p.value}>
  224. <span className="param">
  225. <Typography variant="body1" className="key">
  226. {`${p.key}:`}
  227. </Typography>
  228. <Typography variant="body1" className="value">
  229. {p.value}
  230. </Typography>
  231. </span>
  232. </span>
  233. ) : (
  234. ''
  235. )
  236. )
  237. ) : (
  238. <>--</>
  239. )}
  240. </div>
  241. ) : (
  242. <>--</>
  243. );
  244. },
  245. },
  246. {
  247. id: 'description',
  248. align: 'left',
  249. disablePadding: false,
  250. label: indexTrans('desc'),
  251. },
  252. ];
  253. // get loading state label
  254. return (
  255. <section className={classes.wrapper}>
  256. {collection && (
  257. <section className={classes.infoWrapper}>
  258. <div className={classes.block}>
  259. <Typography variant="h5">{collectionTrans('status')}</Typography>
  260. <StatusAction
  261. status={collection.status}
  262. percentage={collection.loadedPercentage}
  263. schema={collection.schema!}
  264. action={() => {
  265. setDialog({
  266. open: true,
  267. type: 'custom',
  268. params: {
  269. component:
  270. collection.status === LOADING_STATE.UNLOADED ? (
  271. <LoadCollectionDialog collection={collection} />
  272. ) : (
  273. <ReleaseCollectionDialog collection={collection} />
  274. ),
  275. },
  276. });
  277. }}
  278. />
  279. </div>
  280. <div className={classes.block}>
  281. <Typography variant="h5">
  282. {collectionTrans('rowCount')}
  283. <CustomButton className={classes.refreshBtn} onClick={refreshCollection}>
  284. <RefreshIcon />
  285. </CustomButton>
  286. </Typography>
  287. <Typography variant="h6">{collection?.rowCount || '--'}</Typography>
  288. </div>
  289. <div className={classes.block}>
  290. <Typography variant="h5">
  291. {collectionTrans('description')}
  292. </Typography>
  293. <Typography variant="h6">
  294. {collection?.description || '--'}
  295. </Typography>
  296. </div>
  297. <div className={classes.block}>
  298. <Typography variant="h5">
  299. {collectionTrans('consistency')}
  300. </Typography>
  301. <Typography variant="h6">
  302. <Tooltip
  303. title={
  304. consistencyTooltipsMap[collection.consistency_level!] || ''
  305. }
  306. placement="top"
  307. arrow
  308. >
  309. <Chip
  310. className={`${classes.chip} ${classes.featureChip}`}
  311. label={collection.consistency_level}
  312. variant="outlined"
  313. size="small"
  314. />
  315. </Tooltip>
  316. </Typography>
  317. </div>
  318. <div className={classes.block}>
  319. <Typography variant="h5">
  320. {collectionTrans('createdTime')}
  321. </Typography>
  322. <Typography variant="h6">
  323. {new Date(collection.createdTime).toLocaleString()}
  324. </Typography>
  325. </div>
  326. </section>
  327. )}
  328. <section className={classes.gridWrapper}>
  329. <Typography variant="h5">
  330. {collectionTrans('schema')}
  331. {collection &&
  332. collection.schema &&
  333. collection.schema.enable_dynamic_field ? (
  334. <Tooltip
  335. title={collectionTrans('dynamicSchemaTooltip')}
  336. placement="top"
  337. arrow
  338. >
  339. <Chip
  340. className={`${classes.chip}`}
  341. label={collectionTrans('dynamicSchema')}
  342. size="small"
  343. icon={<EnabledIcon />}
  344. />
  345. </Tooltip>
  346. ) : null}
  347. </Typography>
  348. <AttuGrid
  349. toolbarConfigs={[]}
  350. colDefinitions={colDefinitions}
  351. rows={fields}
  352. rowCount={fields.length}
  353. primaryKey="fieldID"
  354. showHoverStyle={false}
  355. isLoading={loading}
  356. openCheckBox={false}
  357. showPagination={false}
  358. labelDisplayedRows={getLabelDisplayedRows(
  359. gridTrans[fields.length > 1 ? 'fields' : 'field']
  360. )}
  361. />
  362. </section>
  363. </section>
  364. );
  365. };
  366. export default Overview;