Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[UI] [Refactor] Empty state for db table #845

Merged
merged 13 commits into from
Nov 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion ui/apps/everest/src/pages/databases/DbClusterView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import { beautifyDbTypeName, dbEngineToDbType } from '@percona/utils';
import { useNamespacePermissionsForResource } from 'hooks/rbac';
import DbActions from 'components/db-actions/db-actions';
import DbActionsModals from 'components/db-actions/db-actions-modals';
import { EmptyState } from './emptyState/emptyState';

export const DbClusterView = () => {
const [isNewClusterMode, setIsNewClusterMode] = useState(false);
Expand Down Expand Up @@ -168,7 +169,7 @@ export const DbClusterView = () => {
<Box sx={{ width: '100%' }}>
<Table
tableName="dbClusterView"
noDataMessage={Messages.dbCluster.noData}
emptyState={<EmptyState />}
state={{ isLoading: dbClustersLoading || loadingNamespaces }}
columns={columns}
data={tableData}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const Messages = {
noDbClusters: 'You currently do not have any database cluster.',
createToStart: 'Create one to get started.',
create: 'Create Database',
contactSupport: 'Contact Percona Support',
};
47 changes: 47 additions & 0 deletions ui/apps/everest/src/pages/databases/emptyState/emptyState.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Box, Button, Divider, Link, Typography } from '@mui/material';
import HelpIcon from '@mui/icons-material/Help';
import AddIcon from '@mui/icons-material/Add';
import { EmptyStateIcon } from '@percona/ui-lib';
import { Link as MUILink } from 'react-router-dom';
import { Messages } from './emptyState.messages';

const centeredContainerStyle = {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
};

export const EmptyState = () => {
return (
<>
<Box
sx={{
...centeredContainerStyle,
marginTop: '50px',
gap: '10px',
}}
>
<EmptyStateIcon w="60px" h="60px" />
<Box sx={centeredContainerStyle}>
<Typography>{Messages.noDbClusters}</Typography>
<Typography> {Messages.createToStart}</Typography>
</Box>

<Button // TODO refactor with new component when #811 is merged
size="small"
startIcon={<AddIcon />}
component={MUILink}
to="/databases/new"
variant="contained"
data-testid="add-db-cluster-button"
>
{Messages.create}
</Button>
<Divider sx={{ width: '30%', marginTop: '10px' }} />
<Link target="_blank" rel="noopener" href="https://hubs.ly/Q02Rt6pG0">
<Button startIcon={<HelpIcon />}> {Messages.contactSupport}</Button>
</Link>
</Box>
</>
);
};
114 changes: 114 additions & 0 deletions ui/packages/ui-lib/src/icons/empty-state/empty-state.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
const EmptyStateIcon = ({ w, h }: { w: string; h: string }) => {
return (
<svg
width={w}
height={h}
viewBox="0 0 66 62"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M40.3396 12.9979C53.5922 12.9979 64.3355 10.3121 64.3355 6.99896C64.3355 3.68583 53.5922 1 40.3396 1C27.0871 1 16.3438 3.68583 16.3438 6.99896C16.3438 10.3121 27.0871 12.9979 40.3396 12.9979Z"
stroke="#2C323E"
strokeWidth="2"
strokeMiterlimit="10"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M37.0659 37.0197C38.1416 37.0576 39.2362 37.0765 40.3498 37.0765C53.6045 37.0765 64.3457 34.3873 64.3457 31.0776"
stroke="#2C323E"
strokeWidth="2"
strokeMiterlimit="10"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M34.8164 24.9131C36.5902 25.0217 38.4365 25.0786 40.3396 25.0786C53.5942 25.0786 64.3355 22.3894 64.3355 19.0797"
stroke="#2C323E"
strokeWidth="2"
strokeMiterlimit="10"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M30.0896 48.428C33.4922 48.8181 36.9147 49.0098 40.3396 49.0021C53.5942 49.0021 64.3354 46.3129 64.3354 43.0031"
stroke="#2C323E"
strokeWidth="2"
strokeMiterlimit="10"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M16.3438 55.0011V51.7482"
stroke="#2C323E"
strokeWidth="2"
strokeMiterlimit="10"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M16.3438 16.3284V6.99896"
stroke="#2C323E"
strokeWidth="2"
strokeMiterlimit="10"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M16.3438 55.001C16.3438 58.3108 27.0902 61 40.3396 61C53.5891 61 64.3355 58.3108 64.3355 55.001V6.99896"
stroke="#2C323E"
strokeWidth="2"
strokeMiterlimit="10"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M56.6761 19.5135C57.6985 19.5135 58.5274 18.6846 58.5274 17.6621C58.5274 16.6397 57.6985 15.8108 56.6761 15.8108C55.6536 15.8108 54.8247 16.6397 54.8247 17.6621C54.8247 18.6846 55.6536 19.5135 56.6761 19.5135Z"
stroke="#0E5FB5"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M56.6761 31.6795C57.6985 31.6795 58.5274 30.8506 58.5274 29.8282C58.5274 28.8057 57.6985 27.9768 56.6761 27.9768C55.6536 27.9768 54.8247 28.8057 54.8247 29.8282C54.8247 30.8506 55.6536 31.6795 56.6761 31.6795Z"
stroke="#0E5FB5"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M56.6761 43.3166C57.6985 43.3166 58.5274 42.4877 58.5274 41.4652C58.5274 40.4428 57.6985 39.6139 56.6761 39.6139C55.6536 39.6139 54.8247 40.4428 54.8247 41.4652C54.8247 42.4877 55.6536 43.3166 56.6761 43.3166Z"
stroke="#0E5FB5"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M56.6761 55.4826C57.6985 55.4826 58.5274 54.6537 58.5274 53.6313C58.5274 52.6088 57.6985 51.7799 56.6761 51.7799C55.6536 51.7799 54.8247 52.6088 54.8247 53.6313C54.8247 54.6537 55.6536 55.4826 56.6761 55.4826Z"
stroke="#0E5FB5"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M37.304 34.0616C37.3055 35.0614 37.2224 36.0596 37.0557 37.0455C36.3546 41.2367 34.1902 45.0435 30.9472 47.7894C27.7042 50.5353 23.5926 52.0425 19.3432 52.043C18.3383 52.0419 17.3352 51.9589 16.3438 51.7947C12.1633 51.0814 8.36998 48.9122 5.63505 45.671C2.90011 42.4299 1.3999 38.3257 1.3999 34.0848C1.3999 29.844 2.90011 25.7398 5.63505 22.4986C8.36998 19.2575 12.1633 17.0883 16.3438 16.3749C17.3352 16.2108 18.3383 16.1278 19.3432 16.1267C24.1031 16.124 28.6691 18.0118 32.0373 21.3751C35.4054 24.7384 37.2998 29.3017 37.304 34.0616Z"
stroke="#0E5FB5"
strokeWidth="2"
strokeMiterlimit="10"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M28.321 31.031V37.03H22.3427V43.0135H16.3489V37.03H10.3706V31.031H16.3489V25.0528H22.3427V31.031H28.321Z"
stroke="#2C323E"
strokeWidth="2"
strokeMiterlimit="10"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
};

export default EmptyStateIcon;
1 change: 1 addition & 0 deletions ui/packages/ui-lib/src/icons/empty-state/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as EmptyStateIcon } from './empty-state';
28 changes: 28 additions & 0 deletions ui/packages/ui-lib/src/icons/icons.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
import { DatabaseIcon, NetworkNode } from './other';
import { GenericErrorIcon } from './generic-error';
import { NoMatchIcon } from './no-match';
import { EmptyStateIcon } from './empty-state';
import {
ErrorIcon,
PausedIcon,
Expand Down Expand Up @@ -75,6 +76,8 @@ const icons = {

noMatch: [NoMatchIcon],

emptyState: [EmptyStateIcon],

status: [
ErrorIcon,
WarningIcon,
Expand Down Expand Up @@ -206,6 +209,31 @@ export const NoMatch: StoryObj<typeof NoMatchIcon> = {
},
};

export const EmptyState: StoryObj<typeof EmptyStateIcon> = {
parameters: {
docs: {
description: {
story: `\`\`\`ts
<EmptyState />
\`\`\``,
},
},
},

render: function Render() {
return (
<Stack direction={'column'} rowGap={'2rem'}>
{icons.emptyState.map((Icon) => (
<Stack direction={'row'} alignItems={'center'} columnGap={'2rem'}>
<Icon h="60px" w="60px" />
<Typography variant="body1">{Icon.name}</Typography>
</Stack>
))}
</Stack>
);
},
};

export const Status: StoryObj<typeof ErrorIcon> = {
parameters: {
docs: {
Expand Down
2 changes: 2 additions & 0 deletions ui/packages/ui-lib/src/icons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ export { NoMatchIcon } from './no-match';
export * from './generic-error';

export * from './status';

export * from './empty-state';
dianabirs marked this conversation as resolved.
Show resolved Hide resolved
40 changes: 26 additions & 14 deletions ui/packages/ui-lib/src/table/table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,27 @@ import KeyboardDoubleArrowDownIcon from '@mui/icons-material/KeyboardDoubleArrow
import MoreVertIcon from '@mui/icons-material/MoreVert';
import SearchIcon from '@mui/icons-material/Search';
import ViewColumnIcon from '@mui/icons-material/ViewColumn';
import { Alert } from '@mui/material';
import { Alert, Box } from '@mui/material';
import { MaterialReactTable, MRT_VisibilityState } from 'material-react-table';
import { useEffect } from 'react';
import { ICONS_OPACITY } from './table.constants';
import { TableProps } from './table.types';
import usePersistentColumnVisibility from './usePersistentColumnVisibility';

const noDataAlertWithMessage = (message?: string) => (
<Alert
severity="info"
sx={{
width: '100%',
height: '50px',
marginTop: 1,
marginBottom: 1,
}}
>
{message}
</Alert>
);

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function Table<T extends Record<string, any>>(props: TableProps<T>) {
const {
Expand All @@ -23,6 +38,7 @@ function Table<T extends Record<string, any>>(props: TableProps<T>) {
tableName,
state,
initialState,
emptyState,
...rest
} = props;
const [columnVisibility, setColumnVisibility] =
Expand Down Expand Up @@ -89,20 +105,16 @@ function Table<T extends Record<string, any>>(props: TableProps<T>) {
return (
<MaterialReactTable
renderEmptyRowsFallback={({ table: { getPreFilteredRowModel } }) => (
<Alert
severity="info"
sx={{
width: '100%',
height: 'max-content',
marginTop: 1,
marginBottom: 1,
}}
>
<>
{/* This means there was data before filtering, so we show the message of empty filtering result */}
{getPreFilteredRowModel().rows.length > 0
? emptyFilterResultsMessage
: noDataMessage}
</Alert>
{getPreFilteredRowModel().rows.length > 0 ? (
noDataAlertWithMessage(emptyFilterResultsMessage)
) : emptyState ? (
<Box>{emptyState}</Box>
) : (
noDataAlertWithMessage(noDataMessage)
)}
</>
)}
layoutMode="grid"
enablePagination={data.length > 10}
Expand Down
3 changes: 2 additions & 1 deletion ui/packages/ui-lib/src/table/table.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ import { type MaterialReactTableProps } from 'material-react-table';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export interface TableProps<T extends Record<string, any>>
extends MaterialReactTableProps<T> {
noDataMessage: string;
noDataMessage?: string;
emptyFilterResultsMessage?: string;
hideExpandAllIcon?: boolean;
tableName: string;
emptyState?: React.ReactNode;
}
Loading