- {Array.from({ length }).map((_, index) => (
-
{
- // eslint-disable-next-line @silverhand/fp/no-mutation
- inputReferences.current[index] = element;
- }}
- // eslint-disable-next-line react/no-array-index-key
- key={`${name}_${index}`}
- name={`${name}_${index}`}
- data-id={index}
- disabled={isDisabled}
- value={codes[index]}
- className={hasError ? styles.error : undefined}
- type="text"
- inputMode="numeric"
- maxLength={2} // Allow overwrite input
- onPaste={onPasteHandler}
- onInput={onInputHandler}
- onKeyDown={onKeyDownHandler}
- onFocus={onFocusHandler}
- />
- ))}
+
+
+ {Array.from({ length }).map((_, index) => (
+ {
+ // eslint-disable-next-line @silverhand/fp/no-mutation
+ inputReferences.current[index] = element;
+ }}
+ // eslint-disable-next-line react/no-array-index-key
+ key={`${name}_${index}`}
+ autoFocus={index === 0}
+ name={`${name}_${index}`}
+ data-id={index}
+ value={codes[index]}
+ className={error ? styles.error : undefined}
+ type="text"
+ inputMode="numeric"
+ maxLength={2} // Allow overwrite input
+ onPaste={onPasteHandler}
+ onInput={onInputHandler}
+ onKeyDown={onKeyDownHandler}
+ onFocus={onFocusHandler}
+ />
+ ))}
+
+ {error &&
}
);
};
diff --git a/packages/ui/src/containers/CreateAccount/index.module.scss b/packages/ui/src/containers/CreateAccount/index.module.scss
index 2b3b719c8..f6cd62ee8 100644
--- a/packages/ui/src/containers/CreateAccount/index.module.scss
+++ b/packages/ui/src/containers/CreateAccount/index.module.scss
@@ -2,7 +2,7 @@
.form {
width: 100%;
- max-width: 320px;
+ max-width: 360px;
@include _.flex-column;
> * {
diff --git a/packages/ui/src/containers/PasscodeValidation/index.module.scss b/packages/ui/src/containers/PasscodeValidation/index.module.scss
new file mode 100644
index 000000000..20507e724
--- /dev/null
+++ b/packages/ui/src/containers/PasscodeValidation/index.module.scss
@@ -0,0 +1,11 @@
+@use '@/scss/underscore' as _;
+
+.form {
+ width: 100%;
+ max-width: 360px;
+ @include _.flex-column;
+
+ > * {
+ width: 100%;
+ }
+}
diff --git a/packages/ui/src/containers/PasscodeValidation/index.tsx b/packages/ui/src/containers/PasscodeValidation/index.tsx
new file mode 100644
index 000000000..c3e10ce14
--- /dev/null
+++ b/packages/ui/src/containers/PasscodeValidation/index.tsx
@@ -0,0 +1,16 @@
+import React from 'react';
+
+import * as styles from './index.module.scss';
+
+type Props = {
+ type: 'sign-in' | 'register';
+ channel: 'email' | 'phone';
+};
+
+const PasscodeValidation = ({ type, channel }: Props) => {
+ console.log(type, channel);
+
+ return
;
+};
+
+export default PasscodeValidation;
diff --git a/packages/ui/src/containers/Passwordless/EmailPasswordless.tsx b/packages/ui/src/containers/Passwordless/EmailPasswordless.tsx
index d41aced7c..9bd3f238d 100644
--- a/packages/ui/src/containers/Passwordless/EmailPasswordless.tsx
+++ b/packages/ui/src/containers/Passwordless/EmailPasswordless.tsx
@@ -9,8 +9,7 @@ import classNames from 'classnames';
import React, { useState, useCallback, useMemo, useEffect, useContext } from 'react';
import { useTranslation } from 'react-i18next';
-import { sendEmailPasscode as sendRegisterEmailPasscode } from '@/apis/register';
-import { sendEmailPasscode as sendSignInEmailPasscode } from '@/apis/sign-in';
+import { getSendPasscodeApi } from '@/apis/utils';
import Button from '@/components/Button';
import { ErrorType } from '@/components/ErrorMessage';
import Input from '@/components/Input';
@@ -47,7 +46,7 @@ const EmailPasswordless = ({ type }: Props) => {
const [fieldErrors, setFieldErrors] = useState
({});
const { setToast } = useContext(PageContext);
- const sendPasscode = type === 'sign-in' ? sendSignInEmailPasscode : sendRegisterEmailPasscode;
+ const sendPasscode = getSendPasscodeApi(type, 'email');
const { loading, error, result, run: asyncSendPasscode } = useApi(sendPasscode);
diff --git a/packages/ui/src/containers/Passwordless/PhonePasswordless.tsx b/packages/ui/src/containers/Passwordless/PhonePasswordless.tsx
index 28b94509a..428a225d7 100644
--- a/packages/ui/src/containers/Passwordless/PhonePasswordless.tsx
+++ b/packages/ui/src/containers/Passwordless/PhonePasswordless.tsx
@@ -9,8 +9,7 @@ import classNames from 'classnames';
import React, { useState, useCallback, useMemo, useEffect, useContext } from 'react';
import { useTranslation } from 'react-i18next';
-import { sendPhonePasscode as sendRegisterPhonePasscode } from '@/apis/register';
-import { sendPhonePasscode as sendSignInPhonePasscode } from '@/apis/sign-in';
+import { getSendPasscodeApi } from '@/apis/utils';
import Button from '@/components/Button';
import { ErrorType } from '@/components/ErrorMessage';
import PhoneInput from '@/components/Input/PhoneInput';
@@ -48,7 +47,7 @@ const PhonePasswordless = ({ type }: Props) => {
const { phoneNumber, setPhoneNumber, isValidPhoneNumber } = usePhoneNumber();
- const sendPasscode = type === 'sign-in' ? sendSignInPhonePasscode : sendRegisterPhonePasscode;
+ const sendPasscode = getSendPasscodeApi(type, 'phone');
const { loading, error, result, run: asyncSendPasscode } = useApi(sendPasscode);
const validations = useMemo(
diff --git a/packages/ui/src/containers/Passwordless/index.module.scss b/packages/ui/src/containers/Passwordless/index.module.scss
index 3a5811973..9bb36c69b 100644
--- a/packages/ui/src/containers/Passwordless/index.module.scss
+++ b/packages/ui/src/containers/Passwordless/index.module.scss
@@ -2,7 +2,7 @@
.form {
width: 100%;
- max-width: 320px;
+ max-width: 360px;
@include _.flex-column;
> * {
diff --git a/packages/ui/src/containers/UsernameSignin/index.module.scss b/packages/ui/src/containers/UsernameSignin/index.module.scss
index 9d50c4fbd..133d305ae 100644
--- a/packages/ui/src/containers/UsernameSignin/index.module.scss
+++ b/packages/ui/src/containers/UsernameSignin/index.module.scss
@@ -2,7 +2,7 @@
.form {
width: 100%;
- max-width: 320px;
+ max-width: 360px;
@include _.flex-column;
> * {
diff --git a/packages/ui/src/pages/Passcode/index.module.scss b/packages/ui/src/pages/Passcode/index.module.scss
new file mode 100644
index 000000000..66b0a03c5
--- /dev/null
+++ b/packages/ui/src/pages/Passcode/index.module.scss
@@ -0,0 +1,22 @@
+@use '@/scss/underscore' as _;
+
+.wrapper {
+ position: relative;
+ padding: _.unit(8) _.unit(5);
+ @include _.flex-column;
+}
+
+.navBar {
+ width: 100%;
+ margin-bottom: _.unit(6);
+
+ svg {
+ margin-left: _.unit(-2);
+ }
+}
+
+.title {
+ width: 100%;
+ @include _.title;
+ margin-bottom: _.unit(9);
+}
diff --git a/packages/ui/src/pages/Passcode/index.tsx b/packages/ui/src/pages/Passcode/index.tsx
new file mode 100644
index 000000000..27eb69541
--- /dev/null
+++ b/packages/ui/src/pages/Passcode/index.tsx
@@ -0,0 +1,48 @@
+import React from 'react';
+import { useTranslation } from 'react-i18next';
+import { useHistory, useParams } from 'react-router-dom';
+
+import NavArrowIcon from '@/components/Icons/NavArrowIcon';
+import PasscodeValidation from '@/containers/PasscodeValidation';
+
+import * as styles from './index.module.scss';
+
+type Props = {
+ type: string;
+ channel: string;
+};
+
+const Passcode = () => {
+ const { t } = useTranslation();
+ const history = useHistory();
+ const { type, channel } = useParams();
+
+ // TODO: 404 page
+ if (type !== 'sign-in' && type !== 'register') {
+ window.location.assign('/404');
+
+ return null;
+ }
+
+ if (channel !== 'email' && channel !== 'phone') {
+ window.location.assign('/404');
+
+ return null;
+ }
+
+ return (
+
+
+ {
+ history.goBack();
+ }}
+ />
+
+
{t('sign_in.enter_passcode')}
+
+
+ );
+};
+
+export default Passcode;
diff --git a/packages/ui/src/pages/Register/index.module.scss b/packages/ui/src/pages/Register/index.module.scss
index 4364e12e6..66b0a03c5 100644
--- a/packages/ui/src/pages/Register/index.module.scss
+++ b/packages/ui/src/pages/Register/index.module.scss
@@ -2,7 +2,7 @@
.wrapper {
position: relative;
- padding: _.unit(8);
+ padding: _.unit(8) _.unit(5);
@include _.flex-column;
}
diff --git a/packages/ui/src/pages/Register/index.test.tsx b/packages/ui/src/pages/Register/index.test.tsx
index 00cfc39b2..278495017 100644
--- a/packages/ui/src/pages/Register/index.test.tsx
+++ b/packages/ui/src/pages/Register/index.test.tsx
@@ -1,5 +1,6 @@
import { render } from '@testing-library/react';
import React from 'react';
+import { Route, MemoryRouter } from 'react-router-dom';
import Register from '@/pages/Register';
@@ -7,8 +8,36 @@ jest.mock('@/apis/register', () => ({ register: jest.fn(async () => Promise.reso
describe('', () => {
test('renders without exploding', async () => {
- const { queryByText } = render();
+ const { queryByText } = render(
+
+
+
+ );
expect(queryByText('register.create_account')).not.toBeNull();
expect(queryByText('register.action')).not.toBeNull();
});
+
+ test('renders phone', async () => {
+ const { queryByText, container } = render(
+
+
+
+
+
+ );
+ expect(queryByText('register.create_account')).not.toBeNull();
+ expect(container.querySelector('input[name="phone"]')).not.toBeNull();
+ });
+
+ test('renders email', async () => {
+ const { queryByText, container } = render(
+
+
+
+
+
+ );
+ expect(queryByText('register.create_account')).not.toBeNull();
+ expect(container.querySelector('input[name="email"]')).not.toBeNull();
+ });
});
diff --git a/packages/ui/src/pages/Register/index.tsx b/packages/ui/src/pages/Register/index.tsx
index a5b62cbc7..a3a2c22a4 100644
--- a/packages/ui/src/pages/Register/index.tsx
+++ b/packages/ui/src/pages/Register/index.tsx
@@ -1,15 +1,33 @@
-import React from 'react';
+import React, { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
-import { useHistory } from 'react-router-dom';
+import { useHistory, useParams } from 'react-router-dom';
import NavArrowIcon from '@/components/Icons/NavArrowIcon';
import CreateAccount from '@/containers/CreateAccount';
+import { PhonePasswordless, EmailPasswordless } from '@/containers/Passwordless';
import * as styles from './index.module.scss';
+type Props = {
+ channel?: 'phone' | 'email' | 'username';
+};
+
const Register = () => {
const { t } = useTranslation();
const history = useHistory();
+ const { channel } = useParams();
+
+ const registerForm = useMemo(() => {
+ if (channel === 'phone') {
+ return ;
+ }
+
+ if (channel === 'email') {
+ return ;
+ }
+
+ return ;
+ }, [channel]);
return (
@@ -21,7 +39,7 @@ const Register = () => {
/>
{t('register.create_account')}
-
+ {registerForm}
);
};
diff --git a/packages/ui/src/pages/SecondarySignIn/index.module.scss b/packages/ui/src/pages/SecondarySignIn/index.module.scss
index 4364e12e6..66b0a03c5 100644
--- a/packages/ui/src/pages/SecondarySignIn/index.module.scss
+++ b/packages/ui/src/pages/SecondarySignIn/index.module.scss
@@ -2,7 +2,7 @@
.wrapper {
position: relative;
- padding: _.unit(8);
+ padding: _.unit(8) _.unit(5);
@include _.flex-column;
}
diff --git a/packages/ui/src/pages/SecondarySignIn/index.test.tsx b/packages/ui/src/pages/SecondarySignIn/index.test.tsx
new file mode 100644
index 000000000..26e3b7b45
--- /dev/null
+++ b/packages/ui/src/pages/SecondarySignIn/index.test.tsx
@@ -0,0 +1,43 @@
+import { render } from '@testing-library/react';
+import React from 'react';
+import { Route, MemoryRouter } from 'react-router-dom';
+
+import SecondarySignIn from '@/pages/SecondarySignIn';
+
+jest.mock('@/apis/register', () => ({ register: jest.fn(async () => Promise.resolve()) }));
+
+describe('