diff --git a/packages/console/src/ds-components/Table/TableLoading.module.scss b/packages/console/src/ds-components/Table/Skeleton/index.module.scss similarity index 86% rename from packages/console/src/ds-components/Table/TableLoading.module.scss rename to packages/console/src/ds-components/Table/Skeleton/index.module.scss index d6ecd7c30..9ce639640 100644 --- a/packages/console/src/ds-components/Table/TableLoading.module.scss +++ b/packages/console/src/ds-components/Table/Skeleton/index.module.scss @@ -1,6 +1,12 @@ @use '@/scss/underscore' as _; -.loading { +.rect { + @include _.shimmering-animation; + height: 26px; + max-width: 344px; +} + +.row { .itemPreview { display: flex; align-items: center; @@ -31,8 +37,6 @@ } .rect { - @include _.shimmering-animation; height: 32px; - max-width: 344px; } } diff --git a/packages/console/src/ds-components/Table/TableLoading.tsx b/packages/console/src/ds-components/Table/Skeleton/index.tsx similarity index 51% rename from packages/console/src/ds-components/Table/TableLoading.tsx rename to packages/console/src/ds-components/Table/Skeleton/index.tsx index 218839cad..97e11de4a 100644 --- a/packages/console/src/ds-components/Table/TableLoading.tsx +++ b/packages/console/src/ds-components/Table/Skeleton/index.tsx @@ -1,15 +1,34 @@ -import * as styles from './TableLoading.module.scss'; +import * as styles from './index.module.scss'; type Props = { columnSpans: number[]; + /** For the compact inline style table */ + isCompact?: boolean; }; -function TableLoading({ columnSpans }: Props) { +function Skeleton({ columnSpans, isCompact }: Props) { + if (isCompact) { + return ( + <> + {Array.from({ length: 2 }).map((_, rowIndex) => ( + // eslint-disable-next-line react/no-array-index-key + + {columnSpans.map((colSpan, columnIndex) => ( + // eslint-disable-next-line react/no-array-index-key + +
+ + ))} + + ))} + + ); + } return ( <> {Array.from({ length: 8 }).map((_, rowIndex) => ( // eslint-disable-next-line react/no-array-index-key - +
@@ -31,4 +50,4 @@ function TableLoading({ columnSpans }: Props) { ); } -export default TableLoading; +export default Skeleton; diff --git a/packages/console/src/ds-components/Table/index.tsx b/packages/console/src/ds-components/Table/index.tsx index 95360c036..9a523203e 100644 --- a/packages/console/src/ds-components/Table/index.tsx +++ b/packages/console/src/ds-components/Table/index.tsx @@ -9,9 +9,9 @@ import Pagination from '@/ds-components/Pagination'; import OverlayScrollbar from '../OverlayScrollbar'; +import Skeleton from './Skeleton'; import TableEmptyWrapper from './TableEmptyWrapper'; import TableError from './TableError'; -import TableLoading from './TableLoading'; import * as styles from './index.module.scss'; import type { Column, RowGroup } from './types'; @@ -35,6 +35,7 @@ export type Props< placeholder?: ReactNode; loadingSkeleton?: ReactNode; errorMessage?: string; + /** The inline style table that is usually embedded in other card containers, has rounded-corner border */ hasBorder?: boolean; onRetry?: () => void; /** A footer that will be rendered on the bottom-left of the table. */ @@ -110,7 +111,10 @@ function Table< {isLoading && (loadingSkeleton ?? ( - colSpan ?? 1)} /> + colSpan ?? 1)} + /> ))} {hasError && ( diff --git a/packages/console/src/pages/Organizations/OrganizationsTable/index.tsx b/packages/console/src/pages/Organizations/OrganizationsTable/index.tsx index 74319ee88..0baaccd48 100644 --- a/packages/console/src/pages/Organizations/OrganizationsTable/index.tsx +++ b/packages/console/src/pages/Organizations/OrganizationsTable/index.tsx @@ -27,10 +27,11 @@ const pathname = '/organizations'; const apiPathname = 'api/organizations'; type Props = { + isLoading: boolean; onCreate: () => void; }; -function OrganizationsTable({ onCreate }: Props) { +function OrganizationsTable({ isLoading, onCreate }: Props) { const [keyword, setKeyword] = useState(''); const [page, setPage] = useState(1); const { data: response, error } = useSWR<[OrganizationWithFeatured[], number], RequestError>( @@ -42,14 +43,14 @@ function OrganizationsTable({ onCreate }: Props) { }) ); const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); - const isLoading = !response && !error; + const isTableLoading = isLoading || (!response && !error); const [data, totalCount] = response ?? [[], 0]; const { navigate } = useTenantPathname(); return ( } rowGroups={[{ key: 'data', data }]} rowClickHandler={({ id }) => { diff --git a/packages/console/src/pages/Organizations/TemplateTable/index.tsx b/packages/console/src/pages/Organizations/TemplateTable/index.tsx index a8a863a3d..f4cd63789 100644 --- a/packages/console/src/pages/Organizations/TemplateTable/index.tsx +++ b/packages/console/src/pages/Organizations/TemplateTable/index.tsx @@ -1,14 +1,11 @@ import { type AdminConsoleKey } from '@logto/phrases'; -import classNames from 'classnames'; import { type FieldValues, type FieldPath } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import CirclePlus from '@/assets/icons/circle-plus.svg'; import Plus from '@/assets/icons/plus.svg'; -import EmptyDataPlaceholder from '@/components/EmptyDataPlaceholder'; import Button from '@/ds-components/Button'; import DynamicT from '@/ds-components/DynamicT'; -import { Ring as Spinner } from '@/ds-components/Spinner'; import Table from '@/ds-components/Table'; import { type Column } from '@/ds-components/Table/types'; @@ -49,12 +46,8 @@ function TemplateTable< isLoading, onPageChange, }: Props) { - const hasData = !isLoading && data.length > 0; const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); - - if (isLoading) { - ; - } + const noData = !isLoading && data.length === 0; return (
@@ -63,10 +56,19 @@ function TemplateTable< )} - {hasData && ( + {onAdd && noData && ( + <> + {name && ( +
+ {t('organizations.empty_placeholder', { entity: String(t(name)).toLowerCase() })} +
+ )} +
} isLoading={isLoading} rowGroups={[ { @@ -94,16 +96,6 @@ function TemplateTable< } /> )} - {onAdd && !hasData && ( - <> - {name && ( -
- {t('organizations.empty_placeholder', { entity: String(t(name)).toLowerCase() })} -
- )} - - {tableColumns.map(({ colSpan }, columnIndex) => ( - // eslint-disable-next-line react/no-array-index-key - - ))} - - ))} - - } /> diff --git a/packages/console/src/pages/UserDetails/UserSettings/UserMfaVerifications/index.tsx b/packages/console/src/pages/UserDetails/UserSettings/UserMfaVerifications/index.tsx index b3ca58532..561b27e90 100644 --- a/packages/console/src/pages/UserDetails/UserSettings/UserMfaVerifications/index.tsx +++ b/packages/console/src/pages/UserDetails/UserSettings/UserMfaVerifications/index.tsx @@ -61,7 +61,7 @@ function UserMfaVerifications({ userId }: Props) { {t(mfaVerifications?.length ? 'field_description' : 'field_description_empty')} )} - {(Boolean(mfaVerifications?.length) || error) && ( + {(isLoading || Boolean(mfaVerifications?.length) || error) && (
-
-
0); + return (
-
- {t( - displayConnectors && displayConnectors.length > 0 - ? 'user_details.connectors.connected' - : 'user_details.connectors.not_connected' - )} -
- {displayConnectors && displayConnectors.length > 0 && ( + {!isLoading && !error && ( +
+ {t( + hasConnectors + ? 'user_details.connectors.connected' + : 'user_details.connectors.not_connected' + )} +
+ )} + {(isLoading || hasConnectors || error) && (
0; + const hasLinkedSsoIdentities = Boolean(displaySsoConnectors && displaySsoConnectors.length > 0); return (
-
- {t( - hasLinkedSsoIdentities - ? 'user_details.sso_connectors.connected' - : 'user_details.sso_connectors.not_connected' - )} -
- {hasLinkedSsoIdentities && ( + {!isLoading && !error && ( +
+ {t( + hasLinkedSsoIdentities + ? 'user_details.sso_connectors.connected' + : 'user_details.sso_connectors.not_connected' + )} +
+ )} + {(isLoading || hasLinkedSsoIdentities || error) && (