mirror of
https://github.com/logto-io/logto.git
synced 2025-04-14 23:11:31 -05:00
feat(console): onboarding sie preview (#3296)
This commit is contained in:
parent
ec65d46826
commit
1efb8097ef
24 changed files with 444 additions and 36 deletions
|
@ -10,6 +10,7 @@ type Props = {
|
|||
options: Option[];
|
||||
value: string[];
|
||||
onChange: (value: string[]) => void;
|
||||
isNotAllowEmpty?: boolean;
|
||||
className?: string;
|
||||
optionClassName?: string;
|
||||
};
|
||||
|
@ -18,12 +19,17 @@ const MultiCardSelector = ({
|
|||
options,
|
||||
value: selectedValues,
|
||||
onChange,
|
||||
isNotAllowEmpty = false,
|
||||
className,
|
||||
optionClassName,
|
||||
}: Props) => {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
|
||||
const onToggle = (value: string) => {
|
||||
if (selectedValues.includes(value) && selectedValues.length === 1 && isNotAllowEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
onChange(
|
||||
selectedValues.includes(value)
|
||||
? selectedValues.filter((selected) => selected !== value)
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import { SignInIdentifier } from '@logto/schemas';
|
||||
|
||||
import type { OnboardingSieConfig } from '../types';
|
||||
import type { OnboardSieConfig } from '../types';
|
||||
import { Authentication } from '../types';
|
||||
|
||||
export const reservationLink = 'https://calendly.com/logto/30min';
|
||||
|
||||
export const defaultOnboardingSieConfig: OnboardingSieConfig = {
|
||||
export const defaultOnboardingSieConfig: OnboardSieConfig = {
|
||||
color: '#5D34F2',
|
||||
identifier: SignInIdentifier.Email,
|
||||
authentications: [Authentication.Password],
|
||||
|
|
|
@ -12,7 +12,6 @@ import * as pageLayout from '@/cloud/scss/layout.module.scss';
|
|||
import Button from '@/components/Button';
|
||||
import Divider from '@/components/Divider';
|
||||
import OverlayScrollbar from '@/components/OverlayScrollbar';
|
||||
import { getBasename } from '@/consts';
|
||||
import { AppEndpointsContext } from '@/contexts/AppEndpointsProvider';
|
||||
|
||||
import { OnboardPage } from '../../types';
|
||||
|
@ -28,7 +27,7 @@ const Congrats = () => {
|
|||
|
||||
const enterAdminConsole = async () => {
|
||||
await update({ hasOnboard: true });
|
||||
navigate(getBasename());
|
||||
navigate('/');
|
||||
};
|
||||
|
||||
const handleBack = () => {
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.tab {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: 6px;
|
||||
font: var(--font-label-2);
|
||||
padding: _.unit(1) _.unit(2);
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
|
||||
.icon {
|
||||
color: var(--color-primary);
|
||||
margin-right: _.unit(2);
|
||||
|
||||
> svg {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
&.selected {
|
||||
color: var(--color-layer-1);
|
||||
background-color: var(--color-inverse-primary);
|
||||
|
||||
.icon {
|
||||
color: var(--color-static-white);
|
||||
opacity: 70%;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.selected):hover {
|
||||
background-color: var(--color-hover-variant);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
import type { AdminConsoleKey } from '@logto/phrases';
|
||||
import classNames from 'classnames';
|
||||
import type { ReactNode } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import type { PreviewPlatform } from '@/components/SignInExperiencePreview/types';
|
||||
import { onKeyDownHandler } from '@/utils/a11y';
|
||||
|
||||
import * as styles from './PlatformTab.module.scss';
|
||||
|
||||
type Props = {
|
||||
isSelected: boolean;
|
||||
icon: ReactNode;
|
||||
title: AdminConsoleKey;
|
||||
tab: PreviewPlatform;
|
||||
onClick: (tab: PreviewPlatform) => void;
|
||||
};
|
||||
|
||||
const PlatformTab = ({ isSelected, icon, title, tab, onClick }: Props) => {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
|
||||
return (
|
||||
<div
|
||||
role="tab"
|
||||
tabIndex={0}
|
||||
className={classNames(styles.tab, isSelected && styles.selected)}
|
||||
onClick={() => {
|
||||
onClick(tab);
|
||||
}}
|
||||
onKeyDown={onKeyDownHandler(() => {
|
||||
onClick(tab);
|
||||
})}
|
||||
>
|
||||
<span className={styles.icon}>{icon}</span>
|
||||
{t(title)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PlatformTab;
|
|
@ -0,0 +1,12 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.container {
|
||||
border-radius: 8px;
|
||||
background-color: var(--color-layer-1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: _.unit(1);
|
||||
gap: 12px;
|
||||
border: 1px solid var(--color-surface-5);
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
import Native from '@/assets/images/connector-platform-icon-native.svg';
|
||||
import Web from '@/assets/images/connector-platform-icon-web.svg';
|
||||
import { PreviewPlatform } from '@/components/SignInExperiencePreview/types';
|
||||
|
||||
import PlatformTab from './PlatformTab';
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
type Props = {
|
||||
currentTab: PreviewPlatform;
|
||||
onSelect: (tab: PreviewPlatform) => void;
|
||||
};
|
||||
|
||||
const PlatformTabs = ({ currentTab, onSelect }: Props) => (
|
||||
<div className={styles.container}>
|
||||
<PlatformTab
|
||||
icon={<Native />}
|
||||
title="cloud.sie.preview.mobile_tab"
|
||||
tab={PreviewPlatform.MobileWeb}
|
||||
isSelected={currentTab === PreviewPlatform.MobileWeb}
|
||||
onClick={onSelect}
|
||||
/>
|
||||
<PlatformTab
|
||||
icon={<Web />}
|
||||
title="cloud.sie.preview.web_tab"
|
||||
tab={PreviewPlatform.DesktopWeb}
|
||||
isSelected={currentTab === PreviewPlatform.DesktopWeb}
|
||||
onClick={onSelect}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default PlatformTabs;
|
|
@ -0,0 +1,40 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.container {
|
||||
border-radius: 16px;
|
||||
background-color: var(--color-neutral-variant-90);
|
||||
padding: _.unit(6);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.topBar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
|
||||
.button {
|
||||
border: 1px solid var(--color-surface-5);
|
||||
margin-left: _.unit(2);
|
||||
}
|
||||
|
||||
.themeButton {
|
||||
padding: _.unit(2);
|
||||
}
|
||||
|
||||
.themeIcon {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.livePreviewIcon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
import type { SignInExperience } from '@logto/schemas';
|
||||
import { AppearanceMode } from '@logto/schemas';
|
||||
import { conditional } from '@silverhand/essentials';
|
||||
import classNames from 'classnames';
|
||||
import { useState } from 'react';
|
||||
|
||||
import LivePreviewButton from '@/components/LivePreviewButton';
|
||||
import SignInExperiencePreview from '@/components/SignInExperiencePreview';
|
||||
import { PreviewPlatform } from '@/components/SignInExperiencePreview/types';
|
||||
import ToggleThemeButton from '@/components/ToggleThemeButton';
|
||||
|
||||
import PlatformTabs from '../PlatformTabs';
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
type Props = {
|
||||
signInExperience?: SignInExperience;
|
||||
isLivePreviewDisabled?: boolean;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const Preview = ({ signInExperience, isLivePreviewDisabled = false, className }: Props) => {
|
||||
const [currentTab, setCurrentTab] = useState(PreviewPlatform.MobileWeb);
|
||||
const [mode, setMode] = useState<Omit<AppearanceMode, AppearanceMode.SyncWithSystem>>(
|
||||
AppearanceMode.LightMode
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={classNames(styles.container, className)}>
|
||||
<div className={styles.topBar}>
|
||||
<PlatformTabs currentTab={currentTab} onSelect={setCurrentTab} />
|
||||
<div className={styles.actions}>
|
||||
<ToggleThemeButton
|
||||
className={classNames(styles.button, styles.themeButton)}
|
||||
iconClassName={styles.themeIcon}
|
||||
value={mode}
|
||||
onToggle={setMode}
|
||||
/>
|
||||
<LivePreviewButton
|
||||
isDisabled={isLivePreviewDisabled}
|
||||
className={styles.button}
|
||||
iconClassName={conditional(!isLivePreviewDisabled && styles.livePreviewIcon)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<SignInExperiencePreview
|
||||
platform={currentTab}
|
||||
mode={mode}
|
||||
signInExperience={signInExperience}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Preview;
|
|
@ -2,14 +2,22 @@
|
|||
|
||||
.content {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
overflow-y: hidden;
|
||||
padding: 0 _.unit(17);
|
||||
|
||||
.config {
|
||||
.configWrapper {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
margin-right: _.unit(4);
|
||||
padding: 0 _.unit(4) _.unit(6) 0;
|
||||
}
|
||||
|
||||
.config {
|
||||
background-color: var(--color-layer-1);
|
||||
border-radius: 8px;
|
||||
padding: _.unit(12);
|
||||
margin-right: _.unit(8);
|
||||
min-width: 430px;
|
||||
|
||||
.title {
|
||||
margin-top: _.unit(6);
|
||||
|
@ -51,5 +59,14 @@
|
|||
|
||||
.preview {
|
||||
flex: 1;
|
||||
margin-bottom: _.unit(6);
|
||||
min-height: 580px;
|
||||
}
|
||||
}
|
||||
|
||||
.continueActions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: _.unit(4);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
import type { SignInExperience as SignInExperienceType } from '@logto/schemas';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import Bulb from '@/assets/images/bulb.svg';
|
||||
import Tools from '@/assets/images/tools.svg';
|
||||
|
@ -8,24 +12,50 @@ import ActionBar from '@/cloud/components/ActionBar';
|
|||
import { CardSelector, MultiCardSelector } from '@/cloud/components/CardSelector';
|
||||
import { defaultOnboardingSieConfig } from '@/cloud/constants';
|
||||
import * as pageLayout from '@/cloud/scss/layout.module.scss';
|
||||
import type { OnboardingSieConfig } from '@/cloud/types';
|
||||
import type { OnboardSieConfig } from '@/cloud/types';
|
||||
import { OnboardPage } from '@/cloud/types';
|
||||
import { getOnboardPagePathname } from '@/cloud/utils';
|
||||
import Button from '@/components/Button';
|
||||
import ColorPicker from '@/components/ColorPicker';
|
||||
import FormField from '@/components/FormField';
|
||||
import OverlayScrollbar from '@/components/OverlayScrollbar';
|
||||
import type { RequestError } from '@/hooks/use-api';
|
||||
import useApi from '@/hooks/use-api';
|
||||
|
||||
import Preview from './components/Preview';
|
||||
import * as styles from './index.module.scss';
|
||||
import { authenticationOptions, identifierOptions } from './options';
|
||||
import { parser } from './utils';
|
||||
|
||||
const SignInExperience = () => {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
const navigate = useNavigate();
|
||||
const { data: signInExperience, mutate } = useSWR<SignInExperienceType, RequestError>(
|
||||
'api/sign-in-exp'
|
||||
);
|
||||
const api = useApi();
|
||||
|
||||
const { reset, control, handleSubmit } = useForm<OnboardingSieConfig>({
|
||||
defaultValues: defaultOnboardingSieConfig,
|
||||
});
|
||||
const {
|
||||
reset,
|
||||
control,
|
||||
watch,
|
||||
handleSubmit,
|
||||
formState: { isSubmitting, isDirty },
|
||||
} = useForm<OnboardSieConfig>({ defaultValues: defaultOnboardingSieConfig });
|
||||
|
||||
useEffect(() => {
|
||||
if (signInExperience) {
|
||||
reset(parser.signInExperienceToOnboardSieConfig(signInExperience));
|
||||
}
|
||||
}, [reset, signInExperience]);
|
||||
|
||||
const onboardingSieConfig = watch();
|
||||
|
||||
const previewSieConfig = useMemo(() => {
|
||||
if (signInExperience) {
|
||||
return parser.onboardSieConfigToSignInExperience(onboardingSieConfig, signInExperience);
|
||||
}
|
||||
}, [onboardingSieConfig, signInExperience]);
|
||||
|
||||
const handleInspireMe = () => {
|
||||
// TODO @xiaoyijun
|
||||
|
@ -34,14 +64,28 @@ const SignInExperience = () => {
|
|||
};
|
||||
|
||||
const onSubmit = handleSubmit(async (formData) => {
|
||||
// TODO @xiaoyijun convert to sign in experience config data and update
|
||||
console.log(formData);
|
||||
if (!signInExperience) {
|
||||
return;
|
||||
}
|
||||
|
||||
const updatedData = await api
|
||||
.patch('api/sign-in-exp', {
|
||||
json: parser.onboardSieConfigToSignInExperience(formData, signInExperience),
|
||||
})
|
||||
.json<SignInExperienceType>();
|
||||
|
||||
void mutate(updatedData);
|
||||
});
|
||||
|
||||
const handleBack = () => {
|
||||
navigate(getOnboardPagePathname(OnboardPage.AboutUser));
|
||||
};
|
||||
|
||||
const handleSave = async () => {
|
||||
await onSubmit();
|
||||
toast.success(t('general.saved'));
|
||||
};
|
||||
|
||||
const handleNext = async () => {
|
||||
await onSubmit();
|
||||
navigate(getOnboardPagePathname(OnboardPage.Congrats));
|
||||
|
@ -49,8 +93,11 @@ const SignInExperience = () => {
|
|||
|
||||
return (
|
||||
<div className={pageLayout.page}>
|
||||
<OverlayScrollbar className={pageLayout.contentContainer}>
|
||||
<div className={styles.content}>
|
||||
<div className={styles.content}>
|
||||
<OverlayScrollbar
|
||||
options={{ scrollbars: { autoHide: 'scroll', autoHideDelay: 500 } }}
|
||||
className={styles.configWrapper}
|
||||
>
|
||||
<div className={styles.config}>
|
||||
<Tools />
|
||||
<div className={styles.title}>{t('cloud.sie.title')}</div>
|
||||
|
@ -102,9 +149,10 @@ const SignInExperience = () => {
|
|||
<Controller
|
||||
name="authentications"
|
||||
control={control}
|
||||
defaultValue={[]}
|
||||
defaultValue={defaultOnboardingSieConfig.authentications}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<MultiCardSelector
|
||||
isNotAllowEmpty
|
||||
className={styles.authnSelector}
|
||||
value={value}
|
||||
options={authenticationOptions}
|
||||
|
@ -121,12 +169,30 @@ const SignInExperience = () => {
|
|||
TBD
|
||||
</FormField>
|
||||
</div>
|
||||
<div className={styles.preview}>Preview(TBD)</div>
|
||||
</div>
|
||||
</OverlayScrollbar>
|
||||
</OverlayScrollbar>
|
||||
<Preview
|
||||
className={styles.preview}
|
||||
signInExperience={previewSieConfig}
|
||||
isLivePreviewDisabled={isDirty}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<ActionBar step={3}>
|
||||
<Button type="primary" title="cloud.sie.finish_and_done" onClick={handleNext} />
|
||||
<Button title="general.back" onClick={handleBack} />
|
||||
<div className={styles.continueActions}>
|
||||
<Button
|
||||
type="outline"
|
||||
title="general.save"
|
||||
disabled={isSubmitting}
|
||||
onClick={handleSave}
|
||||
/>
|
||||
<Button
|
||||
type="primary"
|
||||
title="cloud.sie.finish_and_done"
|
||||
disabled={isSubmitting}
|
||||
onClick={handleNext}
|
||||
/>
|
||||
</div>
|
||||
<Button title="general.back" disabled={isSubmitting} onClick={handleBack} />
|
||||
</ActionBar>
|
||||
</div>
|
||||
);
|
||||
|
|
78
packages/console/src/cloud/pages/SignInExperience/utils.ts
Normal file
78
packages/console/src/cloud/pages/SignInExperience/utils.ts
Normal file
|
@ -0,0 +1,78 @@
|
|||
import type { SignInExperience } from '@logto/schemas';
|
||||
import { SignInIdentifier } from '@logto/schemas';
|
||||
|
||||
import type { OnboardSieConfig } from '@/cloud/types';
|
||||
import { Authentication } from '@/cloud/types';
|
||||
|
||||
const signInExperienceToOnboardSieConfig = (
|
||||
signInExperience: SignInExperience
|
||||
): OnboardSieConfig => {
|
||||
const {
|
||||
color: { primaryColor },
|
||||
signIn: { methods: signInMethods },
|
||||
signUp: { identifiers: signUpIdentifiers },
|
||||
} = signInExperience;
|
||||
|
||||
const identifier =
|
||||
signInMethods.find(({ identifier }) => signUpIdentifiers.includes(identifier))?.identifier ??
|
||||
SignInIdentifier.Username;
|
||||
|
||||
const authentications = signInMethods.reduce<Authentication[]>((result, method) => {
|
||||
const { password, verificationCode } = method;
|
||||
|
||||
return [
|
||||
...result,
|
||||
...(password ? [Authentication.Password] : []),
|
||||
...(verificationCode ? [Authentication.VerificationCode] : []),
|
||||
];
|
||||
}, []);
|
||||
|
||||
return {
|
||||
color: primaryColor,
|
||||
identifier,
|
||||
authentications,
|
||||
};
|
||||
};
|
||||
|
||||
const onboardSieConfigToSignInExperience = (
|
||||
config: OnboardSieConfig,
|
||||
basedConfig: SignInExperience
|
||||
): SignInExperience => {
|
||||
const { color: onboardConfigColor, identifier, authentications } = config;
|
||||
const { color: baseColorConfig } = basedConfig;
|
||||
|
||||
const isPasswordSetup = authentications.includes(Authentication.Password);
|
||||
const isVerificationCodeSetup =
|
||||
authentications.includes(Authentication.VerificationCode) &&
|
||||
identifier !== SignInIdentifier.Username;
|
||||
|
||||
const signInExperience = {
|
||||
...basedConfig,
|
||||
color: {
|
||||
...baseColorConfig,
|
||||
primaryColor: onboardConfigColor,
|
||||
},
|
||||
signUp: {
|
||||
identifiers: [identifier],
|
||||
password: isPasswordSetup,
|
||||
verify: isVerificationCodeSetup,
|
||||
},
|
||||
signIn: {
|
||||
methods: [
|
||||
{
|
||||
identifier,
|
||||
password: isPasswordSetup,
|
||||
verificationCode: isVerificationCodeSetup,
|
||||
isPasswordPrimary: isPasswordSetup,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
return signInExperience;
|
||||
};
|
||||
|
||||
export const parser = {
|
||||
signInExperienceToOnboardSieConfig,
|
||||
onboardSieConfigToSignInExperience,
|
||||
};
|
|
@ -72,7 +72,7 @@ export enum Authentication {
|
|||
VerificationCode = 'verificationCode',
|
||||
}
|
||||
|
||||
export type OnboardingSieConfig = {
|
||||
export type OnboardSieConfig = {
|
||||
color: string;
|
||||
identifier: SignInIdentifier;
|
||||
authentications: Authentication[];
|
||||
|
|
|
@ -15,9 +15,11 @@ import * as styles from './index.module.scss';
|
|||
type Props = {
|
||||
size?: ButtonProps['size'];
|
||||
isDisabled: boolean;
|
||||
className?: string;
|
||||
iconClassName?: string;
|
||||
};
|
||||
|
||||
const LivePreviewButton = ({ size = 'medium', isDisabled }: Props) => {
|
||||
const LivePreviewButton = ({ size = 'medium', isDisabled, className, iconClassName }: Props) => {
|
||||
const { configs, updateConfigs } = useConfigs();
|
||||
const { userEndpoint } = useContext(AppEndpointsContext);
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
|
@ -27,9 +29,12 @@ const LivePreviewButton = ({ size = 'medium', isDisabled }: Props) => {
|
|||
<Button
|
||||
size={size}
|
||||
disabled={isDisabled}
|
||||
className={className}
|
||||
title="sign_in_exp.preview.live_preview"
|
||||
trailingIcon={
|
||||
<ExternalLinkIcon className={classNames(styles.icon, isDisabled && styles.disabled)} />
|
||||
<ExternalLinkIcon
|
||||
className={classNames(styles.icon, iconClassName, isDisabled && styles.disabled)}
|
||||
/>
|
||||
}
|
||||
onClick={() => {
|
||||
if (!configs?.livePreviewChecked) {
|
||||
|
|
|
@ -219,17 +219,6 @@
|
|||
}
|
||||
|
||||
.compact:not(.disabled) {
|
||||
&:focus {
|
||||
color: var(--color-text-link);
|
||||
border-color: var(--color-primary);
|
||||
|
||||
.content {
|
||||
.icon {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--color-text-link);
|
||||
border-color: var(--color-primary);
|
||||
|
|
|
@ -19,11 +19,11 @@ import { PreviewPlatform } from './types';
|
|||
type Props = {
|
||||
platform: PreviewPlatform;
|
||||
mode: Omit<AppearanceMode, AppearanceMode.SyncWithSystem>;
|
||||
language: LanguageTag;
|
||||
language?: LanguageTag;
|
||||
signInExperience?: SignInExperience;
|
||||
};
|
||||
|
||||
const SignInExperiencePreview = ({ platform, mode, language, signInExperience }: Props) => {
|
||||
const SignInExperiencePreview = ({ platform, mode, language = 'en', signInExperience }: Props) => {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
|
||||
const { customPhrases } = useUiLanguages();
|
||||
|
@ -89,6 +89,10 @@ const SignInExperiencePreview = ({ platform, mode, language, signInExperience }:
|
|||
};
|
||||
}, [postPreviewMessage]);
|
||||
|
||||
if (!userEndpoint) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
|
|
|
@ -90,6 +90,10 @@ const cloud = {
|
|||
},
|
||||
social_field: 'Social sign in', // UNTRANSLATED
|
||||
finish_and_done: 'Finish and done', // UNTRANSLATED
|
||||
preview: {
|
||||
mobile_tab: 'Mobile', // UNTRANSLATED
|
||||
web_tab: 'Web', // UNTRANSLATED
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -90,6 +90,10 @@ const cloud = {
|
|||
},
|
||||
social_field: 'Social sign in',
|
||||
finish_and_done: 'Finish and done',
|
||||
preview: {
|
||||
mobile_tab: 'Mobile',
|
||||
web_tab: 'Web',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -90,6 +90,10 @@ const cloud = {
|
|||
},
|
||||
social_field: 'Social sign in', // UNTRANSLATED
|
||||
finish_and_done: 'Finish and done', // UNTRANSLATED
|
||||
preview: {
|
||||
mobile_tab: 'Mobile', // UNTRANSLATED
|
||||
web_tab: 'Web', // UNTRANSLATED
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -90,6 +90,10 @@ const cloud = {
|
|||
},
|
||||
social_field: 'Social sign in', // UNTRANSLATED
|
||||
finish_and_done: 'Finish and done', // UNTRANSLATED
|
||||
preview: {
|
||||
mobile_tab: 'Mobile', // UNTRANSLATED
|
||||
web_tab: 'Web', // UNTRANSLATED
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -90,6 +90,10 @@ const cloud = {
|
|||
},
|
||||
social_field: 'Social sign in', // UNTRANSLATED
|
||||
finish_and_done: 'Finish and done', // UNTRANSLATED
|
||||
preview: {
|
||||
mobile_tab: 'Mobile', // UNTRANSLATED
|
||||
web_tab: 'Web', // UNTRANSLATED
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -90,6 +90,10 @@ const cloud = {
|
|||
},
|
||||
social_field: 'Social sign in', // UNTRANSLATED
|
||||
finish_and_done: 'Finish and done', // UNTRANSLATED
|
||||
preview: {
|
||||
mobile_tab: 'Mobile', // UNTRANSLATED
|
||||
web_tab: 'Web', // UNTRANSLATED
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -90,6 +90,10 @@ const cloud = {
|
|||
},
|
||||
social_field: 'Social sign in', // UNTRANSLATED
|
||||
finish_and_done: 'Finish and done', // UNTRANSLATED
|
||||
preview: {
|
||||
mobile_tab: 'Mobile', // UNTRANSLATED
|
||||
web_tab: 'Web', // UNTRANSLATED
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -90,6 +90,10 @@ const cloud = {
|
|||
},
|
||||
social_field: 'Social sign in', // UNTRANSLATED
|
||||
finish_and_done: 'Finish and done', // UNTRANSLATED
|
||||
preview: {
|
||||
mobile_tab: 'Mobile', // UNTRANSLATED
|
||||
web_tab: 'Web', // UNTRANSLATED
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue