From a671a4d9bfd0229fd12b4dd290bd1dcda4da3dda Mon Sep 17 00:00:00 2001 From: Charles Zhao Date: Wed, 31 Jan 2024 16:05:51 +0800 Subject: [PATCH] feat(console): add loading masks for protected app (#5357) --- .../ProtectedAppSettings/index.module.scss | 25 +++++++++++++ .../ProtectedAppSettings/index.tsx | 24 ++++++++++--- .../index.module.scss | 36 +++++++++++++++++++ .../ProtectedAppCreationForm/index.tsx | 15 +++++++- 4 files changed, 95 insertions(+), 5 deletions(-) diff --git a/packages/console/src/pages/ApplicationDetails/ApplicationDetailsContent/ProtectedAppSettings/index.module.scss b/packages/console/src/pages/ApplicationDetails/ApplicationDetailsContent/ProtectedAppSettings/index.module.scss index e8a0a31c2..7c8b0e157 100644 --- a/packages/console/src/pages/ApplicationDetails/ApplicationDetailsContent/ProtectedAppSettings/index.module.scss +++ b/packages/console/src/pages/ApplicationDetails/ApplicationDetailsContent/ProtectedAppSettings/index.module.scss @@ -115,3 +115,28 @@ padding-inline-start: _.unit(5); } } + +.loadingSkeleton { + width: 100%; + display: flex; + flex-direction: column; + gap: _.unit(2); + padding: _.unit(6); + border-radius: 12px; + border: 1px solid var(--color-divider); + + .bone { + @include _.shimmering-animation; + border-radius: 8px; + } + + .title { + width: 100%; + height: 20px; + } + + .description { + width: 100%; + height: 16px; + } +} diff --git a/packages/console/src/pages/ApplicationDetails/ApplicationDetailsContent/ProtectedAppSettings/index.tsx b/packages/console/src/pages/ApplicationDetails/ApplicationDetailsContent/ProtectedAppSettings/index.tsx index 027ea613b..dfb90df1d 100644 --- a/packages/console/src/pages/ApplicationDetails/ApplicationDetailsContent/ProtectedAppSettings/index.tsx +++ b/packages/console/src/pages/ApplicationDetails/ApplicationDetailsContent/ProtectedAppSettings/index.tsx @@ -5,7 +5,8 @@ import { type CustomDomain as CustomDomainType, } from '@logto/schemas'; import { cond } from '@silverhand/essentials'; -import { type ChangeEvent } from 'react'; +import classNames from 'classnames'; +import { type ChangeEvent, useState } from 'react'; import { Controller, useFieldArray, useFormContext } from 'react-hook-form'; import { Trans, useTranslation } from 'react-i18next'; import useSWR from 'swr'; @@ -41,10 +42,15 @@ const maxSessionDuration = 365; // 1 year function ProtectedAppSettings({ data }: Props) { const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); const { getDocumentationUrl } = useDocumentationUrl(); - const { data: customDomains = [], mutate } = useSWR( + const { + data: customDomains = [], + isLoading: isLoadingCustomDomain, + mutate, + } = useSWR( `api/applications/${data.id}/protected-app-metadata/custom-domains` ); const api = useApi(); + const [isDeletingCustomDomain, setIsDeletingCustomDomain] = useState(false); const { control, @@ -69,6 +75,8 @@ function ProtectedAppSettings({ data }: Props) { customDomain?.status === DomainStatus.Active ? customDomain.domain : host }`; + const showCustomDomainLoadingMask = isLoadingCustomDomain || isDeletingCustomDomain; + return ( <> {!!host && ( - {!customDomain && ( + {showCustomDomainLoadingMask && ( +
+
+
+
+ )} + {!customDomain && !showCustomDomainLoadingMask && ( { @@ -136,15 +150,17 @@ function ProtectedAppSettings({ data }: Props) { }} /> )} - {customDomain && ( + {customDomain && !showCustomDomainLoadingMask && ( { + setIsDeletingCustomDomain(true); await api.delete( `api/applications/${data.id}/protected-app-metadata/custom-domains/${customDomain.domain}` ); + setIsDeletingCustomDomain(false); void mutate(); }} /> diff --git a/packages/console/src/pages/GetStarted/ProtectedAppCreationForm/index.module.scss b/packages/console/src/pages/GetStarted/ProtectedAppCreationForm/index.module.scss index 86722de64..cc86ebb02 100644 --- a/packages/console/src/pages/GetStarted/ProtectedAppCreationForm/index.module.scss +++ b/packages/console/src/pages/GetStarted/ProtectedAppCreationForm/index.module.scss @@ -61,3 +61,39 @@ } } } + +.loadingSkeleton { + display: flex; + align-items: center; + gap: _.unit(6); + padding: _.unit(6) _.unit(8); + border-radius: 12px; + border: 1px solid var(--color-divider); + + .bone { + @include _.shimmering-animation; + border-radius: 8px; + } + + .columnWrapper { + flex: 1; + display: flex; + flex-direction: column; + gap: _.unit(2); + } + + .icon { + width: 48px; + height: 48px; + } + + .title { + width: 100%; + height: 20px; + } + + .description { + width: 100%; + height: 16px; + } +} diff --git a/packages/console/src/pages/GetStarted/ProtectedAppCreationForm/index.tsx b/packages/console/src/pages/GetStarted/ProtectedAppCreationForm/index.tsx index 571012dd9..835eecabf 100644 --- a/packages/console/src/pages/GetStarted/ProtectedAppCreationForm/index.tsx +++ b/packages/console/src/pages/GetStarted/ProtectedAppCreationForm/index.tsx @@ -1,4 +1,5 @@ import { type Application } from '@logto/schemas'; +import classNames from 'classnames'; import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { Link } from 'react-router-dom'; @@ -16,7 +17,7 @@ import * as styles from './index.module.scss'; function ProtectedAppCreationForm() { const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); const { getTo } = useTenantPathname(); - const { data, mutate } = useSWR('api/applications?types=Protected'); + const { data, isLoading, mutate } = useSWR('api/applications?types=Protected'); const { hasAppsReachedLimit } = useApplicationsUsage(); const hasProtectedApps = !!data?.length; const showCreationForm = !hasProtectedApps && !hasAppsReachedLimit; @@ -28,6 +29,18 @@ function ProtectedAppCreationForm() { [data, mutate] ); + if (isLoading) { + return ( +
+
+
+
+
+
+
+ ); + } + return (