0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-01-27 21:39:16 -05:00

refactor(ui): extract secondary page layout (#2285)

This commit is contained in:
simeng-li 2022-10-31 18:10:53 +08:00 committed by GitHub
parent a397a28148
commit d81c497751
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 69 additions and 252 deletions

View file

@ -0,0 +1,39 @@
import { useTranslation } from 'react-i18next';
import type { TFuncKey } from 'react-i18next';
import NavBar from '@/components/NavBar';
import * as styles from './index.module.scss';
type Props = {
title?: TFuncKey;
description?: TFuncKey;
titleProps?: Record<string, unknown>;
descriptionProps?: Record<string, unknown>;
children: React.ReactNode;
};
const SecondaryPageWrapper = ({
title,
description,
titleProps,
descriptionProps,
children,
}: Props) => {
const { t } = useTranslation();
return (
<div className={styles.wrapper}>
<NavBar />
<div className={styles.container}>
{title && <div className={styles.title}>{t(title, titleProps)}</div>}
{description && (
<div className={styles.description}>{t(description, descriptionProps)}</div>
)}
{children}
</div>
</div>
);
};
export default SecondaryPageWrapper;

View file

@ -1,18 +1,14 @@
import { useTranslation } from 'react-i18next';
import { useParams } from 'react-router-dom';
import NavBar from '@/components/NavBar';
import SecondaryPageWrapper from '@/components/SecondaryPageWrapper';
import { EmailPasswordless, PhonePasswordless } from '@/containers/Passwordless';
import ErrorPage from '@/pages/ErrorPage';
import * as styles from './index.module.scss';
type Props = {
method?: string;
};
const ForgotPassword = () => {
const { t } = useTranslation();
const { method = '' } = useParams<Props>();
// TODO: @simeng LOG-4486 apply supported method guard validation. Including the form hasSwitch validation bellow
@ -23,17 +19,13 @@ const ForgotPassword = () => {
const PasswordlessForm = method === 'email' ? EmailPasswordless : PhonePasswordless;
return (
<div className={styles.wrapper}>
<NavBar />
<div className={styles.container}>
<div className={styles.title}>{t('description.reset_password')}</div>
<div className={styles.description}>
{t(`description.reset_password_description_${method === 'email' ? 'email' : 'sms'}`)}
</div>
{/* eslint-disable-next-line jsx-a11y/no-autofocus */}
<PasswordlessForm autoFocus hasSwitch type="forgot-password" hasTerms={false} />
</div>
</div>
<SecondaryPageWrapper
title="description.reset_password"
description={`description.reset_password_description_${method === 'email' ? 'email' : 'sms'}`}
>
{/* eslint-disable-next-line jsx-a11y/no-autofocus */}
<PasswordlessForm autoFocus hasSwitch type="forgot-password" hasTerms={false} />
</SecondaryPageWrapper>
);
};

View file

@ -1,39 +0,0 @@
@use '@/scss/underscore' as _;
.wrapper {
@include _.full-page;
}
.container {
@include _.full-width;
margin-top: _.unit(2);
}
.title {
margin-bottom: _.unit(1);
}
.detail {
margin-bottom: _.unit(6);
@include _.text-hint;
}
:global(body.mobile) {
.container {
margin-top: _.unit(2);
}
.title {
@include _.title;
}
}
:global(body.desktop) {
.container {
margin-top: _.unit(12);
}
.title {
@include _.title-desktop;
}
}

View file

@ -1,24 +1,21 @@
import { useTranslation } from 'react-i18next';
import { useParams, useLocation } from 'react-router-dom';
import { is } from 'superstruct';
import NavBar from '@/components/NavBar';
import SecondaryPageWrapper from '@/components/SecondaryPageWrapper';
import PasscodeValidation from '@/containers/PasscodeValidation';
import ErrorPage from '@/pages/ErrorPage';
import type { UserFlow } from '@/types';
import { passcodeStateGuard, passcodeMethodGuard, userFlowGuard } from '@/types/guard';
import * as styles from './index.module.scss';
type Parameters = {
type: UserFlow;
method: string;
};
const Passcode = () => {
const { t } = useTranslation();
const { method, type = '' } = useParams<Parameters>();
const { state } = useLocation();
const invalidType = !is(type, userFlowGuard);
const invalidMethod = !is(method, passcodeMethodGuard);
const invalidState = !is(state, passcodeStateGuard);
@ -34,18 +31,13 @@ const Passcode = () => {
}
return (
<div className={styles.wrapper}>
<NavBar />
<div className={styles.container}>
<div className={styles.title}>{t('action.enter_passcode')}</div>
<div className={styles.detail}>
{t('description.enter_passcode', {
address: t(`description.${method === 'email' ? 'email' : 'phone_number'}`),
})}
</div>
<PasscodeValidation type={type} method={method} target={target} />
</div>
</div>
<SecondaryPageWrapper
title="action.enter_passcode"
description="description.enter_passcode"
descriptionProps={{ address: `description.${method === 'email' ? 'email' : 'phone_number'}` }}
>
<PasscodeValidation type={type} method={method} target={target} />
</SecondaryPageWrapper>
);
};

View file

@ -1,33 +0,0 @@
@use '@/scss/underscore' as _;
.wrapper {
@include _.full-page;
}
.container {
@include _.full-width;
margin-top: _.unit(2);
}
:global(body.mobile) {
.container {
margin-top: _.unit(2);
}
.title {
@include _.title;
margin-bottom: _.unit(6);
}
}
:global(body.desktop) {
.container {
margin-top: _.unit(12);
}
.title {
@include _.title-desktop;
margin-bottom: _.unit(4);
}
}

View file

@ -1,22 +1,12 @@
import { useTranslation } from 'react-i18next';
import NavBar from '@/components/NavBar';
import SecondaryPageWrapper from '@/components/SecondaryPageWrapper';
import ResetPasswordForm from '@/containers/ResetPassword';
import * as styles from './index.module.scss';
const ResetPassword = () => {
const { t } = useTranslation();
return (
<div className={styles.wrapper}>
<NavBar />
<div className={styles.container}>
<div className={styles.title}>{t('description.new_password')}</div>
{/* eslint-disable-next-line jsx-a11y/no-autofocus */}
<ResetPasswordForm autoFocus />
</div>
</div>
<SecondaryPageWrapper title="description.new_password">
{/* eslint-disable-next-line jsx-a11y/no-autofocus */}
<ResetPasswordForm autoFocus />
</SecondaryPageWrapper>
);
};

View file

@ -1,32 +0,0 @@
@use '@/scss/underscore' as _;
.wrapper {
@include _.full-page;
}
.container {
@include _.full-width;
margin-top: _.unit(2);
}
:global(body.mobile) {
.container {
margin-top: _.unit(2);
}
.title {
@include _.title;
margin-bottom: _.unit(6);
}
}
:global(body.desktop) {
.container {
margin-top: _.unit(12);
}
.title {
@include _.title-desktop;
margin-bottom: _.unit(4);
}
}

View file

@ -1,20 +1,16 @@
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useParams } from 'react-router-dom';
import NavBar from '@/components/NavBar';
import SecondaryPageWrapper from '@/components/SecondaryPageWrapper';
import CreateAccount from '@/containers/CreateAccount';
import { PhonePasswordless, EmailPasswordless } from '@/containers/Passwordless';
import ErrorPage from '@/pages/ErrorPage';
import * as styles from './index.module.scss';
type Parameters = {
method?: string;
};
const SecondaryRegister = () => {
const { t } = useTranslation();
const { method = 'username' } = useParams<Parameters>();
const registerForm = useMemo(() => {
@ -36,15 +32,7 @@ const SecondaryRegister = () => {
return <ErrorPage />;
}
return (
<div className={styles.wrapper}>
<NavBar />
<div className={styles.container}>
<div className={styles.title}>{t('action.create_account')}</div>
{registerForm}
</div>
</div>
);
return <SecondaryPageWrapper title="action.create_account">{registerForm}</SecondaryPageWrapper>;
};
export default SecondaryRegister;

View file

@ -1,32 +0,0 @@
@use '@/scss/underscore' as _;
.wrapper {
@include _.full-page;
}
.container {
@include _.full-width;
margin-top: _.unit(2);
}
:global(body.mobile) {
.container {
margin-top: _.unit(2);
}
.title {
@include _.title;
margin-bottom: _.unit(6);
}
}
:global(body.desktop) {
.container {
margin-top: _.unit(12);
}
.title {
@include _.title-desktop;
margin-bottom: _.unit(4);
}
}

View file

@ -1,20 +1,16 @@
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useParams } from 'react-router-dom';
import NavBar from '@/components/NavBar';
import SecondaryPageWrapper from '@/components/SecondaryPageWrapper';
import { PhonePasswordless, EmailPasswordless } from '@/containers/Passwordless';
import UsernameSignIn from '@/containers/UsernameSignIn';
import ErrorPage from '@/pages/ErrorPage';
import * as styles from './index.module.scss';
type Props = {
method?: string;
};
const SecondarySignIn = () => {
const { t } = useTranslation();
const { method = 'username' } = useParams<Props>();
const signInForm = useMemo(() => {
@ -36,15 +32,7 @@ const SecondarySignIn = () => {
return <ErrorPage />;
}
return (
<div className={styles.wrapper}>
<NavBar />
<div className={styles.container}>
<div className={styles.title}>{t('action.sign_in')}</div>
{signInForm}
</div>
</div>
);
return <SecondaryPageWrapper title="action.sign_in">{signInForm}</SecondaryPageWrapper>;
};
export default SecondarySignIn;

View file

@ -1,26 +0,0 @@
@use '@/scss/underscore' as _;
.wrapper {
@include _.full-page;
}
.container {
@include _.full-width;
}
:global(body.mobile) {
.container {
margin-top: _.unit(2);
}
}
:global(body.desktop) {
.container {
margin-top: _.unit(12);
}
.title {
@include _.title-desktop;
margin-bottom: _.unit(8);
}
}

View file

@ -1,33 +1,23 @@
import { useTranslation } from 'react-i18next';
import { useParams } from 'react-router-dom';
import NavBar from '@/components/NavBar';
import SecondaryPageWrapper from '@/components/SecondaryPageWrapper';
import SocialCreateAccount from '@/containers/SocialCreateAccount';
import usePlatform from '@/hooks/use-platform';
import * as styles from './index.module.scss';
type Parameters = {
connector: string;
};
const SocialRegister = () => {
const { t } = useTranslation();
const { connector } = useParams<Parameters>();
const { isMobile } = usePlatform();
if (!connector) {
return null;
}
return (
<div className={styles.wrapper}>
<NavBar title={isMobile ? t('description.bind_account_title') : undefined} />
<div className={styles.container}>
{!isMobile && <div className={styles.title}>{t('description.bind_account_title')}</div>}
<SocialCreateAccount connectorId={connector} />
</div>
</div>
<SecondaryPageWrapper title="description.bind_account_title">
<SocialCreateAccount connectorId={connector} />
</SecondaryPageWrapper>
);
};