|
@@ -1,153 +1,131 @@
|
|
-import { FC, useMemo, MouseEvent, useContext } from 'react';
|
|
|
|
|
|
+import { FC, useMemo, useContext } from 'react';
|
|
import { StatusActionType } from '@/components/status/Types';
|
|
import { StatusActionType } from '@/components/status/Types';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { useTranslation } from 'react-i18next';
|
|
-import { Typography, useTheme, Chip } from '@mui/material';
|
|
|
|
|
|
+import { useTheme, Chip, Box } from '@mui/material';
|
|
import { rootContext } from '@/context';
|
|
import { rootContext } from '@/context';
|
|
import { LOADING_STATE } from '@/consts';
|
|
import { LOADING_STATE } from '@/consts';
|
|
import StatusIcon, { LoadingType } from '@/components/status/StatusIcon';
|
|
import StatusIcon, { LoadingType } from '@/components/status/StatusIcon';
|
|
import Icons from '@/components/icons/Icons';
|
|
import Icons from '@/components/icons/Icons';
|
|
import CustomToolTip from '@/components/customToolTip/CustomToolTip';
|
|
import CustomToolTip from '@/components/customToolTip/CustomToolTip';
|
|
-import CustomButton from '@/components/customButton/CustomButton';
|
|
|
|
import LoadCollectionDialog from '@/pages/dialogs/LoadCollectionDialog';
|
|
import LoadCollectionDialog from '@/pages/dialogs/LoadCollectionDialog';
|
|
import ReleaseCollectionDialog from '@/pages/dialogs/ReleaseCollectionDialog';
|
|
import ReleaseCollectionDialog from '@/pages/dialogs/ReleaseCollectionDialog';
|
|
|
|
|
|
|
|
+const StatusIndicator: FC<{ color: string; filled?: boolean }> = ({
|
|
|
|
+ color,
|
|
|
|
+ filled,
|
|
|
|
+}) => (
|
|
|
|
+ <span
|
|
|
|
+ style={{
|
|
|
|
+ display: 'inline-block',
|
|
|
|
+ width: 8,
|
|
|
|
+ height: 8,
|
|
|
|
+ borderRadius: '50%',
|
|
|
|
+ border: `1px solid ${color}`,
|
|
|
|
+ background: filled ? color : '#fff',
|
|
|
|
+ verticalAlign: 'middle',
|
|
|
|
+ }}
|
|
|
|
+ />
|
|
|
|
+);
|
|
|
|
+
|
|
const StatusAction: FC<StatusActionType> = props => {
|
|
const StatusAction: FC<StatusActionType> = props => {
|
|
const {
|
|
const {
|
|
status,
|
|
status,
|
|
percentage = 0,
|
|
percentage = 0,
|
|
collection,
|
|
collection,
|
|
- action = () => {},
|
|
|
|
showExtraAction,
|
|
showExtraAction,
|
|
showLoadButton,
|
|
showLoadButton,
|
|
createIndexElement,
|
|
createIndexElement,
|
|
|
|
+ sx,
|
|
} = props;
|
|
} = props;
|
|
|
|
|
|
- // Theme
|
|
|
|
const theme = useTheme();
|
|
const theme = useTheme();
|
|
-
|
|
|
|
- // Context
|
|
|
|
const { setDialog } = useContext(rootContext);
|
|
const { setDialog } = useContext(rootContext);
|
|
-
|
|
|
|
- // Translations
|
|
|
|
const { t: commonTrans } = useTranslation();
|
|
const { t: commonTrans } = useTranslation();
|
|
const { t: collectionTrans } = useTranslation('collection');
|
|
const { t: collectionTrans } = useTranslation('collection');
|
|
- const { t: btnTrans } = useTranslation('btn');
|
|
|
|
|
|
|
|
- // Determine status-related labels and icons
|
|
|
|
- const { label, tooltip, icon, deleteIcon } = useMemo(() => {
|
|
|
|
- switch (status) {
|
|
|
|
- case LOADING_STATE.UNLOADED:
|
|
|
|
- return {
|
|
|
|
- label: commonTrans('status.unloaded'),
|
|
|
|
- tooltip: collectionTrans('clickToLoad'),
|
|
|
|
- icon: (
|
|
|
|
- <span
|
|
|
|
- style={{
|
|
|
|
- display: 'inline-block',
|
|
|
|
- width: 8,
|
|
|
|
- height: 8,
|
|
|
|
- borderRadius: '50%',
|
|
|
|
- border: `1px solid ${theme.palette.primary.main}`,
|
|
|
|
- background: '#fff',
|
|
|
|
- verticalAlign: 'middle',
|
|
|
|
- }}
|
|
|
|
- />
|
|
|
|
- ),
|
|
|
|
- deleteIcon: <Icons.load />,
|
|
|
|
- };
|
|
|
|
- case LOADING_STATE.LOADED:
|
|
|
|
- return {
|
|
|
|
- label: commonTrans('status.loaded'),
|
|
|
|
- tooltip: collectionTrans('clickToRelease'),
|
|
|
|
- icon: (
|
|
|
|
- <span
|
|
|
|
- style={{
|
|
|
|
- display: 'inline-block',
|
|
|
|
- width: 8,
|
|
|
|
- height: 8,
|
|
|
|
- borderRadius: '50%',
|
|
|
|
- border: `1px solid ${theme.palette.primary.main}`,
|
|
|
|
- background: theme.palette.primary.main,
|
|
|
|
- verticalAlign: 'middle',
|
|
|
|
- }}
|
|
|
|
- />
|
|
|
|
- ),
|
|
|
|
- deleteIcon: <Icons.release />,
|
|
|
|
- };
|
|
|
|
- case LOADING_STATE.LOADING:
|
|
|
|
- return {
|
|
|
|
- label: `${percentage}% ${commonTrans('status.loading')}`,
|
|
|
|
- tooltip: collectionTrans('collectionIsLoading'),
|
|
|
|
- icon: (
|
|
|
|
- <span
|
|
|
|
- style={{
|
|
|
|
- display: 'inline-flex',
|
|
|
|
- alignItems: 'center',
|
|
|
|
- marginRight: theme.spacing(1.25),
|
|
|
|
- }}
|
|
|
|
- >
|
|
|
|
- <StatusIcon type={LoadingType.CREATING} />
|
|
|
|
- </span>
|
|
|
|
- ),
|
|
|
|
- deleteIcon: null, // No delete icon during loading
|
|
|
|
- };
|
|
|
|
- default:
|
|
|
|
- return {
|
|
|
|
- label: status,
|
|
|
|
- tooltip: '',
|
|
|
|
- icon: <span></span>,
|
|
|
|
- deleteIcon: <Icons.release />,
|
|
|
|
- };
|
|
|
|
- }
|
|
|
|
- }, [status, percentage, theme, collectionTrans]);
|
|
|
|
|
|
+ const chipStyles = {
|
|
|
|
+ paddingLeft: theme.spacing(0.5),
|
|
|
|
+ };
|
|
|
|
|
|
- // Handle missing vector index
|
|
|
|
- const noIndex = collection.schema && !collection.schema.hasVectorIndex;
|
|
|
|
- const noIndexIcon = (
|
|
|
|
|
|
+ const LoadingIndicator = () => (
|
|
<span
|
|
<span
|
|
style={{
|
|
style={{
|
|
- display: 'inline-block',
|
|
|
|
- width: 8,
|
|
|
|
- height: 8,
|
|
|
|
- borderRadius: '50%',
|
|
|
|
- border: `1px solid ${theme.palette.text.disabled}`,
|
|
|
|
- background: '#fff',
|
|
|
|
- verticalAlign: 'middle',
|
|
|
|
|
|
+ display: 'inline-flex',
|
|
|
|
+ alignItems: 'center',
|
|
|
|
+ marginRight: theme.spacing(1.25),
|
|
}}
|
|
}}
|
|
- />
|
|
|
|
|
|
+ >
|
|
|
|
+ <StatusIcon type={LoadingType.CREATING} />
|
|
|
|
+ </span>
|
|
);
|
|
);
|
|
|
|
+
|
|
|
|
+ const noIndex = collection.schema && !collection.schema.hasVectorIndex;
|
|
|
|
+ const noIndexIcon = <StatusIndicator color={theme.palette.text.disabled} />;
|
|
const noIndexTooltip = collectionTrans('noVectorIndexTooltip');
|
|
const noIndexTooltip = collectionTrans('noVectorIndexTooltip');
|
|
|
|
|
|
- // Handle chip click
|
|
|
|
- const handleChipClick = (e: MouseEvent<HTMLDivElement>) => {
|
|
|
|
- e.stopPropagation();
|
|
|
|
- setDialog({
|
|
|
|
- open: true,
|
|
|
|
- type: 'custom',
|
|
|
|
- params: {
|
|
|
|
- component:
|
|
|
|
- status === LOADING_STATE.UNLOADED ? (
|
|
|
|
- <LoadCollectionDialog collection={collection} />
|
|
|
|
- ) : (
|
|
|
|
- <ReleaseCollectionDialog collection={collection} />
|
|
|
|
- ),
|
|
|
|
|
|
+ type IconConfig = {
|
|
|
|
+ icon: JSX.Element | null;
|
|
|
|
+ show: boolean;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const getIconConfig = (
|
|
|
|
+ status: (typeof LOADING_STATE)[keyof typeof LOADING_STATE]
|
|
|
|
+ ): IconConfig => {
|
|
|
|
+ const iconConfigs: Partial<
|
|
|
|
+ Record<(typeof LOADING_STATE)[keyof typeof LOADING_STATE], IconConfig>
|
|
|
|
+ > = {
|
|
|
|
+ [LOADING_STATE.LOADED]: {
|
|
|
|
+ icon: (
|
|
|
|
+ <Icons.release
|
|
|
|
+ className="action-icon"
|
|
|
|
+ style={{
|
|
|
|
+ fontSize: '14px',
|
|
|
|
+ display: 'none',
|
|
|
|
+ fontWeight: 500,
|
|
|
|
+ transition: 'all 0.2s',
|
|
|
|
+ color: 'inherit',
|
|
|
|
+ }}
|
|
|
|
+ />
|
|
|
|
+ ),
|
|
|
|
+ show: true,
|
|
},
|
|
},
|
|
- });
|
|
|
|
|
|
+ [LOADING_STATE.UNLOADED]: {
|
|
|
|
+ icon: noIndex ? null : (
|
|
|
|
+ <Icons.load
|
|
|
|
+ style={{
|
|
|
|
+ fontSize: '14px',
|
|
|
|
+ fontWeight: 500,
|
|
|
|
+ marginLeft: showLoadButton ? 2 : 0,
|
|
|
|
+ display: showLoadButton ? 'inline' : 'none',
|
|
|
|
+ color: 'inherit',
|
|
|
|
+ }}
|
|
|
|
+ className={!showLoadButton ? 'action-icon' : ''}
|
|
|
|
+ />
|
|
|
|
+ ),
|
|
|
|
+ show: !noIndex,
|
|
|
|
+ },
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ return iconConfigs[status] || { icon: null, show: false };
|
|
};
|
|
};
|
|
|
|
|
|
- if (
|
|
|
|
- collection.schema &&
|
|
|
|
- status === LOADING_STATE.UNLOADED &&
|
|
|
|
- collection.schema.hasVectorIndex &&
|
|
|
|
- showLoadButton
|
|
|
|
- ) {
|
|
|
|
- return (
|
|
|
|
- <CustomButton
|
|
|
|
- startIcon={<Icons.load />}
|
|
|
|
- sx={{ height: 24, padding: '0 8px' }}
|
|
|
|
- variant="contained"
|
|
|
|
- tooltip={collectionTrans('clickToLoad')}
|
|
|
|
- onClick={() => {
|
|
|
|
|
|
+ const statusConfig = useMemo(() => {
|
|
|
|
+ const baseConfig = {
|
|
|
|
+ icon: <StatusIndicator color={theme.palette.primary.main} />,
|
|
|
|
+ variant: 'outlined' as const,
|
|
|
|
+ color: 'primary' as const,
|
|
|
|
+ onClick: undefined as (() => void) | undefined,
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const configs = {
|
|
|
|
+ [LOADING_STATE.UNLOADED]: {
|
|
|
|
+ ...baseConfig,
|
|
|
|
+ label: commonTrans(
|
|
|
|
+ noIndex ? 'status.noVectorIndex' : 'status.readyToLoad'
|
|
|
|
+ ),
|
|
|
|
+ tooltip: collectionTrans('clickToLoad'),
|
|
|
|
+ variant: 'outlined' as const,
|
|
|
|
+ onClick: () => {
|
|
setDialog({
|
|
setDialog({
|
|
open: true,
|
|
open: true,
|
|
type: 'custom',
|
|
type: 'custom',
|
|
@@ -155,54 +133,158 @@ const StatusAction: FC<StatusActionType> = props => {
|
|
component: <LoadCollectionDialog collection={collection} />,
|
|
component: <LoadCollectionDialog collection={collection} />,
|
|
},
|
|
},
|
|
});
|
|
});
|
|
- }}
|
|
|
|
- >
|
|
|
|
- {collectionTrans('loadTitle')}
|
|
|
|
- </CustomButton>
|
|
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ [LOADING_STATE.LOADED]: {
|
|
|
|
+ ...baseConfig,
|
|
|
|
+ label: commonTrans('status.loaded'),
|
|
|
|
+ tooltip: collectionTrans('clickToRelease'),
|
|
|
|
+ icon: <StatusIndicator color={theme.palette.primary.main} filled />,
|
|
|
|
+ onClick: () => {
|
|
|
|
+ setDialog({
|
|
|
|
+ open: true,
|
|
|
|
+ type: 'custom',
|
|
|
|
+ params: {
|
|
|
|
+ component: <ReleaseCollectionDialog collection={collection} />,
|
|
|
|
+ },
|
|
|
|
+ });
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ [LOADING_STATE.LOADING]: {
|
|
|
|
+ ...baseConfig,
|
|
|
|
+ label: `${percentage}% ${commonTrans('status.loading')}`,
|
|
|
|
+ tooltip: collectionTrans('collectionIsLoading'),
|
|
|
|
+ icon: <LoadingIndicator />,
|
|
|
|
+ },
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ return (
|
|
|
|
+ configs[status] || {
|
|
|
|
+ ...baseConfig,
|
|
|
|
+ label: status,
|
|
|
|
+ tooltip: '',
|
|
|
|
+ icon: <span />,
|
|
|
|
+ }
|
|
);
|
|
);
|
|
- }
|
|
|
|
|
|
+ }, [
|
|
|
|
+ status,
|
|
|
|
+ percentage,
|
|
|
|
+ theme,
|
|
|
|
+ collectionTrans,
|
|
|
|
+ commonTrans,
|
|
|
|
+ collection,
|
|
|
|
+ setDialog,
|
|
|
|
+ ]);
|
|
|
|
|
|
- return (
|
|
|
|
- <div style={{ display: 'flex', alignItems: 'center' }}>
|
|
|
|
- <CustomToolTip title={noIndex ? noIndexTooltip : tooltip} placement="top">
|
|
|
|
|
|
+ const renderStatusChip = () => {
|
|
|
|
+ const iconConfig = getIconConfig(status);
|
|
|
|
+
|
|
|
|
+ const getChipStyles = () => {
|
|
|
|
+ if (noIndex) {
|
|
|
|
+ return {
|
|
|
|
+ ...chipStyles,
|
|
|
|
+ cursor: 'default',
|
|
|
|
+ fontSize: '12px',
|
|
|
|
+ borderStyle: 'dashed',
|
|
|
|
+ borderColor:
|
|
|
|
+ theme.palette.mode === 'dark'
|
|
|
|
+ ? theme.palette.grey[600]
|
|
|
|
+ : theme.palette.text.disabled,
|
|
|
|
+ backgroundColor:
|
|
|
|
+ theme.palette.mode === 'dark' ? theme.palette.grey[800] : '#fff',
|
|
|
|
+ color:
|
|
|
|
+ theme.palette.mode === 'dark'
|
|
|
|
+ ? theme.palette.grey[400]
|
|
|
|
+ : theme.palette.text.disabled,
|
|
|
|
+ };
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (status === LOADING_STATE.UNLOADED) {
|
|
|
|
+ return {
|
|
|
|
+ ...chipStyles,
|
|
|
|
+ cursor: 'pointer',
|
|
|
|
+ fontSize: '12px',
|
|
|
|
+ borderColor: theme.palette.primary.main,
|
|
|
|
+ backgroundColor: 'transparent',
|
|
|
|
+ color: theme.palette.primary.main,
|
|
|
|
+ '&:hover': {
|
|
|
|
+ backgroundColor: `${theme.palette.primary.main} !important`,
|
|
|
|
+ color: `#fff !important`,
|
|
|
|
+ },
|
|
|
|
+ };
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (status === LOADING_STATE.LOADED) {
|
|
|
|
+ return {
|
|
|
|
+ ...chipStyles,
|
|
|
|
+ cursor: 'pointer',
|
|
|
|
+ fontSize: '12px',
|
|
|
|
+ borderColor: 'transparent',
|
|
|
|
+ backgroundColor: theme.palette.primary.light,
|
|
|
|
+ '&:hover': {
|
|
|
|
+ borderColor: theme.palette.primary.main,
|
|
|
|
+ '& .action-icon': {
|
|
|
|
+ display: 'inline-flex !important',
|
|
|
|
+ color: theme.palette.text.primary,
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ };
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return {
|
|
|
|
+ ...chipStyles,
|
|
|
|
+ cursor: 'default',
|
|
|
|
+ fontSize: '12px',
|
|
|
|
+ };
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ return (
|
|
|
|
+ <CustomToolTip
|
|
|
|
+ title={noIndex ? noIndexTooltip : statusConfig.tooltip}
|
|
|
|
+ placement="top"
|
|
|
|
+ enterDelay={1000}
|
|
|
|
+ leaveDelay={0}
|
|
|
|
+ >
|
|
<Chip
|
|
<Chip
|
|
- sx={{
|
|
|
|
- border: 'none',
|
|
|
|
- marginRight: theme.spacing(0.5),
|
|
|
|
- paddingLeft: theme.spacing(0.5),
|
|
|
|
- }}
|
|
|
|
- label={<Typography>{label}</Typography>}
|
|
|
|
- onClick={handleChipClick}
|
|
|
|
|
|
+ sx={getChipStyles()}
|
|
|
|
+ label={
|
|
|
|
+ <Box
|
|
|
|
+ sx={{
|
|
|
|
+ display: 'flex',
|
|
|
|
+ alignItems: 'center',
|
|
|
|
+ gap: 0.5,
|
|
|
|
+ }}
|
|
|
|
+ >
|
|
|
|
+ {statusConfig.label}
|
|
|
|
+ {iconConfig.show && iconConfig.icon}
|
|
|
|
+ </Box>
|
|
|
|
+ }
|
|
|
|
+ onClick={statusConfig.onClick}
|
|
disabled={noIndex}
|
|
disabled={noIndex}
|
|
- deleteIcon={<Icons.release />}
|
|
|
|
|
|
+ variant={statusConfig.variant}
|
|
|
|
+ color={statusConfig.color}
|
|
size="small"
|
|
size="small"
|
|
- icon={noIndex ? noIndexIcon : icon}
|
|
|
|
|
|
+ icon={noIndex ? noIndexIcon : statusConfig.icon}
|
|
/>
|
|
/>
|
|
</CustomToolTip>
|
|
</CustomToolTip>
|
|
|
|
+ );
|
|
|
|
+ };
|
|
|
|
|
|
- {showExtraAction && collection.schema && (
|
|
|
|
- <>
|
|
|
|
- {status === LOADING_STATE.LOADED && (
|
|
|
|
- <CustomButton
|
|
|
|
- startIcon={<Icons.navSearch />}
|
|
|
|
- sx={{ height: 24, padding: '0 8px' }}
|
|
|
|
- tooltip={collectionTrans('clickToSearch')}
|
|
|
|
- onClick={() => {
|
|
|
|
- const newHash = window.location.hash.replace(
|
|
|
|
- 'schema',
|
|
|
|
- 'search'
|
|
|
|
- );
|
|
|
|
- window.location.hash = newHash;
|
|
|
|
- }}
|
|
|
|
- >
|
|
|
|
- {btnTrans('vectorSearch')}
|
|
|
|
- </CustomButton>
|
|
|
|
- )}
|
|
|
|
-
|
|
|
|
- {!collection.schema.hasVectorIndex && createIndexElement}
|
|
|
|
- </>
|
|
|
|
- )}
|
|
|
|
- </div>
|
|
|
|
|
|
+ return (
|
|
|
|
+ <Box
|
|
|
|
+ sx={{
|
|
|
|
+ display: 'flex',
|
|
|
|
+ alignItems: 'center',
|
|
|
|
+ gap: theme.spacing(0.5),
|
|
|
|
+ ...sx,
|
|
|
|
+ }}
|
|
|
|
+ >
|
|
|
|
+ {renderStatusChip()}
|
|
|
|
+ {showExtraAction &&
|
|
|
|
+ collection.schema &&
|
|
|
|
+ !collection.schema.hasVectorIndex &&
|
|
|
|
+ createIndexElement}
|
|
|
|
+ </Box>
|
|
);
|
|
);
|
|
};
|
|
};
|
|
|
|
|