2
0

Overview.tsx 11 KB

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