From b755e95a75623c0200127243bd35fa340003706a Mon Sep 17 00:00:00 2001 From: Gao Sun Date: Thu, 30 May 2024 12:40:19 +0800 Subject: [PATCH] refactor(console): fix onboarding issues --- .../SignInExperiencePreview/index.tsx | 22 +- .../console/src/contexts/AppDataProvider.tsx | 19 +- .../pages/SignInExperience/Preview/index.tsx | 8 +- .../pages/SignInExperience/index.tsx | 228 ++++++++++-------- 4 files changed, 158 insertions(+), 119 deletions(-) diff --git a/packages/console/src/components/SignInExperiencePreview/index.tsx b/packages/console/src/components/SignInExperiencePreview/index.tsx index 5d7e6bf30..ca0a61ac8 100644 --- a/packages/console/src/components/SignInExperiencePreview/index.tsx +++ b/packages/console/src/components/SignInExperiencePreview/index.tsx @@ -23,13 +23,25 @@ type Props = { readonly mode: Theme; readonly language?: LanguageTag; readonly signInExperience?: SignInExperience; + /** + * The Logto endpoint to use for the preview. If not provided, the current tenant endpoint from + * the `AppDataContext` will be used. + */ + readonly endpoint?: URL; }; -function SignInExperiencePreview({ platform, mode, language = 'en', signInExperience }: Props) { +function SignInExperiencePreview({ + platform, + mode, + language = 'en', + signInExperience, + endpoint: endpointInput, +}: Props) { const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); const { customPhrases } = useUiLanguages(); const { tenantEndpoint } = useContext(AppDataContext); + const endpoint = endpointInput ?? tenantEndpoint; const previewRef = useRef(null); const { data: allConnectors } = useSWR('api/connectors'); const [iframeLoaded, setIframeLoaded] = useState(false); @@ -76,9 +88,9 @@ function SignInExperiencePreview({ platform, mode, language = 'en', signInExperi previewRef.current?.contentWindow?.postMessage( { sender: 'ac_preview', config: configForUiPage }, - tenantEndpoint?.origin ?? '' + endpoint?.origin ?? '' ); - }, [tenantEndpoint?.origin, configForUiPage, customPhrases]); + }, [endpoint?.origin, configForUiPage, customPhrases]); const iframeOnLoadEventHandler = useCallback(() => { setIframeLoaded(true); @@ -102,7 +114,7 @@ function SignInExperiencePreview({ platform, mode, language = 'en', signInExperi postPreviewMessage(); }, [iframeLoaded, postPreviewMessage]); - if (!tenantEndpoint) { + if (!endpoint) { return null; } @@ -131,7 +143,7 @@ function SignInExperiencePreview({ platform, mode, language = 'en', signInExperi ref={previewRef} // Allow all sandbox rules sandbox={undefined} - src={new URL('/sign-in?preview=true', tenantEndpoint).toString()} + src={new URL('/sign-in?preview=true', endpoint).toString()} tabIndex={-1} title={t('sign_in_exp.preview.title')} /> diff --git a/packages/console/src/contexts/AppDataProvider.tsx b/packages/console/src/contexts/AppDataProvider.tsx index 8ae3112af..f52562402 100644 --- a/packages/console/src/contexts/AppDataProvider.tsx +++ b/packages/console/src/contexts/AppDataProvider.tsx @@ -22,20 +22,17 @@ type AppData = { export const AppDataContext = createContext({}); +export const useTenantEndpoint = (tenantId: string) => { + return useSWRImmutable(`api/.well-known/endpoints/${tenantId}`, async (pathname) => { + const { user } = await ky.get(new URL(pathname, adminTenantEndpoint)).json<{ user: string }>(); + return new URL(user); + }); +}; + /** The context provider for the global app data. */ function AppDataProvider({ children }: Props) { const { currentTenantId } = useContext(TenantsContext); - - const { data: tenantEndpoint } = useSWRImmutable( - `api/.well-known/endpoints/${currentTenantId}`, - async (pathname) => { - const { user } = await ky - .get(new URL(pathname, adminTenantEndpoint)) - .json<{ user: string }>(); - return new URL(user); - } - ); - + const { data: tenantEndpoint } = useTenantEndpoint(currentTenantId); const memorizedContext = useMemo( () => ({ diff --git a/packages/console/src/onboarding/pages/SignInExperience/Preview/index.tsx b/packages/console/src/onboarding/pages/SignInExperience/Preview/index.tsx index 49086ff29..8a920b85d 100644 --- a/packages/console/src/onboarding/pages/SignInExperience/Preview/index.tsx +++ b/packages/console/src/onboarding/pages/SignInExperience/Preview/index.tsx @@ -12,9 +12,14 @@ import * as styles from './index.module.scss'; type Props = { readonly signInExperience?: SignInExperience; readonly className?: string; + /** + * The Logto endpoint to use for the preview. If not provided, the current tenant endpoint from + * the `AppDataContext` will be used. + */ + readonly endpoint?: URL; }; -function Preview({ signInExperience, className }: Props) { +function Preview({ signInExperience, className, endpoint }: Props) { const [currentTab, setCurrentTab] = useState(PreviewPlatform.DesktopWeb); return ( @@ -24,6 +29,7 @@ function Preview({ signInExperience, className }: Props) { platform={currentTab} mode={Theme.Light} signInExperience={signInExperience} + endpoint={endpoint} /> ); diff --git a/packages/console/src/onboarding/pages/SignInExperience/index.tsx b/packages/console/src/onboarding/pages/SignInExperience/index.tsx index 64bb27a47..366e7127c 100644 --- a/packages/console/src/onboarding/pages/SignInExperience/index.tsx +++ b/packages/console/src/onboarding/pages/SignInExperience/index.tsx @@ -4,12 +4,14 @@ import type { SignInExperience as SignInExperienceType, ConnectorResponse } from import { useCallback, useEffect, useMemo } from 'react'; import { Controller, useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; -import useSWR from 'swr'; +import { useParams } from 'react-router-dom'; +import useSWR, { SWRConfig } from 'swr'; import Tools from '@/assets/icons/tools.svg'; import ActionBar from '@/components/ActionBar'; import { GtagConversionId, reportConversion } from '@/components/Conversion/utils'; import PageMeta from '@/components/PageMeta'; +import { useTenantEndpoint } from '@/contexts/AppDataProvider'; import Button from '@/ds-components/Button'; import ColorPicker from '@/ds-components/ColorPicker'; import FormField from '@/ds-components/FormField'; @@ -39,6 +41,19 @@ import { authenticationOptions, identifierOptions } from './options'; import { defaultOnboardingSieFormData } from './sie-config-templates'; import { Authentication, type OnboardingSieFormData } from './types'; +const useCurrentTenantEndpoint = () => { + const { tenantId: currentTenantId } = useParams(); + + if (!currentTenantId) { + throw new Error( + 'No tenant ID param found in the current route. This hook should be used in a route with a tenant ID param.' + ); + } + + const { data } = useTenantEndpoint(currentTenantId); + return data; +}; + function SignInExperience() { const swrOptions = useTenantSwrOptions(); const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); @@ -54,6 +69,7 @@ function SignInExperience() { const { isReady: isUserAssetsServiceReady } = useUserAssetsService(); const { update } = useUserOnboardingData(); const { user } = useCurrentUser(); + const endpoint = useCurrentTenantEndpoint(); const enterAdminConsole = async () => { await update({ isOnboardingDone: true }); @@ -136,114 +152,122 @@ function SignInExperience() { } return ( -
- - -
-
- -
{t('cloud.sie.title')}
- { - for (const [key, value] of Object.entries(template)) { - // eslint-disable-next-line no-restricted-syntax - setValue(key as keyof OnboardingSieFormData, value, { shouldDirty: true }); - } - updateAuthenticationConfig(); - }} - /> - - {isUserAssetsServiceReady ? ( + +
+ + +
+
+ +
{t('cloud.sie.title')}
+ { + for (const [key, value] of Object.entries(template)) { + // eslint-disable-next-line no-restricted-syntax + setValue(key as keyof OnboardingSieFormData, value, { shouldDirty: true }); + } + updateAuthenticationConfig(); + }} + /> + + {isUserAssetsServiceReady ? ( + ( + + )} + /> + ) : ( + + !value || uriValidator(value) || t('errors.invalid_uri_format'), + })} + error={errors.logo?.message} + /> + )} + + ( - + render={({ field: { onChange, value } }) => ( + )} /> - ) : ( - - !value || uriValidator(value) || t('errors.invalid_uri_format'), - })} - error={errors.logo?.message} + + + ( + { + onChange(value); + updateAuthenticationConfig(); + }} + /> + )} /> - )} - - - ( - - )} + + + ( + + onboardingSieFormData.identifier !== SignInIdentifier.Username || + value === Authentication.Password + )} + onChange={onChange} + /> + )} + /> + + + ( + + )} + /> + +
+ {endpoint && ( + - - - ( - { - onChange(value); - updateAuthenticationConfig(); - }} - /> - )} - /> - - - ( - - onboardingSieFormData.identifier !== SignInIdentifier.Username || - value === Authentication.Password - )} - onChange={onChange} - /> - )} - /> - - - ( - - )} - /> - + )}
- -
- - -
-
-
-
+ + +
+
+
+
+ ); }