mirror of
https://github.com/logto-io/logto.git
synced 2025-02-24 22:05:56 -05:00
refactor(console): refactor onboarding sie form (#5194)
This commit is contained in:
parent
06c5795181
commit
8edbc0b4a8
17 changed files with 96 additions and 85 deletions
|
@ -33,7 +33,7 @@
|
|||
&:not(:active),
|
||||
&:active {
|
||||
&:hover {
|
||||
background: var(--color-layer-1) center / 90% no-repeat url('raw:../../../../assets/images/fireworks.svg');
|
||||
background: var(--color-layer-1) center / 90% no-repeat url('raw:../../../assets/images/fireworks.svg');
|
||||
}
|
||||
}
|
||||
|
|
@ -6,14 +6,14 @@ import Bulb from '@/assets/icons/bulb.svg';
|
|||
import LightBulb from '@/assets/icons/light-bulb.svg';
|
||||
import Button from '@/ds-components/Button';
|
||||
import useConnectorGroups from '@/hooks/use-connector-groups';
|
||||
import type { OnboardingSieConfig } from '@/onboarding/types';
|
||||
|
||||
import { randomSieConfigTemplate } from '../../sie-config-templates';
|
||||
import { randomSieFormDataTemplate } from '../sie-config-templates';
|
||||
import { type OnboardingSieFormData } from '../types';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
type Props = {
|
||||
onInspired: (template: OnboardingSieConfig) => void;
|
||||
onInspired: (template: OnboardingSieFormData) => void;
|
||||
};
|
||||
|
||||
function InspireMe({ onInspired }: Props) {
|
||||
|
@ -29,7 +29,7 @@ function InspireMe({ onInspired }: Props) {
|
|||
.map(({ target }) => target) ?? [];
|
||||
|
||||
const handleInspire = () => {
|
||||
const { template, templateIndex } = randomSieConfigTemplate(
|
||||
const { template, templateIndex } = randomSieFormDataTemplate(
|
||||
lastTemplateIndex,
|
||||
availableSocialTargets
|
||||
);
|
|
@ -6,8 +6,7 @@ import { useState } from 'react';
|
|||
import SignInExperiencePreview from '@/components/SignInExperiencePreview';
|
||||
import { PreviewPlatform } from '@/components/SignInExperiencePreview/types';
|
||||
|
||||
import PlatformTabs from '../PlatformTabs';
|
||||
|
||||
import PlatformTabs from './PlatformTabs';
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
type Props = {
|
|
@ -1,6 +1,6 @@
|
|||
import * as pageLayout from '@/onboarding/scss/layout.module.scss';
|
||||
|
||||
import * as sieLayout from '../../index.module.scss';
|
||||
import * as sieLayout from '../index.module.scss';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
|
|
@ -7,7 +7,7 @@ import useConnectorGroups from '@/hooks/use-connector-groups';
|
|||
import { MultiCardSelector } from '@/onboarding/components/CardSelector';
|
||||
import type { MultiCardSelectorOption } from '@/onboarding/components/CardSelector';
|
||||
|
||||
import { fakeSocialTargetOptions } from '../../options';
|
||||
import { fakeSocialTargetOptions } from '../options';
|
||||
|
||||
type Props = {
|
||||
value: string[];
|
|
@ -2,12 +2,9 @@ import type { SignInExperience } from '@logto/schemas';
|
|||
import { SignInIdentifier } from '@logto/schemas';
|
||||
import { conditional } from '@silverhand/essentials';
|
||||
|
||||
import type { OnboardingSieConfig } from '@/onboarding/types';
|
||||
import { Authentication } from '@/onboarding/types';
|
||||
import { Authentication, type UpdateOnboardingSieData, type OnboardingSieFormData } from './types';
|
||||
|
||||
const signInExperienceToOnboardSieConfig = (
|
||||
signInExperience: SignInExperience
|
||||
): OnboardingSieConfig => {
|
||||
const fromSignInExperience = (signInExperience: SignInExperience): OnboardingSieFormData => {
|
||||
const {
|
||||
color: { primaryColor },
|
||||
branding: { logoUrl: logo },
|
||||
|
@ -39,12 +36,12 @@ const signInExperienceToOnboardSieConfig = (
|
|||
};
|
||||
};
|
||||
|
||||
const onboardSieConfigToSignInExperience = (
|
||||
config: OnboardingSieConfig,
|
||||
basedConfig: SignInExperience
|
||||
const toSignInExperience = (
|
||||
formData: OnboardingSieFormData,
|
||||
initialSignInExperience: SignInExperience
|
||||
): SignInExperience => {
|
||||
const { logo, color: onboardConfigColor, identifier, authentications, socialTargets } = config;
|
||||
const { color: baseColorConfig, branding: baseBranding } = basedConfig;
|
||||
const { logo, color: onboardConfigColor, identifier, authentications, socialTargets } = formData;
|
||||
const { color: initialColor, branding: initialBranding } = initialSignInExperience;
|
||||
|
||||
// Map to sign-up config
|
||||
const shouldSetPasswordAtSignUp =
|
||||
|
@ -59,14 +56,14 @@ const onboardSieConfigToSignInExperience = (
|
|||
authentications.includes(Authentication.VerificationCode);
|
||||
|
||||
const signInExperience: SignInExperience = {
|
||||
...basedConfig,
|
||||
...initialSignInExperience,
|
||||
branding: {
|
||||
...baseBranding,
|
||||
...initialBranding,
|
||||
logoUrl: conditional(logo?.length && logo),
|
||||
darkLogoUrl: conditional(logo?.length && logo),
|
||||
},
|
||||
color: {
|
||||
...baseColorConfig,
|
||||
...initialColor,
|
||||
primaryColor: onboardConfigColor,
|
||||
},
|
||||
signUp: {
|
||||
|
@ -90,7 +87,26 @@ const onboardSieConfigToSignInExperience = (
|
|||
return signInExperience;
|
||||
};
|
||||
|
||||
export const parser = {
|
||||
signInExperienceToOnboardSieConfig,
|
||||
onboardSieConfigToSignInExperience,
|
||||
const toUpdateOnboardingSieData = (
|
||||
formData: OnboardingSieFormData,
|
||||
initialSignInExperience: SignInExperience
|
||||
): UpdateOnboardingSieData => {
|
||||
const { color, branding, signUp, signIn, socialSignInConnectorTargets } = toSignInExperience(
|
||||
formData,
|
||||
initialSignInExperience
|
||||
);
|
||||
|
||||
return {
|
||||
color,
|
||||
branding,
|
||||
signUp,
|
||||
signIn,
|
||||
socialSignInConnectorTargets,
|
||||
};
|
||||
};
|
||||
|
||||
export const formDataParser = {
|
||||
fromSignInExperience,
|
||||
toSignInExperience,
|
||||
toUpdateOnboardingSieData,
|
||||
};
|
|
@ -24,21 +24,21 @@ import useUserAssetsService from '@/hooks/use-user-assets-service';
|
|||
import { CardSelector, MultiCardSelector } from '@/onboarding/components/CardSelector';
|
||||
import useUserOnboardingData from '@/onboarding/hooks/use-user-onboarding-data';
|
||||
import * as pageLayout from '@/onboarding/scss/layout.module.scss';
|
||||
import type { OnboardingSieConfig } from '@/onboarding/types';
|
||||
import { Authentication, OnboardingPage } from '@/onboarding/types';
|
||||
import { OnboardingPage } from '@/onboarding/types';
|
||||
import { getOnboardingPage } from '@/onboarding/utils';
|
||||
import { trySubmitSafe } from '@/utils/form';
|
||||
import { buildUrl } from '@/utils/url';
|
||||
import { uriValidator } from '@/utils/validator';
|
||||
|
||||
import InspireMe from './components/InspireMe';
|
||||
import Preview from './components/Preview';
|
||||
import Skeleton from './components/Skeleton';
|
||||
import SocialSelector from './components/SocialSelector';
|
||||
import InspireMe from './InspireMe';
|
||||
import Preview from './Preview';
|
||||
import Skeleton from './Skeleton';
|
||||
import SocialSelector from './SocialSelector';
|
||||
import { formDataParser } from './form-data-parser';
|
||||
import * as styles from './index.module.scss';
|
||||
import { authenticationOptions, identifierOptions } from './options';
|
||||
import { defaultOnboardingSieConfig } from './sie-config-templates';
|
||||
import { parser } from './utils';
|
||||
import { defaultOnboardingSieFormData } from './sie-config-templates';
|
||||
import { Authentication, type OnboardingSieFormData } from './types';
|
||||
|
||||
function SignInExperience() {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
|
@ -70,7 +70,7 @@ function SignInExperience() {
|
|||
getValues,
|
||||
setValue,
|
||||
formState: { isSubmitting, errors },
|
||||
} = useForm<OnboardingSieConfig>({ defaultValues: defaultOnboardingSieConfig });
|
||||
} = useForm<OnboardingSieFormData>({ defaultValues: defaultOnboardingSieFormData });
|
||||
|
||||
const updateAuthenticationConfig = useCallback(() => {
|
||||
const identifier = getValues('identifier');
|
||||
|
@ -82,21 +82,21 @@ function SignInExperience() {
|
|||
|
||||
useEffect(() => {
|
||||
if (signInExperience) {
|
||||
reset(parser.signInExperienceToOnboardSieConfig(signInExperience));
|
||||
reset(formDataParser.fromSignInExperience(signInExperience));
|
||||
updateAuthenticationConfig();
|
||||
}
|
||||
}, [reset, signInExperience, updateAuthenticationConfig]);
|
||||
|
||||
const onboardingSieConfig = watch();
|
||||
const onboardingSieFormData = watch();
|
||||
|
||||
const previewSieConfig = useMemo(() => {
|
||||
if (signInExperience) {
|
||||
return parser.onboardSieConfigToSignInExperience(onboardingSieConfig, signInExperience);
|
||||
return formDataParser.toSignInExperience(onboardingSieFormData, signInExperience);
|
||||
}
|
||||
}, [onboardingSieConfig, signInExperience]);
|
||||
}, [onboardingSieFormData, signInExperience]);
|
||||
|
||||
const submit = (onSuccess: () => void) =>
|
||||
trySubmitSafe(async (formData: OnboardingSieConfig) => {
|
||||
trySubmitSafe(async (formData: OnboardingSieFormData) => {
|
||||
if (!signInExperience) {
|
||||
return;
|
||||
}
|
||||
|
@ -116,19 +116,9 @@ function SignInExperience() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: extract `mfa` since it will not be updated on the SIE config page.
|
||||
* This is a temporary solution, we will split `SignInExperience` type into multiple types
|
||||
* when the SIE config page is split into multiple pages.
|
||||
*/
|
||||
const { mfa, ...payload } = parser.onboardSieConfigToSignInExperience(
|
||||
formData,
|
||||
signInExperience
|
||||
);
|
||||
|
||||
const updatedData = await api
|
||||
.patch(buildUrl('api/sign-in-exp', { removeUnusedDemoSocialConnector: '1' }), {
|
||||
json: payload,
|
||||
json: formDataParser.toUpdateOnboardingSieData(formData, signInExperience),
|
||||
})
|
||||
.json<SignInExperienceType>();
|
||||
|
||||
|
@ -153,7 +143,7 @@ function SignInExperience() {
|
|||
onInspired={(template) => {
|
||||
for (const [key, value] of Object.entries(template)) {
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
setValue(key as keyof OnboardingSieConfig, value, { shouldDirty: true });
|
||||
setValue(key as keyof OnboardingSieFormData, value, { shouldDirty: true });
|
||||
}
|
||||
updateAuthenticationConfig();
|
||||
}}
|
||||
|
@ -207,7 +197,7 @@ function SignInExperience() {
|
|||
<Controller
|
||||
name="authentications"
|
||||
control={control}
|
||||
defaultValue={defaultOnboardingSieConfig.authentications}
|
||||
defaultValue={defaultOnboardingSieFormData.authentications}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<MultiCardSelector
|
||||
isNotAllowEmpty
|
||||
|
@ -215,7 +205,7 @@ function SignInExperience() {
|
|||
value={value}
|
||||
options={authenticationOptions.filter(
|
||||
({ value }) =>
|
||||
onboardingSieConfig.identifier !== SignInIdentifier.Username ||
|
||||
onboardingSieFormData.identifier !== SignInIdentifier.Username ||
|
||||
value === Authentication.Password
|
||||
)}
|
||||
onChange={onChange}
|
||||
|
@ -227,7 +217,7 @@ function SignInExperience() {
|
|||
<Controller
|
||||
name="socialTargets"
|
||||
control={control}
|
||||
defaultValue={defaultOnboardingSieConfig.socialTargets}
|
||||
defaultValue={defaultOnboardingSieFormData.socialTargets}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<SocialSelector value={value ?? []} onChange={onChange} />
|
||||
)}
|
||||
|
|
|
@ -9,7 +9,6 @@ import type {
|
|||
MultiCardSelectorOption,
|
||||
CardSelectorOption,
|
||||
} from '@/onboarding/components/CardSelector';
|
||||
import { Authentication } from '@/onboarding/types';
|
||||
|
||||
import Apple from '../../assets/icons/social-apple.svg';
|
||||
import Facebook from '../../assets/icons/social-facebook.svg';
|
||||
|
@ -18,6 +17,8 @@ import Microsoft from '../../assets/icons/social-microsoft.svg';
|
|||
import Oidc from '../../assets/icons/social-oidc.svg';
|
||||
import Smal from '../../assets/icons/social-smal.svg';
|
||||
|
||||
import { Authentication } from './types';
|
||||
|
||||
export const identifierOptions: CardSelectorOption[] = [
|
||||
{
|
||||
icon: <Envelop />,
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
import { SignInIdentifier } from '@logto/schemas';
|
||||
|
||||
import type { OnboardingSieConfig } from '@/onboarding/types';
|
||||
import { Authentication } from '@/onboarding/types';
|
||||
import type { OnboardingSieFormData } from './types';
|
||||
import { Authentication } from './types';
|
||||
|
||||
const assetsUrl =
|
||||
'https://logtodev.blob.core.windows.net/public-blobs/admin/BY4BCq8GvfBF/2023/03/10';
|
||||
|
||||
export const defaultOnboardingSieConfig: OnboardingSieConfig = {
|
||||
export const defaultOnboardingSieFormData: OnboardingSieFormData = {
|
||||
color: '#5D34F2',
|
||||
identifier: SignInIdentifier.Email,
|
||||
authentications: [Authentication.Password],
|
||||
};
|
||||
|
||||
// Email + password sign-up; password sign-in
|
||||
const configTemplate1: OnboardingSieConfig = {
|
||||
const formDataTemplate1: OnboardingSieFormData = {
|
||||
logo: `${assetsUrl}/tVCAHjAB/logo1.png`,
|
||||
color: '#19BEFD',
|
||||
identifier: SignInIdentifier.Email,
|
||||
|
@ -21,7 +21,7 @@ const configTemplate1: OnboardingSieConfig = {
|
|||
};
|
||||
|
||||
// Email + password sign-up; password + code sign-in
|
||||
const configTemplate2: OnboardingSieConfig = {
|
||||
const formDataTemplate2: OnboardingSieFormData = {
|
||||
logo: `${assetsUrl}/IcI0snBP/logo3.png`,
|
||||
color: '#FF5449',
|
||||
identifier: SignInIdentifier.Email,
|
||||
|
@ -29,7 +29,7 @@ const configTemplate2: OnboardingSieConfig = {
|
|||
};
|
||||
|
||||
// Email + code sign-up; code sign-in
|
||||
const configTemplate3: OnboardingSieConfig = {
|
||||
const formDataTemplate3: OnboardingSieFormData = {
|
||||
logo: `${assetsUrl}/7UQyvuFc/logo4.png`,
|
||||
color: '#CA4E96',
|
||||
identifier: SignInIdentifier.Email,
|
||||
|
@ -37,29 +37,29 @@ const configTemplate3: OnboardingSieConfig = {
|
|||
};
|
||||
|
||||
// Username sign-up; password sign-in
|
||||
const configTemplate4: OnboardingSieConfig = {
|
||||
const formDataTemplate4: OnboardingSieFormData = {
|
||||
logo: `${assetsUrl}/uLoMzrlz/logo7.png`,
|
||||
color: '#FF5449',
|
||||
identifier: SignInIdentifier.Username,
|
||||
authentications: [Authentication.Password],
|
||||
};
|
||||
|
||||
const sieConfigTemplates: OnboardingSieConfig[] = [
|
||||
configTemplate1,
|
||||
configTemplate2,
|
||||
configTemplate3,
|
||||
configTemplate4,
|
||||
const formDataTemplates: OnboardingSieFormData[] = [
|
||||
formDataTemplate1,
|
||||
formDataTemplate2,
|
||||
formDataTemplate3,
|
||||
formDataTemplate4,
|
||||
];
|
||||
|
||||
export const randomSieConfigTemplate = (
|
||||
export const randomSieFormDataTemplate = (
|
||||
lastTemplateIndex: number | undefined,
|
||||
availableSocialTargets: string[]
|
||||
) => {
|
||||
// Get random template
|
||||
const randomIndex = Math.floor(Math.random() * sieConfigTemplates.length);
|
||||
const randomIndex = Math.floor(Math.random() * formDataTemplates.length);
|
||||
const index =
|
||||
randomIndex === lastTemplateIndex ? (randomIndex + 1) % sieConfigTemplates.length : randomIndex;
|
||||
const template = sieConfigTemplates[index] ?? configTemplate1;
|
||||
randomIndex === lastTemplateIndex ? (randomIndex + 1) % formDataTemplates.length : randomIndex;
|
||||
const template = formDataTemplates[index] ?? formDataTemplate1;
|
||||
|
||||
// Get 2 or 3 random social targets
|
||||
const randomCount = Math.floor(Math.random() * 2) + 2;
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
import { type SignInExperience, type SignInIdentifier } from '@logto/schemas';
|
||||
|
||||
export enum Authentication {
|
||||
Password = 'password',
|
||||
VerificationCode = 'verificationCode',
|
||||
}
|
||||
|
||||
export type OnboardingSieFormData = {
|
||||
logo?: string;
|
||||
color: string;
|
||||
identifier: SignInIdentifier;
|
||||
authentications: Authentication[];
|
||||
socialTargets?: string[];
|
||||
};
|
||||
|
||||
export type UpdateOnboardingSieData = Pick<
|
||||
SignInExperience,
|
||||
'branding' | 'color' | 'signUp' | 'signIn' | 'socialSignInConnectorTargets'
|
||||
>;
|
|
@ -1,4 +1,3 @@
|
|||
import type { SignInIdentifier } from '@logto/schemas';
|
||||
import { z } from 'zod';
|
||||
|
||||
export enum OnboardingRoute {
|
||||
|
@ -95,16 +94,3 @@ export const userOnboardingDataGuard = z.object({
|
|||
});
|
||||
|
||||
export type UserOnboardingData = z.infer<typeof userOnboardingDataGuard>;
|
||||
|
||||
export enum Authentication {
|
||||
Password = 'password',
|
||||
VerificationCode = 'verificationCode',
|
||||
}
|
||||
|
||||
export type OnboardingSieConfig = {
|
||||
logo?: string;
|
||||
color: string;
|
||||
identifier: SignInIdentifier;
|
||||
authentications: Authentication[];
|
||||
socialTargets?: string[];
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue