0
Fork 0
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:
Xiao Yijun 2022-10-19 15:13:39 +08:00 committed by GitHub
parent e5f8139880
commit e936ad04ae
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 346 additions and 243 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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 havent set up a SMS connector yet. Your sign in experience wont go live until you finish the settings first. ',
no_connector_email:
'You havent set up an Email connector yet. Your sign in experience wont go live until you finish the settings first. ',
no_connector_social:
no_connector_email_or_phone:
'You havent set up both Email and SMS connectors yet. Your sign in experience wont go live until you finish the settings first. ',
no_connector_none:
'You havent set up any social connectors yet. Your sign in experience wont go live until you finish the settings first. ',
no_added_social_connector:
'Youve set up a few social connectors now. Make sure to add some to your sign in experience.',

View file

@ -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 havent set up both Email and SMS connectors yet. Your sign in experience wont 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.",

View file

@ -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 havent set up both Email and SMS connectors yet. Your sign in experience wont go live until you finish the settings first. ', // UNTRANSLATED
no_connector_none:
'소셜 연동이 아직 설정되지 않았어요. 설정이 완료될 때 까지, 사용자는 이 로그인 방법을 사용할 수 없어요.',
no_added_social_connector:
'보다 많은 소셜 연동들을 설정하여, 고객에게 보다 나은 경험을 제공해보세요.',

View file

@ -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 havent set up both Email and SMS connectors yet. Your sign in experience wont 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.',

View file

@ -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 çı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 havent set up both Email and SMS connectors yet. Your sign in experience wont 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.',

View file

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

View file

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