0
Fork 0
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:
Xiao Yijun 2024-01-03 16:39:10 +08:00 committed by GitHub
parent 06c5795181
commit 8edbc0b4a8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 96 additions and 85 deletions

View file

@ -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');
}
}

View file

@ -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
);

View file

@ -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 = {

View file

@ -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';

View file

@ -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[];

View file

@ -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,
};

View file

@ -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} />
)}

View file

@ -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 />,

View file

@ -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;

View file

@ -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'
>;

View file

@ -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[];
};