mirror of
https://github.com/logto-io/logto.git
synced 2024-12-16 20:26:19 -05:00
feat(console): sie sign up form (#2191)
This commit is contained in:
parent
e5f8139880
commit
e936ad04ae
16 changed files with 346 additions and 243 deletions
|
@ -79,6 +79,7 @@
|
|||
"react-syntax-highlighter": "^15.5.0",
|
||||
"recharts": "^2.1.13",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"snake-case": "^3.0.4",
|
||||
"stylelint": "^14.9.1",
|
||||
"swr": "^1.3.0",
|
||||
"typescript": "^4.7.4",
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
import { ConnectorResponse, ConnectorType, SignInMethodKey } from '@logto/schemas';
|
||||
import { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import Alert from '@/components/Alert';
|
||||
import { RequestError } from '@/hooks/use-api';
|
||||
|
||||
type Props = {
|
||||
method: SignInMethodKey;
|
||||
};
|
||||
|
||||
const ConnectorSetupWarning = ({ method }: Props) => {
|
||||
const { data: connectors } = useSWR<ConnectorResponse[], RequestError>('/api/connectors');
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
|
||||
const type = useMemo(() => {
|
||||
if (method === SignInMethodKey.Username) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (method === SignInMethodKey.Sms) {
|
||||
return ConnectorType.Sms;
|
||||
}
|
||||
|
||||
if (method === SignInMethodKey.Email) {
|
||||
return ConnectorType.Email;
|
||||
}
|
||||
|
||||
return ConnectorType.Social;
|
||||
}, [method]);
|
||||
|
||||
if (!type || !connectors) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (connectors.some(({ type: connectorType, enabled }) => connectorType === type && enabled)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Alert
|
||||
action="general.set_up"
|
||||
href={type === ConnectorType.Social ? '/connectors/social' : '/connectors'}
|
||||
>
|
||||
{t('sign_in_exp.setup_warning.no_connector', { context: type.toLowerCase() })}
|
||||
</Alert>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConnectorSetupWarning;
|
|
@ -1,147 +0,0 @@
|
|||
import { SignInMethodKey } from '@logto/schemas';
|
||||
import { useMemo } from 'react';
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import Checkbox from '@/components/Checkbox';
|
||||
import FormField from '@/components/FormField';
|
||||
import Select from '@/components/Select';
|
||||
import Switch from '@/components/Switch';
|
||||
|
||||
import { SignInExperienceForm } from '../types';
|
||||
import ConnectorSetupWarning from './ConnectorSetupWarning';
|
||||
import ConnectorsTransfer from './ConnectorsTransfer';
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
const signInMethods = Object.values(SignInMethodKey);
|
||||
|
||||
const SignInMethodsForm = () => {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
const { register, watch, control, getValues, setValue } = useFormContext<SignInExperienceForm>();
|
||||
const primaryMethod = watch('signInMethods.primary');
|
||||
const enableSecondary = watch('signInMethods.enableSecondary');
|
||||
const sms = watch('signInMethods.sms');
|
||||
const email = watch('signInMethods.email');
|
||||
const social = watch('signInMethods.social');
|
||||
|
||||
const postPrimaryMethodChange = (
|
||||
oldPrimaryMethod?: SignInMethodKey,
|
||||
primaryMethod?: SignInMethodKey
|
||||
) => {
|
||||
if (oldPrimaryMethod) {
|
||||
// The secondary sign-in method should select the old primary method by default.
|
||||
setValue(`signInMethods.${oldPrimaryMethod}`, true);
|
||||
}
|
||||
|
||||
if (primaryMethod) {
|
||||
// When one of the sign-in methods has been primary, it should not be able to be secondary simultaneously.
|
||||
setValue(`signInMethods.${primaryMethod}`, false);
|
||||
}
|
||||
};
|
||||
|
||||
const secondaryMethodsFields = useMemo(
|
||||
() =>
|
||||
signInMethods.map((method) => {
|
||||
const label = (
|
||||
<>
|
||||
{t('sign_in_exp.sign_in_methods.methods', { context: method })}
|
||||
{primaryMethod === method && (
|
||||
<span className={styles.primaryTag}>
|
||||
{t('sign_in_exp.sign_in_methods.methods_primary_tag')}
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
const enabled =
|
||||
(method === SignInMethodKey.Email && email) ||
|
||||
(method === SignInMethodKey.Sms && sms) ||
|
||||
(method === SignInMethodKey.Social && social);
|
||||
|
||||
return (
|
||||
<div key={method} className={styles.method}>
|
||||
<Controller
|
||||
name={`signInMethods.${method}`}
|
||||
control={control}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<Checkbox
|
||||
label={label}
|
||||
disabled={primaryMethod === method}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{enabled && <ConnectorSetupWarning method={method} />}
|
||||
</div>
|
||||
);
|
||||
}),
|
||||
[t, primaryMethod, email, sms, social, control]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.title}>{t('sign_in_exp.sign_in_methods.title')}</div>
|
||||
<FormField title="sign_in_exp.sign_in_methods.primary">
|
||||
<Controller
|
||||
name="signInMethods.primary"
|
||||
control={control}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<Select
|
||||
value={value}
|
||||
options={signInMethods.map((method) => ({
|
||||
value: method,
|
||||
title: t('sign_in_exp.sign_in_methods.methods', { context: method }),
|
||||
}))}
|
||||
onChange={(value) => {
|
||||
const oldPrimaryMethod = getValues('signInMethods.primary');
|
||||
onChange(value);
|
||||
postPrimaryMethodChange(oldPrimaryMethod, value);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</FormField>
|
||||
{primaryMethod && <ConnectorSetupWarning method={primaryMethod} />}
|
||||
{primaryMethod === SignInMethodKey.Social && (
|
||||
<div className={styles.primarySocial}>
|
||||
<Controller
|
||||
name="socialSignInConnectorTargets"
|
||||
control={control}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<ConnectorsTransfer value={value} onChange={onChange} />
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<FormField title="sign_in_exp.sign_in_methods.enable_secondary">
|
||||
<Switch
|
||||
/**
|
||||
* DO NOT SET THIS FIELD TO REQUIRED UNLESS YOU KNOW WHAT YOU ARE DOING.
|
||||
* https://github.com/react-hook-form/react-hook-form/issues/2323
|
||||
*/
|
||||
{...register('signInMethods.enableSecondary')}
|
||||
label={t('sign_in_exp.sign_in_methods.enable_secondary_description')}
|
||||
/>
|
||||
</FormField>
|
||||
{enableSecondary && (
|
||||
<>
|
||||
{secondaryMethodsFields}
|
||||
{social && (
|
||||
<FormField title="sign_in_exp.sign_in_methods.define_social_methods">
|
||||
<Controller
|
||||
name="socialSignInConnectorTargets"
|
||||
control={control}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<ConnectorsTransfer value={value} onChange={onChange} />
|
||||
)}
|
||||
/>
|
||||
</FormField>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SignInMethodsForm;
|
|
@ -10,18 +10,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.primaryTag {
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.method {
|
||||
margin-top: _.unit(3);
|
||||
}
|
||||
|
||||
.primarySocial {
|
||||
margin-top: _.unit(2);
|
||||
}
|
||||
|
||||
.darkModeTip {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
|
|
|
@ -25,7 +25,7 @@ import usePreviewConfigs from './hooks';
|
|||
import * as styles from './index.module.scss';
|
||||
import BrandingTab from './tabs/BrandingTab';
|
||||
import OthersTab from './tabs/OthersTab';
|
||||
import SignInMethodsTab from './tabs/SignInMethodsTab';
|
||||
import SignUpAndSignInTab from './tabs/SignUpAndSignInTab';
|
||||
import { SignInExperienceForm } from './types';
|
||||
import { compareSignInMethods, signInExperienceParser } from './utilities';
|
||||
|
||||
|
@ -124,8 +124,8 @@ const SignInExperience = () => {
|
|||
<TabNavItem href="/sign-in-experience/branding">
|
||||
{t('sign_in_exp.tabs.branding')}
|
||||
</TabNavItem>
|
||||
<TabNavItem href="/sign-in-experience/methods">
|
||||
{t('sign_in_exp.tabs.methods')}
|
||||
<TabNavItem href="/sign-in-experience/sign-up-and-sign-in">
|
||||
{t('sign_in_exp.tabs.sign_up_and_sign_in')}
|
||||
</TabNavItem>
|
||||
<TabNavItem href="/sign-in-experience/others">
|
||||
{t('sign_in_exp.tabs.others')}
|
||||
|
@ -139,8 +139,8 @@ const SignInExperience = () => {
|
|||
{tab === 'branding' && (
|
||||
<BrandingTab defaultData={defaultFormData} isDataDirty={isDirty} />
|
||||
)}
|
||||
{tab === 'methods' && (
|
||||
<SignInMethodsTab defaultData={defaultFormData} isDataDirty={isDirty} />
|
||||
{tab === 'sign-up-and-sign-in' && (
|
||||
<SignUpAndSignInTab defaultData={defaultFormData} isDataDirty={isDirty} />
|
||||
)}
|
||||
{tab === 'others' && (
|
||||
<OthersTab defaultData={defaultFormData} isDataDirty={isDirty} />
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
import { ConnectorResponse, ConnectorType, SignUpIdentifier } from '@logto/schemas';
|
||||
import { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { snakeCase } from 'snake-case';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import Alert from '@/components/Alert';
|
||||
import { RequestError } from '@/hooks/use-api';
|
||||
|
||||
type Props = {
|
||||
signUpIdentifier: SignUpIdentifier;
|
||||
};
|
||||
|
||||
const ConnectorSetupWarning = ({ signUpIdentifier }: Props) => {
|
||||
const { data: connectors } = useSWR<ConnectorResponse[], RequestError>('/api/connectors');
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
|
||||
const connectorTypes = useMemo(() => {
|
||||
if (signUpIdentifier === SignUpIdentifier.Username) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (signUpIdentifier === SignUpIdentifier.Email) {
|
||||
return [ConnectorType.Email];
|
||||
}
|
||||
|
||||
if (signUpIdentifier === SignUpIdentifier.Phone) {
|
||||
return [ConnectorType.Sms];
|
||||
}
|
||||
|
||||
if (signUpIdentifier === SignUpIdentifier.EmailOrPhone) {
|
||||
return [ConnectorType.Email, ConnectorType.Sms];
|
||||
}
|
||||
|
||||
return [ConnectorType.Social];
|
||||
}, [signUpIdentifier]);
|
||||
|
||||
if (connectorTypes.length === 0 || !connectors) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (
|
||||
connectorTypes.every((connectorType) =>
|
||||
connectors.some(({ type, enabled }) => type === connectorType && enabled)
|
||||
)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Alert
|
||||
action="general.set_up"
|
||||
href={connectorTypes.includes(ConnectorType.Social) ? '/connectors/social' : '/connectors'}
|
||||
>
|
||||
{t('sign_in_exp.setup_warning.no_connector', { context: snakeCase(signUpIdentifier) })}
|
||||
</Alert>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConnectorSetupWarning;
|
|
@ -0,0 +1,123 @@
|
|||
import { SignUpIdentifier } from '@logto/schemas';
|
||||
import { useEffect } from 'react';
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { snakeCase } from 'snake-case';
|
||||
|
||||
import Checkbox from '@/components/Checkbox';
|
||||
import FormField from '@/components/FormField';
|
||||
import Select from '@/components/Select';
|
||||
|
||||
import { SignInExperienceForm } from '../../types';
|
||||
import ConnectorSetupWarning from './ConnectorSetupWarning';
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
const signUpIdentifiers = Object.values(SignUpIdentifier);
|
||||
|
||||
const requireVerifyIdentifiers = new Set([
|
||||
SignUpIdentifier.Email,
|
||||
SignUpIdentifier.Phone,
|
||||
SignUpIdentifier.EmailOrPhone,
|
||||
]);
|
||||
|
||||
const SignUpForm = () => {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
const { control, setValue, resetField, watch } = useFormContext<SignInExperienceForm>();
|
||||
const signUpIdentifier = watch('signUp.identifier');
|
||||
|
||||
useEffect(() => {
|
||||
if (signUpIdentifier === SignUpIdentifier.Username) {
|
||||
setValue('signUp.password', true);
|
||||
setValue('signUp.verify', false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (signUpIdentifier === SignUpIdentifier.None) {
|
||||
setValue('signUp.password', false);
|
||||
setValue('signUp.verify', false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (requireVerifyIdentifiers.has(signUpIdentifier)) {
|
||||
resetField('signUp.password');
|
||||
setValue('signUp.verify', true);
|
||||
}
|
||||
}, [resetField, setValue, signUpIdentifier]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.title}>{t('sign_in_exp.sign_up_and_sign_in.sign_up.title')}</div>
|
||||
<FormField title="sign_in_exp.sign_up_and_sign_in.sign_up.sign_up_identifier">
|
||||
<Controller
|
||||
name="signUp.identifier"
|
||||
control={control}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<Select
|
||||
value={value}
|
||||
options={signUpIdentifiers.map((identifier) => ({
|
||||
value: identifier,
|
||||
title: (
|
||||
<div>
|
||||
{t('sign_in_exp.sign_up_and_sign_in.identifiers', {
|
||||
context: snakeCase(identifier),
|
||||
})}
|
||||
{identifier === SignUpIdentifier.None && (
|
||||
<span className={styles.socialOnlyDescription}>
|
||||
{t(
|
||||
'sign_in_exp.sign_up_and_sign_in.sign_up.social_only_creation_description'
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
}))}
|
||||
onChange={(value) => {
|
||||
onChange(value);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{signUpIdentifier !== SignUpIdentifier.None && (
|
||||
<ConnectorSetupWarning signUpIdentifier={signUpIdentifier} />
|
||||
)}
|
||||
</FormField>
|
||||
{signUpIdentifier !== SignUpIdentifier.None && (
|
||||
<FormField
|
||||
title="sign_in_exp.sign_up_and_sign_in.sign_up.sign_up_authentication"
|
||||
className={styles.signUpAuthentication}
|
||||
>
|
||||
<Controller
|
||||
name="signUp.password"
|
||||
control={control}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<Checkbox
|
||||
label={t('sign_in_exp.sign_up_and_sign_in.sign_up.set_a_password_option')}
|
||||
disabled={signUpIdentifier === SignUpIdentifier.Username}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{signUpIdentifier !== SignUpIdentifier.Username && (
|
||||
<Controller
|
||||
name="signUp.verify"
|
||||
control={control}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<Checkbox
|
||||
label={t('sign_in_exp.sign_up_and_sign_in.sign_up.verify_at_sign_up_option')}
|
||||
value={value}
|
||||
disabled={requireVerifyIdentifiers.has(signUpIdentifier)}
|
||||
onChange={onChange}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</FormField>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SignUpForm;
|
|
@ -0,0 +1,22 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.title {
|
||||
@include _.subhead-cap;
|
||||
color: var(--color-neutral-variant-60);
|
||||
margin-top: _.unit(12);
|
||||
|
||||
&:first-child {
|
||||
margin-top: _.unit(6);
|
||||
}
|
||||
}
|
||||
|
||||
.socialOnlyDescription {
|
||||
margin-left: _.unit(1);
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.signUpAuthentication {
|
||||
> :not(:first-child) {
|
||||
margin-top: _.unit(3);
|
||||
}
|
||||
}
|
|
@ -3,15 +3,15 @@ import { useFormContext } from 'react-hook-form';
|
|||
|
||||
import UnsavedChangesAlertModal from '@/components/UnsavedChangesAlertModal';
|
||||
|
||||
import SignInMethodsForm from '../components/SignInMethodsForm';
|
||||
import { SignInExperienceForm } from '../types';
|
||||
import { SignInExperienceForm } from '../../types';
|
||||
import SignUpForm from './SignUpForm';
|
||||
|
||||
type Props = {
|
||||
defaultData: SignInExperienceForm;
|
||||
isDataDirty: boolean;
|
||||
};
|
||||
|
||||
const SignInMethodsTab = ({ defaultData, isDataDirty }: Props) => {
|
||||
const SignUpAndSignInTab = ({ defaultData, isDataDirty }: Props) => {
|
||||
const { reset } = useFormContext<SignInExperienceForm>();
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -22,10 +22,10 @@ const SignInMethodsTab = ({ defaultData, isDataDirty }: Props) => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<SignInMethodsForm />
|
||||
<SignUpForm />
|
||||
<UnsavedChangesAlertModal hasUnsavedChanges={isDataDirty} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SignInMethodsTab;
|
||||
export default SignUpAndSignInTab;
|
|
@ -3,7 +3,7 @@ const sign_in_exp = {
|
|||
description: 'Customize the sign in UI to match your brand and view in real time',
|
||||
tabs: {
|
||||
branding: 'Branding',
|
||||
methods: 'Sign-in methods',
|
||||
sign_up_and_sign_in: 'Sign up and Sign in',
|
||||
others: 'Others',
|
||||
},
|
||||
welcome: {
|
||||
|
@ -38,6 +38,22 @@ const sign_in_exp = {
|
|||
slogan: 'Slogan',
|
||||
slogan_placeholder: 'Unleash your creativity',
|
||||
},
|
||||
sign_up_and_sign_in: {
|
||||
identifiers: 'Sign up identifiers',
|
||||
identifiers_email: 'Email address',
|
||||
identifiers_phone: 'Phone number',
|
||||
identifiers_username: 'Username',
|
||||
identifiers_email_or_phone: 'Email address or phone number',
|
||||
identifiers_none: 'None',
|
||||
sign_up: {
|
||||
title: 'SIGN UP',
|
||||
sign_up_identifier: 'Sign up identifier',
|
||||
sign_up_authentication: 'Sign up authentication',
|
||||
set_a_password_option: 'Set a password',
|
||||
verify_at_sign_up_option: 'Verify at sign up',
|
||||
social_only_creation_description: '(This apply to social only account creation)',
|
||||
},
|
||||
},
|
||||
sign_in_methods: {
|
||||
title: 'SIGN-IN METHODS',
|
||||
primary: 'Primary sign-in method',
|
||||
|
@ -110,11 +126,13 @@ const sign_in_exp = {
|
|||
},
|
||||
setup_warning: {
|
||||
no_connector: '',
|
||||
no_connector_sms:
|
||||
no_connector_phone:
|
||||
'You haven’t set up a SMS connector yet. Your sign in experience won’t go live until you finish the settings first. ',
|
||||
no_connector_email:
|
||||
'You haven’t set up an Email connector yet. Your sign in experience won’t go live until you finish the settings first. ',
|
||||
no_connector_social:
|
||||
no_connector_email_or_phone:
|
||||
'You haven’t set up both Email and SMS connectors yet. Your sign in experience won’t go live until you finish the settings first. ',
|
||||
no_connector_none:
|
||||
'You haven’t set up any social connectors yet. Your sign in experience won’t go live until you finish the settings first. ',
|
||||
no_added_social_connector:
|
||||
'You’ve set up a few social connectors now. Make sure to add some to your sign in experience.',
|
||||
|
|
|
@ -4,7 +4,7 @@ const sign_in_exp = {
|
|||
"Personnalisez l'interface utilisateur pour qu'elle corresponde à votre marque et consultez-la en temps réel.",
|
||||
tabs: {
|
||||
branding: 'Image de marque',
|
||||
methods: 'Méthodes de connexion',
|
||||
sign_up_and_sign_in: 'Sign up and Sign in', // UNTRANSLATED
|
||||
others: 'Autres',
|
||||
},
|
||||
welcome: {
|
||||
|
@ -40,6 +40,22 @@ const sign_in_exp = {
|
|||
slogan: 'Slogan',
|
||||
slogan_placeholder: 'Libérez votre créativité',
|
||||
},
|
||||
sign_up_and_sign_in: {
|
||||
identifiers: 'Sign up identifiers', // UNTRANSLATED
|
||||
identifiers_email: 'Email address', // UNTRANSLATED
|
||||
identifiers_phone: 'Phone number', // UNTRANSLATED
|
||||
identifiers_username: 'Username', // UNTRANSLATED
|
||||
identifiers_email_or_phone: 'Email address or phone number', // UNTRANSLATED
|
||||
identifiers_none: 'None', // UNTRANSLATED
|
||||
sign_up: {
|
||||
title: 'SIGN UP', // UNTRANSLATED
|
||||
sign_up_identifier: 'Sign up identifier', // UNTRANSLATED
|
||||
sign_up_authentication: 'Sign up authentication', // UNTRANSLATED
|
||||
set_a_password_option: 'Set a password', // UNTRANSLATED
|
||||
verify_at_sign_up_option: 'Verify at sign up', // UNTRANSLATED
|
||||
social_only_creation_description: '(This apply to social only account creation)', // UNTRANSLATED
|
||||
},
|
||||
},
|
||||
sign_in_methods: {
|
||||
title: 'METHODES DE CONNEXION',
|
||||
primary: 'Méthode de connexion principale',
|
||||
|
@ -112,11 +128,13 @@ const sign_in_exp = {
|
|||
},
|
||||
setup_warning: {
|
||||
no_connector: '',
|
||||
no_connector_sms:
|
||||
no_connector_phone:
|
||||
"Vous n'avez pas encore configuré de connecteur SMS. Votre expérience de connexion ne sera pas disponible tant que vous n'aurez pas terminé les paramètres. ",
|
||||
no_connector_email:
|
||||
"Vous n'avez pas encore configuré de connecteur Email. Votre expérience de connexion ne sera pas disponible tant que vous n'aurez pas terminé les paramètres. ",
|
||||
no_connector_social:
|
||||
no_connector_email_or_phone:
|
||||
'You haven’t set up both Email and SMS connectors yet. Your sign in experience won’t go live until you finish the settings first. ', // UNTRANSLATED
|
||||
no_connector_none:
|
||||
"Vous n'avez pas encore configuré de connecteurs sociaux. Votre expérience de connexion ne sera pas disponible tant que vous n'aurez pas terminé les paramètres. ",
|
||||
no_added_social_connector:
|
||||
"Vous avez maintenant configuré quelques connecteurs sociaux. Assurez-vous d'en ajouter quelques-uns à votre expérience de connexion.",
|
||||
|
|
|
@ -3,7 +3,7 @@ const sign_in_exp = {
|
|||
description: '로그인 화면을 브랜드에 맞게 커스터마이징 그리고 실시간으로 확인해보세요.',
|
||||
tabs: {
|
||||
branding: '브랜딩',
|
||||
methods: '로그인 방법',
|
||||
sign_up_and_sign_in: 'Sign up and Sign in', // UNTRANSLATED
|
||||
others: '기타',
|
||||
},
|
||||
welcome: {
|
||||
|
@ -35,6 +35,22 @@ const sign_in_exp = {
|
|||
slogan: '슬로건',
|
||||
slogan_placeholder: 'Unleash your creativity',
|
||||
},
|
||||
sign_up_and_sign_in: {
|
||||
identifiers: 'Sign up identifiers', // UNTRANSLATED
|
||||
identifiers_email: 'Email address', // UNTRANSLATED
|
||||
identifiers_phone: 'Phone number', // UNTRANSLATED
|
||||
identifiers_username: 'Username', // UNTRANSLATED
|
||||
identifiers_email_or_phone: 'Email address or phone number', // UNTRANSLATED
|
||||
identifiers_none: 'None', // UNTRANSLATED
|
||||
sign_up: {
|
||||
title: 'SIGN UP', // UNTRANSLATED
|
||||
sign_up_identifier: 'Sign up identifier', // UNTRANSLATED
|
||||
sign_up_authentication: 'Sign up authentication', // UNTRANSLATED
|
||||
set_a_password_option: 'Set a password', // UNTRANSLATED
|
||||
verify_at_sign_up_option: 'Verify at sign up', // UNTRANSLATED
|
||||
social_only_creation_description: '(This apply to social only account creation)', // UNTRANSLATED
|
||||
},
|
||||
},
|
||||
sign_in_methods: {
|
||||
title: '로그인 방법',
|
||||
primary: '메인 로그인 방법',
|
||||
|
@ -107,11 +123,13 @@ const sign_in_exp = {
|
|||
},
|
||||
setup_warning: {
|
||||
no_connector: '',
|
||||
no_connector_sms:
|
||||
no_connector_phone:
|
||||
'SMS 연동이 아직 설정되지 않았어요. 설정이 완료될 때 까지, 사용자는 이 로그인 방법을 사용할 수 없어요.',
|
||||
no_connector_email:
|
||||
'이메일 연동이 아직 설정되지 않았어요. 설정이 완료될 때 까지, 사용자는 이 로그인 방법을 사용할 수 없어요.',
|
||||
no_connector_social:
|
||||
no_connector_email_or_phone:
|
||||
'You haven’t set up both Email and SMS connectors yet. Your sign in experience won’t go live until you finish the settings first. ', // UNTRANSLATED
|
||||
no_connector_none:
|
||||
'소셜 연동이 아직 설정되지 않았어요. 설정이 완료될 때 까지, 사용자는 이 로그인 방법을 사용할 수 없어요.',
|
||||
no_added_social_connector:
|
||||
'보다 많은 소셜 연동들을 설정하여, 고객에게 보다 나은 경험을 제공해보세요.',
|
||||
|
|
|
@ -4,7 +4,7 @@ const sign_in_exp = {
|
|||
'Personalize a interface de login para corresponder a sua marca e visualize em tempo real',
|
||||
tabs: {
|
||||
branding: 'Marca',
|
||||
methods: 'Métodos de login',
|
||||
sign_up_and_sign_in: 'Sign up and Sign in', // UNTRANSLATED
|
||||
others: 'Outros',
|
||||
},
|
||||
welcome: {
|
||||
|
@ -38,6 +38,22 @@ const sign_in_exp = {
|
|||
slogan: 'Slogan',
|
||||
slogan_placeholder: 'Liberte a sua criatividade',
|
||||
},
|
||||
sign_up_and_sign_in: {
|
||||
identifiers: 'Sign up identifiers', // UNTRANSLATED
|
||||
identifiers_email: 'Email address', // UNTRANSLATED
|
||||
identifiers_phone: 'Phone number', // UNTRANSLATED
|
||||
identifiers_username: 'Username', // UNTRANSLATED
|
||||
identifiers_email_or_phone: 'Email address or phone number', // UNTRANSLATED
|
||||
identifiers_none: 'None', // UNTRANSLATED
|
||||
sign_up: {
|
||||
title: 'SIGN UP', // UNTRANSLATED
|
||||
sign_up_identifier: 'Sign up identifier', // UNTRANSLATED
|
||||
sign_up_authentication: 'Sign up authentication', // UNTRANSLATED
|
||||
set_a_password_option: 'Set a password', // UNTRANSLATED
|
||||
verify_at_sign_up_option: 'Verify at sign up', // UNTRANSLATED
|
||||
social_only_creation_description: '(This apply to social only account creation)', // UNTRANSLATED
|
||||
},
|
||||
},
|
||||
sign_in_methods: {
|
||||
title: 'MÉTODOS DE LOGIN',
|
||||
primary: 'Método de login principal',
|
||||
|
@ -110,11 +126,13 @@ const sign_in_exp = {
|
|||
},
|
||||
setup_warning: {
|
||||
no_connector: '',
|
||||
no_connector_sms:
|
||||
no_connector_phone:
|
||||
'Ainda não configurou um conector de SMS. A experiência de login não será ativada até que conclua as configurações primeiro. ',
|
||||
no_connector_email:
|
||||
'Ainda não configurou um conector de email. A experiência de login não será ativada até que conclua as configurações primeiro. ',
|
||||
no_connector_social:
|
||||
no_connector_email_or_phone:
|
||||
'You haven’t set up both Email and SMS connectors yet. Your sign in experience won’t go live until you finish the settings first. ', // UNTRANSLATED
|
||||
no_connector_none:
|
||||
'Ainda não configurou um conector social. A experiência de login não será ativada até que conclua as configurações primeiro. ',
|
||||
no_added_social_connector:
|
||||
'Configurou alguns conectores sociais agora. Certifique-se de adicionar alguns a experiência de login.',
|
||||
|
|
|
@ -4,7 +4,7 @@ const sign_in_exp = {
|
|||
'Oturum açma kullanıcı arayüzünü markanıza uyacak şekilde özelleştirin ve gerçek zamanlı olarak görüntüleyin',
|
||||
tabs: {
|
||||
branding: 'Markalaşma',
|
||||
methods: 'Oturum açma yöntemleri',
|
||||
sign_up_and_sign_in: 'Sign up and Sign in', // UNTRANSLATED
|
||||
others: 'Diğerleri',
|
||||
},
|
||||
welcome: {
|
||||
|
@ -39,6 +39,22 @@ const sign_in_exp = {
|
|||
slogan: 'Slogan',
|
||||
slogan_placeholder: 'Yaratıcılığınızı açığa çıkarın',
|
||||
},
|
||||
sign_up_and_sign_in: {
|
||||
identifiers: 'Sign up identifiers', // UNTRANSLATED
|
||||
identifiers_email: 'Email address', // UNTRANSLATED
|
||||
identifiers_phone: 'Phone number', // UNTRANSLATED
|
||||
identifiers_username: 'Username', // UNTRANSLATED
|
||||
identifiers_email_or_phone: 'Email address or phone number', // UNTRANSLATED
|
||||
identifiers_none: 'None', // UNTRANSLATED
|
||||
sign_up: {
|
||||
title: 'SIGN UP', // UNTRANSLATED
|
||||
sign_up_identifier: 'Sign up identifier', // UNTRANSLATED
|
||||
sign_up_authentication: 'Sign up authentication', // UNTRANSLATED
|
||||
set_a_password_option: 'Set a password', // UNTRANSLATED
|
||||
verify_at_sign_up_option: 'Verify at sign up', // UNTRANSLATED
|
||||
social_only_creation_description: '(This apply to social only account creation)', // UNTRANSLATED
|
||||
},
|
||||
},
|
||||
sign_in_methods: {
|
||||
title: 'OTURUM AÇMA YÖNTEMLERİ',
|
||||
primary: 'Birincil oturum açma yöntemi',
|
||||
|
@ -111,11 +127,13 @@ const sign_in_exp = {
|
|||
},
|
||||
setup_warning: {
|
||||
no_connector: '',
|
||||
no_connector_sms:
|
||||
no_connector_phone:
|
||||
'Henüz bir SMS bağlayıcısı kurmadınız. Öncelikle ayarları tamamlayana kadar oturum açma deneyiminiz yayınlanmayacaktır. ',
|
||||
no_connector_email:
|
||||
'Henüz bir e-posta adresi bağlayıcısı kurmadınız. Öncelikle ayarları tamamlayana kadar oturum açma deneyiminiz yayınlanmayacaktır. ',
|
||||
no_connector_social:
|
||||
no_connector_email_or_phone:
|
||||
'You haven’t set up both Email and SMS connectors yet. Your sign in experience won’t go live until you finish the settings first. ', // UNTRANSLATED
|
||||
no_connector_none:
|
||||
'Henüz herhangi bir social connector kurmadınız. Öncelikle ayarları tamamlayana kadar oturum açma deneyiminiz yayınlanmayacaktır. ',
|
||||
no_added_social_connector:
|
||||
'Şimdi birkaç social connector kurdunuz. Oturum açma deneyiminize bazı şeyler eklediğinizden emin olun.',
|
||||
|
|
|
@ -3,7 +3,7 @@ const sign_in_exp = {
|
|||
description: '自定义登录界面,并实时预览真实效果',
|
||||
tabs: {
|
||||
branding: '品牌',
|
||||
methods: '登录方式',
|
||||
sign_up_and_sign_in: 'Sign up and Sign in', // UNTRANSLATED
|
||||
others: '其它',
|
||||
},
|
||||
welcome: {
|
||||
|
@ -36,6 +36,22 @@ const sign_in_exp = {
|
|||
slogan: '标语',
|
||||
slogan_placeholder: '释放你的创意',
|
||||
},
|
||||
sign_up_and_sign_in: {
|
||||
identifiers: 'Sign up identifiers', // UNTRANSLATED
|
||||
identifiers_email: 'Email address', // UNTRANSLATED
|
||||
identifiers_phone: 'Phone number', // UNTRANSLATED
|
||||
identifiers_username: 'Username', // UNTRANSLATED
|
||||
identifiers_email_or_phone: 'Email address or phone number', // UNTRANSLATED
|
||||
identifiers_none: 'None', // UNTRANSLATED
|
||||
sign_up: {
|
||||
title: 'SIGN UP', // UNTRANSLATED
|
||||
sign_up_identifier: 'Sign up identifier', // UNTRANSLATED
|
||||
sign_up_authentication: 'Sign up authentication', // UNTRANSLATED
|
||||
set_a_password_option: 'Set a password', // UNTRANSLATED
|
||||
verify_at_sign_up_option: 'Verify at sign up', // UNTRANSLATED
|
||||
social_only_creation_description: '(This apply to social only account creation)', // UNTRANSLATED
|
||||
},
|
||||
},
|
||||
sign_in_methods: {
|
||||
title: '登录方式',
|
||||
primary: '主要登录方式',
|
||||
|
@ -105,9 +121,11 @@ const sign_in_exp = {
|
|||
},
|
||||
setup_warning: {
|
||||
no_connector: '',
|
||||
no_connector_sms: '你还没有设置 SMS 连接器。你需完成设置后登录体验才会生效。',
|
||||
no_connector_phone: '你还没有设置 SMS 连接器。你需完成设置后登录体验才会生效。',
|
||||
no_connector_email: '你还没有设置 email 连接器。你需完成设置后登录体验才会生效。',
|
||||
no_connector_social: '你还没有设置社交连接器。你需完成设置后登录体验才会生效。',
|
||||
no_connector_email_or_phone:
|
||||
'你还没有设置 email 和 SMS 连接器。你需完成设置后登录体验才会生效。',
|
||||
no_connector_none: '你还没有设置社交连接器。你需完成设置后登录体验才会生效。',
|
||||
no_added_social_connector: '你已经成功设置了一些社交连接器。点按「+」添加一些到你的登录体验。',
|
||||
},
|
||||
save_alert: {
|
||||
|
|
|
@ -164,6 +164,7 @@ importers:
|
|||
react-syntax-highlighter: ^15.5.0
|
||||
recharts: ^2.1.13
|
||||
remark-gfm: ^3.0.1
|
||||
snake-case: ^3.0.4
|
||||
stylelint: ^14.9.1
|
||||
swr: ^1.3.0
|
||||
typescript: ^4.7.4
|
||||
|
@ -231,6 +232,7 @@ importers:
|
|||
react-syntax-highlighter: 15.5.0_react@18.2.0
|
||||
recharts: 2.1.13_v2m5e27vhdewzwhryxwfaorcca
|
||||
remark-gfm: 3.0.1
|
||||
snake-case: 3.0.4
|
||||
stylelint: 14.9.1
|
||||
swr: 1.3.0_react@18.2.0
|
||||
typescript: 4.7.4
|
||||
|
@ -5747,7 +5749,6 @@ packages:
|
|||
dependencies:
|
||||
no-case: 3.0.4
|
||||
tslib: 2.4.0
|
||||
dev: false
|
||||
|
||||
/dot-prop/5.3.0:
|
||||
resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==}
|
||||
|
@ -9324,7 +9325,6 @@ packages:
|
|||
resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==}
|
||||
dependencies:
|
||||
tslib: 2.4.0
|
||||
dev: false
|
||||
|
||||
/lowercase-keys/1.0.1:
|
||||
resolution: {integrity: sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==}
|
||||
|
@ -10097,7 +10097,6 @@ packages:
|
|||
dependencies:
|
||||
lower-case: 2.0.2
|
||||
tslib: 2.4.0
|
||||
dev: false
|
||||
|
||||
/nock/13.2.2:
|
||||
resolution: {integrity: sha512-PcBHuvl9i6zfaJ50A7LS55oU+nFLv8htXIhffJO+FxyfibdZ4jEvd9kTuvkrJireBFIGMZ+oUIRpMK5gU9h//g==}
|
||||
|
@ -12310,8 +12309,7 @@ packages:
|
|||
resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==}
|
||||
dependencies:
|
||||
dot-case: 3.0.4
|
||||
tslib: 2.3.1
|
||||
dev: false
|
||||
tslib: 2.4.0
|
||||
|
||||
/snakecase-keys/5.1.2:
|
||||
resolution: {integrity: sha512-fvtDQZqPBqYb0dEY97TGuOMbN2NJ05Tj4MaoKwjTKkmjcG6mrd58JYGr23UWZRi6Aqv49Fk4HtjTIStOQenaug==}
|
||||
|
@ -13165,6 +13163,7 @@ packages:
|
|||
|
||||
/tslib/2.3.1:
|
||||
resolution: {integrity: sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==}
|
||||
dev: true
|
||||
|
||||
/tslib/2.4.0:
|
||||
resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==}
|
||||
|
|
Loading…
Reference in a new issue