From cf6da809621b4ebe8c9acedb035b689e7e1b60b1 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Wed, 25 Oct 2023 17:27:32 +0700 Subject: Update register page --- package.json | 1 + src-migrate/common/components/elements/Modal.tsx | 18 +++-- .../common/components/elements/ReCaptcha.tsx | 17 +++++ src-migrate/common/stores/useRegisterStore.ts | 5 ++ src-migrate/common/types/auth.ts | 31 ++++++-- .../account-activation/components/FormEmail.tsx | 7 ++ .../account-activation/components/FormOTP.tsx | 23 ++++++ .../account-activation/components/FormToken.tsx | 83 ++++++++++++++++++++++ src-migrate/modules/account-activation/index.tsx | 12 ++++ src-migrate/modules/register/components/Form.tsx | 53 ++++++++------ .../modules/register/components/FormCaptcha.tsx | 14 ++++ .../modules/register/components/Register.tsx | 39 ---------- .../modules/register/components/TermCondition.tsx | 26 +++++-- src-migrate/modules/register/index.tsx | 46 +++++++++++- src-migrate/pages/register.tsx | 9 ++- src-migrate/services/auth.ts | 35 ++++++++- 16 files changed, 337 insertions(+), 82 deletions(-) create mode 100644 src-migrate/common/components/elements/ReCaptcha.tsx create mode 100644 src-migrate/modules/account-activation/components/FormEmail.tsx create mode 100644 src-migrate/modules/account-activation/components/FormOTP.tsx create mode 100644 src-migrate/modules/account-activation/components/FormToken.tsx create mode 100644 src-migrate/modules/account-activation/index.tsx create mode 100644 src-migrate/modules/register/components/FormCaptcha.tsx delete mode 100644 src-migrate/modules/register/components/Register.tsx diff --git a/package.json b/package.json index 127de3df..589eabc4 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ "@types/node": "^20.8.7", "@types/react": "^18.2.31", "@types/react-dom": "^18.2.14", + "@types/react-google-recaptcha": "^2.1.7", "autoprefixer": "^10.4.14", "eslint": "8.26.0", "eslint-config-next": "13.0.0", diff --git a/src-migrate/common/components/elements/Modal.tsx b/src-migrate/common/components/elements/Modal.tsx index ad1fe51b..91421251 100644 --- a/src-migrate/common/components/elements/Modal.tsx +++ b/src-migrate/common/components/elements/Modal.tsx @@ -11,7 +11,8 @@ type Props = { active: boolean title?: string close?: () => void, - className?: string + className?: string, + mode?: "mobile" | "desktop" } const Modal = ({ @@ -19,11 +20,14 @@ const Modal = ({ active = false, title, close, - className + className, + mode }: Props) => { const { width } = useWindowSize() const [rendered, setRendered] = useState(false) + mode = mode || width >= 768 ? "desktop" : "mobile" + useEffect(() => { setRendered(true) }, []) @@ -31,16 +35,16 @@ const Modal = ({ const modalClassNames = clsxm( "fixed bg-white max-h-[80vh] overflow-auto p-4 pt-0 z-[60] border-gray_r-6", { - "left-1/2 -translate-x-1/2 translate-y-1/2 bottom-1/2 md:w-1/4 lg:w-1/3 border rounded-xl": width >= 768, - "left-0 w-full border-t bottom-0 rounded-t-xl": width < 768 + "left-1/2 -translate-x-1/2 translate-y-1/2 bottom-1/2 w-11/12 md:w-1/4 lg:w-1/3 border rounded-xl": mode === 'desktop', + "left-0 w-full border-t bottom-0 rounded-t-xl": mode === 'mobile' }, className ) const variant = { - initial: { bottom: width >= 768 ? '45%' : '-100%', opacity: 0 }, - animate: { bottom: width >= 768 ? '50%' : 0, opacity: 1 }, - exit: { bottom: width >= 768 ? '55%' : '-100%', opacity: 0 }, + initial: { bottom: mode === 'desktop' ? '45%' : '-100%', opacity: 0 }, + animate: { bottom: mode === 'desktop' ? '50%' : 0, opacity: 1 }, + exit: { bottom: mode === 'desktop' ? '55%' : '-100%', opacity: 0 }, transition: { ease: 'linear', duration: 0.25 } } diff --git a/src-migrate/common/components/elements/ReCaptcha.tsx b/src-migrate/common/components/elements/ReCaptcha.tsx new file mode 100644 index 00000000..1bc31d90 --- /dev/null +++ b/src-migrate/common/components/elements/ReCaptcha.tsx @@ -0,0 +1,17 @@ +import ReCAPTCHA, { ReCAPTCHAProps } from "react-google-recaptcha" + +const GOOGLE_RECAPTCHA_KEY = process.env.NEXT_PUBLIC_RECAPTCHA_GOOGLE || '' + +type Props = Omit & { + sitekey?: string; +} + +const ReCaptcha = (props: Props) => { + const { sitekey, ...rest } = props + + return ( + + ) +} + +export default ReCaptcha \ No newline at end of file diff --git a/src-migrate/common/stores/useRegisterStore.ts b/src-migrate/common/stores/useRegisterStore.ts index fcd2cd8b..725bbfda 100644 --- a/src-migrate/common/stores/useRegisterStore.ts +++ b/src-migrate/common/stores/useRegisterStore.ts @@ -6,10 +6,12 @@ type State = { isValid: boolean; isCheckedTNC: boolean; isOpenTNC: boolean; + isValidCaptcha: boolean; }; type Action = { updateForm: (name: string, value: string) => void; + updateValidCaptcha: (value: boolean) => void; toggleCheckTNC: () => void; openTNC: () => void; closeTNC: () => void; @@ -21,10 +23,12 @@ export const useRegisterStore = create((set) => ({ name: '', email: '', password: '', + phone: '', }, isValid: false, isCheckedTNC: false, isOpenTNC: false, + isValidCaptcha: false, updateForm: (name, value) => set((state) => { const updatedForm = { ...state.form, [name]: value }; @@ -49,4 +53,5 @@ export const useRegisterStore = create((set) => ({ toggleCheckTNC: () => set((state) => ({ isCheckedTNC: !state.isCheckedTNC })), openTNC: () => set(() => ({ isOpenTNC: true })), closeTNC: () => set(() => ({ isOpenTNC: false })), + updateValidCaptcha: (value) => set(() => ({ isValidCaptcha: value })), })); diff --git a/src-migrate/common/types/auth.ts b/src-migrate/common/types/auth.ts index 63fac6e0..3d9ffee4 100644 --- a/src-migrate/common/types/auth.ts +++ b/src-migrate/common/types/auth.ts @@ -22,11 +22,32 @@ export type RegisterProps = { email: string; password: string; company: string; + phone: string; +}; + +export type RegisterResApiProps = { + register: boolean; + reason: 'EMAIL_USED' | null; +}; + +type ActivationResProps = { + activation: boolean; + user: AuthProps | null; +}; + +export type ActivationTokenProps = { + token: string; +}; + +export type ActivationTokenResApiProps = ActivationResProps & { + reason: 'INVALID_TOKEN' | null; +}; + +export type ActivationOtpProps = { + email: string; + otp: string; }; -export type RegisterApiProps = OdooApiProps & { - result: { - register: boolean; - reason?: 'EMAIL_USED'; - }; +export type ActivationOtpResApiProps = ActivationResProps & { + reason: 'INVALID_OTP' | null; }; diff --git a/src-migrate/modules/account-activation/components/FormEmail.tsx b/src-migrate/modules/account-activation/components/FormEmail.tsx new file mode 100644 index 00000000..f16ab93f --- /dev/null +++ b/src-migrate/modules/account-activation/components/FormEmail.tsx @@ -0,0 +1,7 @@ +const FormEmail = () => { + return ( +
FormEmail
+ ) +} + +export default FormEmail \ No newline at end of file diff --git a/src-migrate/modules/account-activation/components/FormOTP.tsx b/src-migrate/modules/account-activation/components/FormOTP.tsx new file mode 100644 index 00000000..24e9e7f6 --- /dev/null +++ b/src-migrate/modules/account-activation/components/FormOTP.tsx @@ -0,0 +1,23 @@ +import { HStack, PinInput, PinInputField } from "@chakra-ui/react" +import Modal from '~/common/components/elements/Modal' + +const FormOTP = () => { + return ( + +
+
Masukan Kode OTP
+ + + + + + + + +
Kode OTP dikirimkan ke email anda
+
+
+ ) +} + +export default FormOTP \ No newline at end of file diff --git a/src-migrate/modules/account-activation/components/FormToken.tsx b/src-migrate/modules/account-activation/components/FormToken.tsx new file mode 100644 index 00000000..041fbae8 --- /dev/null +++ b/src-migrate/modules/account-activation/components/FormToken.tsx @@ -0,0 +1,83 @@ +import { Alert, AlertDescription, AlertIcon, AlertTitle, Spinner } from "@chakra-ui/react" +import { useRouter } from "next/router" +import { useEffect, useState } from "react" +import Link from "next/link" +import { useMutation } from "react-query" + +import Modal from "~/common/components/elements/Modal" +import { ActivationTokenProps } from "~/common/types/auth" +import { activationUserToken } from "~/services/auth" + +type StatusProps = "success" | "error" | "loading"; + +const FormToken = () => { + const { query } = useRouter() + const [status, setStatus] = useState("loading") + const [active, setActive] = useState(false) + + const mutation = useMutation({ + mutationFn: (data: ActivationTokenProps) => activationUserToken(data), + onSuccess: (data) => { + if (data.activation === true) { + setStatus("success") + } else { + setStatus("error") + } + } + }) + + useEffect(() => { + if (query?.activation === 'token' && typeof query?.token === 'string') { + mutation.mutate({ token: query.token }) + setActive(true) + } + //eslint-disable-next-line + }, [query.activation, query.token]) + + return ( + +
+ {status === 'loading' && ( + <> + +
Mohon tunggu sedang memverifikasi akun
+ + )} + + {status !== 'loading' && ( + + + + Aktivasi akun {status === 'success' ? 'berhasil' : 'gagal'} + + + {status === 'success' && ( + <> + Akun anda berhasil diaktifkan, selamat berbelanja di Indoteknik. + Kembali ke halaman utama + + )} + + {status === 'error' && mutation.data?.reason === 'INVALID_TOKEN' && ( + <> + Token sudah kadaluwarsa, silahkan coba kembali. + Aktivasi Akun + + )} + + + )} +
+
+ ) +} + +export default FormToken \ No newline at end of file diff --git a/src-migrate/modules/account-activation/index.tsx b/src-migrate/modules/account-activation/index.tsx new file mode 100644 index 00000000..edcc6652 --- /dev/null +++ b/src-migrate/modules/account-activation/index.tsx @@ -0,0 +1,12 @@ +import { useRouter } from "next/router" +import FormToken from "./components/FormToken" + +const AccountActivation = () => { + return ( + <> + + + ) +} + +export default AccountActivation \ No newline at end of file diff --git a/src-migrate/modules/register/components/Form.tsx b/src-migrate/modules/register/components/Form.tsx index ac194b46..fc1567ab 100644 --- a/src-migrate/modules/register/components/Form.tsx +++ b/src-migrate/modules/register/components/Form.tsx @@ -3,9 +3,17 @@ import { useMutation } from "react-query"; import { useRegisterStore } from "~/common/stores/useRegisterStore"; import { RegisterProps } from "~/common/types/auth"; import { registerUser } from "~/services/auth"; +import TermCondition from "./TermCondition"; +import FormCaptcha from "./FormCaptcha"; const Form = () => { - const { form, isValid, isCheckedTNC, updateForm, toggleCheckTNC, openTNC } = useRegisterStore() + const { + form, + isValid, + isCheckedTNC, + isValidCaptcha, + updateForm, + } = useRegisterStore() const handleInputChange = (event: ChangeEvent) => { const { name, value } = event.target; @@ -20,8 +28,10 @@ const Form = () => { e.preventDefault() const response = await mutation.mutateAsync(form) - console.log(response); + if (response?.register === true) { + + } } return ( @@ -57,6 +67,20 @@ const Form = () => { /> +
+ + + +
+
@@ -68,6 +92,7 @@ const Form = () => { placeholder='Masukan alamat email anda' value={form.email} onChange={handleInputChange} + autoComplete="username" />
@@ -81,32 +106,18 @@ const Form = () => { placeholder='••••••••••••' value={form.password} onChange={handleInputChange} + autoComplete="current-password" /> -
- - - {' '} - - syarat dan ketentuan - - -
+ + + diff --git a/src-migrate/modules/register/components/FormCaptcha.tsx b/src-migrate/modules/register/components/FormCaptcha.tsx new file mode 100644 index 00000000..967be017 --- /dev/null +++ b/src-migrate/modules/register/components/FormCaptcha.tsx @@ -0,0 +1,14 @@ +import ReCaptcha from '~/common/components/elements/ReCaptcha' +import { useRegisterStore } from '~/common/stores/useRegisterStore' + +const FormCaptcha = () => { + const { updateValidCaptcha } = useRegisterStore() + + const handleCaptchaChange = (value: string | null) => { + updateValidCaptcha(value !== null) + } + + return +} + +export default FormCaptcha \ No newline at end of file diff --git a/src-migrate/modules/register/components/Register.tsx b/src-migrate/modules/register/components/Register.tsx deleted file mode 100644 index e3e29b9f..00000000 --- a/src-migrate/modules/register/components/Register.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import PageContent from "~/modules/page-content" -import Form from "./Form" -import Link from "next/link" -import Modal from "~/common/components/elements/Modal" -import TermCondition from "./TermCondition" - -const Register = () => { - return ( -
-
-
-

- Daftar Akun Indoteknik -

-

- Buat akun sekarang lebih mudah dan terverifikasi -

- -
- -
- Sudah punya akun Indoteknik?{' '} - - Masuk - -
-
- -
- -
-
- - -
- ) -} - -export default Register \ No newline at end of file diff --git a/src-migrate/modules/register/components/TermCondition.tsx b/src-migrate/modules/register/components/TermCondition.tsx index 304ffd69..aaba6604 100644 --- a/src-migrate/modules/register/components/TermCondition.tsx +++ b/src-migrate/modules/register/components/TermCondition.tsx @@ -1,15 +1,33 @@ +import { Checkbox } from '@chakra-ui/react' import React from 'react' import Modal from '~/common/components/elements/Modal' import { useRegisterStore } from '~/common/stores/useRegisterStore' import PageContent from '~/modules/page-content' const TermCondition = () => { - const { isOpenTNC, closeTNC } = useRegisterStore() + const { isOpenTNC, closeTNC, isCheckedTNC, toggleCheckTNC, openTNC } = useRegisterStore() return ( - - - + <> +
+ +
+ + {' '} + + syarat dan ketentuan + + +
+
+ + + + + ) } diff --git a/src-migrate/modules/register/index.tsx b/src-migrate/modules/register/index.tsx index ba5efa3a..6325ee09 100644 --- a/src-migrate/modules/register/index.tsx +++ b/src-migrate/modules/register/index.tsx @@ -1,3 +1,47 @@ -import Register from "./components/Register"; +import PageContent from "~/modules/page-content" +import Form from "./components/Form" +import Link from "next/link" +import Image from "next/image" +import IndoteknikLogo from "~/images/logo.png" +import AccountActivation from "../account-activation" + +const LOGO_WIDTH = 150; +const LOGO_HEIGHT = LOGO_WIDTH / 3; + +const Register = () => { + return ( +
+
+
+ + Logo Indoteknik + + +

+ Daftar Akun Indoteknik +

+

+ Buat akun sekarang lebih mudah dan terverifikasi +

+ + + +
+ Sudah punya akun Indoteknik?{' '} + + Masuk + +
+
+ +
+ +
+
+ + +
+ ) +} export default Register \ No newline at end of file diff --git a/src-migrate/pages/register.tsx b/src-migrate/pages/register.tsx index 4ba72cbc..bd5c37f4 100644 --- a/src-migrate/pages/register.tsx +++ b/src-migrate/pages/register.tsx @@ -1,12 +1,17 @@ import BasicLayout from "@/core/components/layouts/BasicLayout" +import { useWindowSize } from "usehooks-ts" import Register from "~/modules/register" const RegisterPage = () => { + const { width } = useWindowSize() + + const Layout = width > 768 ? BasicLayout : "div"; + return ( - + - + ) } diff --git a/src-migrate/services/auth.ts b/src-migrate/services/auth.ts index f2fd7761..feaabec8 100644 --- a/src-migrate/services/auth.ts +++ b/src-migrate/services/auth.ts @@ -1,10 +1,39 @@ import odooApi from '~/common/libs/odooApi'; -import { RegisterApiProps, RegisterProps } from '~/common/types/auth'; +import { + RegisterResApiProps, + RegisterProps, + ActivationTokenProps, + ActivationTokenResApiProps, + ActivationOtpProps, + ActivationOtpResApiProps, +} from '~/common/types/auth'; + +const BASE_PATH = '/api/v1/user'; export const registerUser = async ( data: RegisterProps -): Promise => { - const response = await odooApi('POST', '/api/v1/user/register', data); +): Promise => { + const response = await odooApi('POST', `${BASE_PATH}/register`, data); + + return response; +}; + +export const activationUserToken = async ( + params: ActivationTokenProps +): Promise => { + const response = await odooApi( + 'POST', + `${BASE_PATH}/activation-token`, + params + ); + + return response; +}; + +export const activationUserOTP = async ( + params: ActivationOtpProps +): Promise => { + const response = await odooApi('POST', `${BASE_PATH}/activation-otp`, params); return response; }; -- cgit v1.2.3