From 90710579ba1c12060877f6ec2d26103f9c31058d Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Mon, 23 Oct 2023 17:11:33 +0700 Subject: Refactor and migrate register page --- src-migrate/common/components/elements/Modal.tsx | 84 +++ .../components/skeleton/PageContentSkeleton.tsx | 19 + src-migrate/common/constants/menu.ts | 20 + src-migrate/common/libs/auth.ts | 26 + src-migrate/common/libs/clsxm.ts | 6 + src-migrate/common/libs/odooApi.ts | 81 +++ src-migrate/common/stores/useRegisterStore.ts | 52 ++ .../common/styles/fonts/Inter/Inter-Black.woff | Bin 0 -> 138764 bytes .../common/styles/fonts/Inter/Inter-Black.woff2 | Bin 0 -> 102868 bytes .../styles/fonts/Inter/Inter-BlackItalic.woff | Bin 0 -> 146824 bytes .../styles/fonts/Inter/Inter-BlackItalic.woff2 | Bin 0 -> 108752 bytes .../common/styles/fonts/Inter/Inter-Bold.woff | Bin 0 -> 143208 bytes .../common/styles/fonts/Inter/Inter-Bold.woff2 | Bin 0 -> 106140 bytes .../styles/fonts/Inter/Inter-BoldItalic.woff | Bin 0 -> 151052 bytes .../styles/fonts/Inter/Inter-BoldItalic.woff2 | Bin 0 -> 111808 bytes .../common/styles/fonts/Inter/Inter-ExtraBold.woff | Bin 0 -> 142920 bytes .../styles/fonts/Inter/Inter-ExtraBold.woff2 | Bin 0 -> 106108 bytes .../styles/fonts/Inter/Inter-ExtraBoldItalic.woff | Bin 0 -> 150628 bytes .../styles/fonts/Inter/Inter-ExtraBoldItalic.woff2 | Bin 0 -> 111708 bytes .../styles/fonts/Inter/Inter-ExtraLight.woff | Bin 0 -> 140724 bytes .../styles/fonts/Inter/Inter-ExtraLight.woff2 | Bin 0 -> 104232 bytes .../styles/fonts/Inter/Inter-ExtraLightItalic.woff | Bin 0 -> 149996 bytes .../fonts/Inter/Inter-ExtraLightItalic.woff2 | Bin 0 -> 111392 bytes .../common/styles/fonts/Inter/Inter-Italic.woff | Bin 0 -> 144372 bytes .../common/styles/fonts/Inter/Inter-Italic.woff2 | Bin 0 -> 106876 bytes .../common/styles/fonts/Inter/Inter-Light.woff | Bin 0 -> 140632 bytes .../common/styles/fonts/Inter/Inter-Light.woff2 | Bin 0 -> 104332 bytes .../styles/fonts/Inter/Inter-LightItalic.woff | Bin 0 -> 150092 bytes .../styles/fonts/Inter/Inter-LightItalic.woff2 | Bin 0 -> 111332 bytes .../common/styles/fonts/Inter/Inter-Medium.woff | Bin 0 -> 142552 bytes .../common/styles/fonts/Inter/Inter-Medium.woff2 | Bin 0 -> 105924 bytes .../styles/fonts/Inter/Inter-MediumItalic.woff | Bin 0 -> 150988 bytes .../styles/fonts/Inter/Inter-MediumItalic.woff2 | Bin 0 -> 112184 bytes .../common/styles/fonts/Inter/Inter-Regular.woff | Bin 0 -> 133844 bytes .../common/styles/fonts/Inter/Inter-Regular.woff2 | Bin 0 -> 98868 bytes .../common/styles/fonts/Inter/Inter-SemiBold.woff | Bin 0 -> 142932 bytes .../common/styles/fonts/Inter/Inter-SemiBold.woff2 | Bin 0 -> 105804 bytes .../styles/fonts/Inter/Inter-SemiBoldItalic.woff | Bin 0 -> 151180 bytes .../styles/fonts/Inter/Inter-SemiBoldItalic.woff2 | Bin 0 -> 112048 bytes .../common/styles/fonts/Inter/Inter-Thin.woff | Bin 0 -> 135920 bytes .../common/styles/fonts/Inter/Inter-Thin.woff2 | Bin 0 -> 99632 bytes .../styles/fonts/Inter/Inter-ThinItalic.woff | Bin 0 -> 145480 bytes .../styles/fonts/Inter/Inter-ThinItalic.woff2 | Bin 0 -> 106496 bytes .../styles/fonts/Inter/Inter-italic.var.woff2 | Bin 0 -> 245036 bytes .../styles/fonts/Inter/Inter-roman.var.woff2 | Bin 0 -> 227180 bytes .../common/styles/fonts/Inter/Inter.var.woff2 | Bin 0 -> 324864 bytes src-migrate/common/styles/fonts/Inter/inter.css | 199 ++++++ src-migrate/common/styles/globals.css | 674 +++++++++++++++++++++ src-migrate/common/types/auth.ts | 32 + src-migrate/common/types/nav.ts | 4 + src-migrate/common/types/odoo.ts | 6 + src-migrate/common/types/pageContent.ts | 5 + src-migrate/images/LOGO-INDOTEKNIK-GEAR-GREY.png | Bin 0 -> 14190 bytes src-migrate/images/LOGO-INDOTEKNIK-GEAR.png | Bin 0 -> 18304 bytes src-migrate/images/logo.png | Bin 0 -> 47325 bytes .../modules/header/components/HeaderDesktop.tsx | 82 +++ .../modules/header/components/HeaderMobile.tsx | 7 + .../modules/header/components/SearchBar.tsx | 24 + src-migrate/modules/header/index.tsx | 13 + src-migrate/modules/home/components/Home.tsx | 15 + .../modules/home/components/VerticalBanner.tsx | 7 + src-migrate/modules/home/index.tsx | 3 + src-migrate/modules/page-content/index.tsx | 22 + src-migrate/modules/register/components/Form.tsx | 117 ++++ .../modules/register/components/Register.tsx | 39 ++ .../modules/register/components/TermCondition.tsx | 16 + src-migrate/modules/register/index.tsx | 3 + src-migrate/pages/_app.tsx | 7 + src-migrate/pages/register.tsx | 13 + src-migrate/services/auth.ts | 10 + src-migrate/services/banner.ts | 0 src-migrate/services/pageContent.ts | 14 + 72 files changed, 1600 insertions(+) create mode 100644 src-migrate/common/components/elements/Modal.tsx create mode 100644 src-migrate/common/components/skeleton/PageContentSkeleton.tsx create mode 100644 src-migrate/common/constants/menu.ts create mode 100644 src-migrate/common/libs/auth.ts create mode 100644 src-migrate/common/libs/clsxm.ts create mode 100644 src-migrate/common/libs/odooApi.ts create mode 100644 src-migrate/common/stores/useRegisterStore.ts create mode 100644 src-migrate/common/styles/fonts/Inter/Inter-Black.woff create mode 100644 src-migrate/common/styles/fonts/Inter/Inter-Black.woff2 create mode 100644 src-migrate/common/styles/fonts/Inter/Inter-BlackItalic.woff create mode 100644 src-migrate/common/styles/fonts/Inter/Inter-BlackItalic.woff2 create mode 100644 src-migrate/common/styles/fonts/Inter/Inter-Bold.woff create mode 100644 src-migrate/common/styles/fonts/Inter/Inter-Bold.woff2 create mode 100644 src-migrate/common/styles/fonts/Inter/Inter-BoldItalic.woff create mode 100644 src-migrate/common/styles/fonts/Inter/Inter-BoldItalic.woff2 create mode 100644 src-migrate/common/styles/fonts/Inter/Inter-ExtraBold.woff create mode 100644 src-migrate/common/styles/fonts/Inter/Inter-ExtraBold.woff2 create mode 100644 src-migrate/common/styles/fonts/Inter/Inter-ExtraBoldItalic.woff create mode 100644 src-migrate/common/styles/fonts/Inter/Inter-ExtraBoldItalic.woff2 create mode 100644 src-migrate/common/styles/fonts/Inter/Inter-ExtraLight.woff create mode 100644 src-migrate/common/styles/fonts/Inter/Inter-ExtraLight.woff2 create mode 100644 src-migrate/common/styles/fonts/Inter/Inter-ExtraLightItalic.woff create mode 100644 src-migrate/common/styles/fonts/Inter/Inter-ExtraLightItalic.woff2 create mode 100644 src-migrate/common/styles/fonts/Inter/Inter-Italic.woff create mode 100644 src-migrate/common/styles/fonts/Inter/Inter-Italic.woff2 create mode 100644 src-migrate/common/styles/fonts/Inter/Inter-Light.woff create mode 100644 src-migrate/common/styles/fonts/Inter/Inter-Light.woff2 create mode 100644 src-migrate/common/styles/fonts/Inter/Inter-LightItalic.woff create mode 100644 src-migrate/common/styles/fonts/Inter/Inter-LightItalic.woff2 create mode 100644 src-migrate/common/styles/fonts/Inter/Inter-Medium.woff create mode 100644 src-migrate/common/styles/fonts/Inter/Inter-Medium.woff2 create mode 100644 src-migrate/common/styles/fonts/Inter/Inter-MediumItalic.woff create mode 100644 src-migrate/common/styles/fonts/Inter/Inter-MediumItalic.woff2 create mode 100644 src-migrate/common/styles/fonts/Inter/Inter-Regular.woff create mode 100644 src-migrate/common/styles/fonts/Inter/Inter-Regular.woff2 create mode 100644 src-migrate/common/styles/fonts/Inter/Inter-SemiBold.woff create mode 100644 src-migrate/common/styles/fonts/Inter/Inter-SemiBold.woff2 create mode 100644 src-migrate/common/styles/fonts/Inter/Inter-SemiBoldItalic.woff create mode 100644 src-migrate/common/styles/fonts/Inter/Inter-SemiBoldItalic.woff2 create mode 100644 src-migrate/common/styles/fonts/Inter/Inter-Thin.woff create mode 100644 src-migrate/common/styles/fonts/Inter/Inter-Thin.woff2 create mode 100644 src-migrate/common/styles/fonts/Inter/Inter-ThinItalic.woff create mode 100644 src-migrate/common/styles/fonts/Inter/Inter-ThinItalic.woff2 create mode 100644 src-migrate/common/styles/fonts/Inter/Inter-italic.var.woff2 create mode 100644 src-migrate/common/styles/fonts/Inter/Inter-roman.var.woff2 create mode 100644 src-migrate/common/styles/fonts/Inter/Inter.var.woff2 create mode 100644 src-migrate/common/styles/fonts/Inter/inter.css create mode 100644 src-migrate/common/styles/globals.css create mode 100644 src-migrate/common/types/auth.ts create mode 100644 src-migrate/common/types/nav.ts create mode 100644 src-migrate/common/types/odoo.ts create mode 100644 src-migrate/common/types/pageContent.ts create mode 100644 src-migrate/images/LOGO-INDOTEKNIK-GEAR-GREY.png create mode 100644 src-migrate/images/LOGO-INDOTEKNIK-GEAR.png create mode 100644 src-migrate/images/logo.png create mode 100644 src-migrate/modules/header/components/HeaderDesktop.tsx create mode 100644 src-migrate/modules/header/components/HeaderMobile.tsx create mode 100644 src-migrate/modules/header/components/SearchBar.tsx create mode 100644 src-migrate/modules/header/index.tsx create mode 100644 src-migrate/modules/home/components/Home.tsx create mode 100644 src-migrate/modules/home/components/VerticalBanner.tsx create mode 100644 src-migrate/modules/home/index.tsx create mode 100644 src-migrate/modules/page-content/index.tsx create mode 100644 src-migrate/modules/register/components/Form.tsx create mode 100644 src-migrate/modules/register/components/Register.tsx create mode 100644 src-migrate/modules/register/components/TermCondition.tsx create mode 100644 src-migrate/modules/register/index.tsx create mode 100644 src-migrate/pages/_app.tsx create mode 100644 src-migrate/pages/register.tsx create mode 100644 src-migrate/services/auth.ts create mode 100644 src-migrate/services/banner.ts create mode 100644 src-migrate/services/pageContent.ts (limited to 'src-migrate') diff --git a/src-migrate/common/components/elements/Modal.tsx b/src-migrate/common/components/elements/Modal.tsx new file mode 100644 index 00000000..ad1fe51b --- /dev/null +++ b/src-migrate/common/components/elements/Modal.tsx @@ -0,0 +1,84 @@ +import { XMarkIcon } from "@heroicons/react/24/outline"; +import { AnimatePresence, motion } from "framer-motion" +import { useEffect, useState } from "react"; +import ReactDOM from "react-dom"; +import { useWindowSize } from "usehooks-ts"; +import clsxm from "~/common/libs/clsxm"; + + +type Props = { + children: React.ReactNode + active: boolean + title?: string + close?: () => void, + className?: string +} + +const Modal = ({ + children, + active = false, + title, + close, + className +}: Props) => { + const { width } = useWindowSize() + const [rendered, setRendered] = useState(false) + + useEffect(() => { + setRendered(true) + }, []) + + 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 + }, + 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 }, + transition: { ease: 'linear', duration: 0.25 } + } + + return rendered && ReactDOM.createPortal( + + {active && ( + + )} + + {active && ( + +
+
+ {title} +
+ {close && ( + + )} +
+ + {children} +
+ )} + +
, + document.querySelector('body')! + ) +} + +export default Modal \ No newline at end of file diff --git a/src-migrate/common/components/skeleton/PageContentSkeleton.tsx b/src-migrate/common/components/skeleton/PageContentSkeleton.tsx new file mode 100644 index 00000000..bf85cff1 --- /dev/null +++ b/src-migrate/common/components/skeleton/PageContentSkeleton.tsx @@ -0,0 +1,19 @@ +const PageContentSkeleton = () => { + return ( +
+
+
+
+
+
+
+
+
+
+
+
+
+ ) +} + +export default PageContentSkeleton \ No newline at end of file diff --git a/src-migrate/common/constants/menu.ts b/src-migrate/common/constants/menu.ts new file mode 100644 index 00000000..853da507 --- /dev/null +++ b/src-migrate/common/constants/menu.ts @@ -0,0 +1,20 @@ +import { SecondaryNavItemProps } from '../types/nav' + +export const SECONDARY_MENU_ITEMS: SecondaryNavItemProps[] = [ + { + label: 'Semua Brand', + href: '/shop/brands' + }, + { + label: 'Ready Stock', + href: '/shop/search?orderBy=stock' + }, + { + label: 'Blog Indoteknik', + href: 'https://blog.indoteknik.com/' + }, + { + label: 'Indoteknik TV', + href: '/video' + } +] diff --git a/src-migrate/common/libs/auth.ts b/src-migrate/common/libs/auth.ts new file mode 100644 index 00000000..fb4e836a --- /dev/null +++ b/src-migrate/common/libs/auth.ts @@ -0,0 +1,26 @@ +import { deleteCookie, getCookie, setCookie } from 'cookies-next'; +import { AuthProps } from '../types/auth'; + +const COOKIE_KEY = 'auth'; + +export const getAuth = (): AuthProps | boolean => { + const auth = getCookie(COOKIE_KEY); + + if (typeof auth === 'string') { + return JSON.parse(auth); + } + + return false; +}; + +export const setAuth = (user: AuthProps): boolean => { + setCookie(COOKIE_KEY, JSON.stringify(user)); + + return true; +}; + +export const deleteAuth = (): boolean => { + deleteCookie(COOKIE_KEY); + + return true; +}; diff --git a/src-migrate/common/libs/clsxm.ts b/src-migrate/common/libs/clsxm.ts new file mode 100644 index 00000000..0fc10317 --- /dev/null +++ b/src-migrate/common/libs/clsxm.ts @@ -0,0 +1,6 @@ +import clsx, { ClassValue } from 'clsx'; +import { twMerge } from 'tw-merge'; + +export default function clsxm(...classes: ClassValue[]) { + return twMerge(clsx(...classes)); +} diff --git a/src-migrate/common/libs/odooApi.ts b/src-migrate/common/libs/odooApi.ts new file mode 100644 index 00000000..2dbc18d3 --- /dev/null +++ b/src-migrate/common/libs/odooApi.ts @@ -0,0 +1,81 @@ +import axios, { AxiosRequestConfig, Method } from 'axios'; +import { getCookie, setCookie } from 'cookies-next'; +import { getAuth } from './auth'; +import { AuthApiProps, AuthProps } from '../types/auth'; + +const ODOO_HOST = process.env.NEXT_PUBLIC_ODOO_API_HOST as string; + +const renewToken = async () => { + let token = await axios.get(`${ODOO_HOST}/api/token`); + setCookie('token', token.data.result); + + return token.data.result; +}; + +const getToken = async () => { + let token = getCookie('token'); + if (token == undefined) token = await renewToken(); + + return token; +}; + +const maxConnectionAttempt = 15; +let connectionAttempt = 0; + +const odooApi = async ( + method: Method, + url: string, + data = {}, + headers = {} +): Promise => { + connectionAttempt++; + + try { + let token = await getToken(); + const auth = getAuth(); + + let axiosParameter: AxiosRequestConfig = { + method, + url: process.env.NEXT_PUBLIC_ODOO_API_HOST + url, + headers: { Authorization: token, ...headers }, + }; + + if (typeof auth === 'object' && 'token' in auth) { + axiosParameter.headers = { + ...axiosParameter.headers, + Token: auth.token, + }; + } + + if (method.toUpperCase() === 'POST') { + axiosParameter.headers = { + ...axiosParameter.headers, + 'Content-Type': 'application/x-www-form-urlencoded', + }; + } + + if (Object.keys(data).length > 0) { + axiosParameter.data = new URLSearchParams( + Object.entries(data) + ).toString(); + } + + let res = await axios(axiosParameter); + const authResponse: AuthApiProps = res.data; + + if ( + authResponse.status.code == 401 && + connectionAttempt < maxConnectionAttempt + ) { + await renewToken(); + return odooApi(method, url, data, headers); + } + + return authResponse.result || null; + } catch (error) { + console.log(error); + return null; + } +}; + +export default odooApi; diff --git a/src-migrate/common/stores/useRegisterStore.ts b/src-migrate/common/stores/useRegisterStore.ts new file mode 100644 index 00000000..fcd2cd8b --- /dev/null +++ b/src-migrate/common/stores/useRegisterStore.ts @@ -0,0 +1,52 @@ +import { create } from 'zustand'; +import { RegisterProps } from '../types/auth'; + +type State = { + form: RegisterProps; + isValid: boolean; + isCheckedTNC: boolean; + isOpenTNC: boolean; +}; + +type Action = { + updateForm: (name: string, value: string) => void; + toggleCheckTNC: () => void; + openTNC: () => void; + closeTNC: () => void; +}; + +export const useRegisterStore = create((set) => ({ + form: { + company: '', + name: '', + email: '', + password: '', + }, + isValid: false, + isCheckedTNC: false, + isOpenTNC: false, + updateForm: (name, value) => + set((state) => { + const updatedForm = { ...state.form, [name]: value }; + + const fieldKeys = Object.keys( + updatedForm + ) as (keyof typeof updatedForm)[]; + + const allFieldsValid = fieldKeys.every((key) => { + const value = updatedForm[key]; + + if (key === 'company') return true; + + return value !== ''; + }); + + return { + form: updatedForm, + isValid: allFieldsValid, + }; + }), + toggleCheckTNC: () => set((state) => ({ isCheckedTNC: !state.isCheckedTNC })), + openTNC: () => set(() => ({ isOpenTNC: true })), + closeTNC: () => set(() => ({ isOpenTNC: false })), +})); diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Black.woff b/src-migrate/common/styles/fonts/Inter/Inter-Black.woff new file mode 100644 index 00000000..a18593a0 Binary files /dev/null and b/src-migrate/common/styles/fonts/Inter/Inter-Black.woff differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Black.woff2 b/src-migrate/common/styles/fonts/Inter/Inter-Black.woff2 new file mode 100644 index 00000000..68f64c9e Binary files /dev/null and b/src-migrate/common/styles/fonts/Inter/Inter-Black.woff2 differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-BlackItalic.woff b/src-migrate/common/styles/fonts/Inter/Inter-BlackItalic.woff new file mode 100644 index 00000000..b6b01943 Binary files /dev/null and b/src-migrate/common/styles/fonts/Inter/Inter-BlackItalic.woff differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-BlackItalic.woff2 b/src-migrate/common/styles/fonts/Inter/Inter-BlackItalic.woff2 new file mode 100644 index 00000000..1c9c7ca8 Binary files /dev/null and b/src-migrate/common/styles/fonts/Inter/Inter-BlackItalic.woff2 differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Bold.woff b/src-migrate/common/styles/fonts/Inter/Inter-Bold.woff new file mode 100644 index 00000000..eaf3d4bf Binary files /dev/null and b/src-migrate/common/styles/fonts/Inter/Inter-Bold.woff differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Bold.woff2 b/src-migrate/common/styles/fonts/Inter/Inter-Bold.woff2 new file mode 100644 index 00000000..2846f29c Binary files /dev/null and b/src-migrate/common/styles/fonts/Inter/Inter-Bold.woff2 differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-BoldItalic.woff b/src-migrate/common/styles/fonts/Inter/Inter-BoldItalic.woff new file mode 100644 index 00000000..32750761 Binary files /dev/null and b/src-migrate/common/styles/fonts/Inter/Inter-BoldItalic.woff differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-BoldItalic.woff2 b/src-migrate/common/styles/fonts/Inter/Inter-BoldItalic.woff2 new file mode 100644 index 00000000..0b1fe8e1 Binary files /dev/null and b/src-migrate/common/styles/fonts/Inter/Inter-BoldItalic.woff2 differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-ExtraBold.woff b/src-migrate/common/styles/fonts/Inter/Inter-ExtraBold.woff new file mode 100644 index 00000000..c2c17ede Binary files /dev/null and b/src-migrate/common/styles/fonts/Inter/Inter-ExtraBold.woff differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-ExtraBold.woff2 b/src-migrate/common/styles/fonts/Inter/Inter-ExtraBold.woff2 new file mode 100644 index 00000000..c24c2bdc Binary files /dev/null and b/src-migrate/common/styles/fonts/Inter/Inter-ExtraBold.woff2 differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-ExtraBoldItalic.woff b/src-migrate/common/styles/fonts/Inter/Inter-ExtraBoldItalic.woff new file mode 100644 index 00000000..c42f7052 Binary files /dev/null and b/src-migrate/common/styles/fonts/Inter/Inter-ExtraBoldItalic.woff differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-ExtraBoldItalic.woff2 b/src-migrate/common/styles/fonts/Inter/Inter-ExtraBoldItalic.woff2 new file mode 100644 index 00000000..4a81dc79 Binary files /dev/null and b/src-migrate/common/styles/fonts/Inter/Inter-ExtraBoldItalic.woff2 differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-ExtraLight.woff b/src-migrate/common/styles/fonts/Inter/Inter-ExtraLight.woff new file mode 100644 index 00000000..d0de5f39 Binary files /dev/null and b/src-migrate/common/styles/fonts/Inter/Inter-ExtraLight.woff differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-ExtraLight.woff2 b/src-migrate/common/styles/fonts/Inter/Inter-ExtraLight.woff2 new file mode 100644 index 00000000..f2ea706f Binary files /dev/null and b/src-migrate/common/styles/fonts/Inter/Inter-ExtraLight.woff2 differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-ExtraLightItalic.woff b/src-migrate/common/styles/fonts/Inter/Inter-ExtraLightItalic.woff new file mode 100644 index 00000000..81f1a28e Binary files /dev/null and b/src-migrate/common/styles/fonts/Inter/Inter-ExtraLightItalic.woff differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-ExtraLightItalic.woff2 b/src-migrate/common/styles/fonts/Inter/Inter-ExtraLightItalic.woff2 new file mode 100644 index 00000000..9af717ba Binary files /dev/null and b/src-migrate/common/styles/fonts/Inter/Inter-ExtraLightItalic.woff2 differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Italic.woff b/src-migrate/common/styles/fonts/Inter/Inter-Italic.woff new file mode 100644 index 00000000..a806b382 Binary files /dev/null and b/src-migrate/common/styles/fonts/Inter/Inter-Italic.woff differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Italic.woff2 b/src-migrate/common/styles/fonts/Inter/Inter-Italic.woff2 new file mode 100644 index 00000000..a619fc54 Binary files /dev/null and b/src-migrate/common/styles/fonts/Inter/Inter-Italic.woff2 differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Light.woff b/src-migrate/common/styles/fonts/Inter/Inter-Light.woff new file mode 100644 index 00000000..c496464d Binary files /dev/null and b/src-migrate/common/styles/fonts/Inter/Inter-Light.woff differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Light.woff2 b/src-migrate/common/styles/fonts/Inter/Inter-Light.woff2 new file mode 100644 index 00000000..bc4be665 Binary files /dev/null and b/src-migrate/common/styles/fonts/Inter/Inter-Light.woff2 differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-LightItalic.woff b/src-migrate/common/styles/fonts/Inter/Inter-LightItalic.woff new file mode 100644 index 00000000..f84a9de3 Binary files /dev/null and b/src-migrate/common/styles/fonts/Inter/Inter-LightItalic.woff differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-LightItalic.woff2 b/src-migrate/common/styles/fonts/Inter/Inter-LightItalic.woff2 new file mode 100644 index 00000000..842b2dfc Binary files /dev/null and b/src-migrate/common/styles/fonts/Inter/Inter-LightItalic.woff2 differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Medium.woff b/src-migrate/common/styles/fonts/Inter/Inter-Medium.woff new file mode 100644 index 00000000..d546843f Binary files /dev/null and b/src-migrate/common/styles/fonts/Inter/Inter-Medium.woff differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Medium.woff2 b/src-migrate/common/styles/fonts/Inter/Inter-Medium.woff2 new file mode 100644 index 00000000..f92498a2 Binary files /dev/null and b/src-migrate/common/styles/fonts/Inter/Inter-Medium.woff2 differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-MediumItalic.woff b/src-migrate/common/styles/fonts/Inter/Inter-MediumItalic.woff new file mode 100644 index 00000000..459a6568 Binary files /dev/null and b/src-migrate/common/styles/fonts/Inter/Inter-MediumItalic.woff differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-MediumItalic.woff2 b/src-migrate/common/styles/fonts/Inter/Inter-MediumItalic.woff2 new file mode 100644 index 00000000..0e3019f4 Binary files /dev/null and b/src-migrate/common/styles/fonts/Inter/Inter-MediumItalic.woff2 differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Regular.woff b/src-migrate/common/styles/fonts/Inter/Inter-Regular.woff new file mode 100644 index 00000000..62d3a618 Binary files /dev/null and b/src-migrate/common/styles/fonts/Inter/Inter-Regular.woff differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Regular.woff2 b/src-migrate/common/styles/fonts/Inter/Inter-Regular.woff2 new file mode 100644 index 00000000..6c2b6893 Binary files /dev/null and b/src-migrate/common/styles/fonts/Inter/Inter-Regular.woff2 differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-SemiBold.woff b/src-migrate/common/styles/fonts/Inter/Inter-SemiBold.woff new file mode 100644 index 00000000..a815f43a Binary files /dev/null and b/src-migrate/common/styles/fonts/Inter/Inter-SemiBold.woff differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-SemiBold.woff2 b/src-migrate/common/styles/fonts/Inter/Inter-SemiBold.woff2 new file mode 100644 index 00000000..611e90c9 Binary files /dev/null and b/src-migrate/common/styles/fonts/Inter/Inter-SemiBold.woff2 differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-SemiBoldItalic.woff b/src-migrate/common/styles/fonts/Inter/Inter-SemiBoldItalic.woff new file mode 100644 index 00000000..909e43a9 Binary files /dev/null and b/src-migrate/common/styles/fonts/Inter/Inter-SemiBoldItalic.woff differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-SemiBoldItalic.woff2 b/src-migrate/common/styles/fonts/Inter/Inter-SemiBoldItalic.woff2 new file mode 100644 index 00000000..545685bd Binary files /dev/null and b/src-migrate/common/styles/fonts/Inter/Inter-SemiBoldItalic.woff2 differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Thin.woff b/src-migrate/common/styles/fonts/Inter/Inter-Thin.woff new file mode 100644 index 00000000..62bc58cd Binary files /dev/null and b/src-migrate/common/styles/fonts/Inter/Inter-Thin.woff differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Thin.woff2 b/src-migrate/common/styles/fonts/Inter/Inter-Thin.woff2 new file mode 100644 index 00000000..abbc3a5c Binary files /dev/null and b/src-migrate/common/styles/fonts/Inter/Inter-Thin.woff2 differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-ThinItalic.woff b/src-migrate/common/styles/fonts/Inter/Inter-ThinItalic.woff new file mode 100644 index 00000000..700a7f06 Binary files /dev/null and b/src-migrate/common/styles/fonts/Inter/Inter-ThinItalic.woff differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-ThinItalic.woff2 b/src-migrate/common/styles/fonts/Inter/Inter-ThinItalic.woff2 new file mode 100644 index 00000000..ab0b2002 Binary files /dev/null and b/src-migrate/common/styles/fonts/Inter/Inter-ThinItalic.woff2 differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-italic.var.woff2 b/src-migrate/common/styles/fonts/Inter/Inter-italic.var.woff2 new file mode 100644 index 00000000..b826d5af Binary files /dev/null and b/src-migrate/common/styles/fonts/Inter/Inter-italic.var.woff2 differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-roman.var.woff2 b/src-migrate/common/styles/fonts/Inter/Inter-roman.var.woff2 new file mode 100644 index 00000000..6a256a06 Binary files /dev/null and b/src-migrate/common/styles/fonts/Inter/Inter-roman.var.woff2 differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter.var.woff2 b/src-migrate/common/styles/fonts/Inter/Inter.var.woff2 new file mode 100644 index 00000000..365eedc5 Binary files /dev/null and b/src-migrate/common/styles/fonts/Inter/Inter.var.woff2 differ diff --git a/src-migrate/common/styles/fonts/Inter/inter.css b/src-migrate/common/styles/fonts/Inter/inter.css new file mode 100644 index 00000000..de6ce273 --- /dev/null +++ b/src-migrate/common/styles/fonts/Inter/inter.css @@ -0,0 +1,199 @@ +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 100; + font-display: swap; + src: url('Inter-Thin.woff2?v=3.19') format('woff2'), + url('Inter-Thin.woff?v=3.19') format('woff'); +} +@font-face { + font-family: 'Inter'; + font-style: italic; + font-weight: 100; + font-display: swap; + src: url('Inter-ThinItalic.woff2?v=3.19') format('woff2'), + url('Inter-ThinItalic.woff?v=3.19') format('woff'); +} + +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 200; + font-display: swap; + src: url('Inter-ExtraLight.woff2?v=3.19') format('woff2'), + url('Inter-ExtraLight.woff?v=3.19') format('woff'); +} +@font-face { + font-family: 'Inter'; + font-style: italic; + font-weight: 200; + font-display: swap; + src: url('Inter-ExtraLightItalic.woff2?v=3.19') format('woff2'), + url('Inter-ExtraLightItalic.woff?v=3.19') format('woff'); +} + +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 300; + font-display: swap; + src: url('Inter-Light.woff2?v=3.19') format('woff2'), + url('Inter-Light.woff?v=3.19') format('woff'); +} +@font-face { + font-family: 'Inter'; + font-style: italic; + font-weight: 300; + font-display: swap; + src: url('Inter-LightItalic.woff2?v=3.19') format('woff2'), + url('Inter-LightItalic.woff?v=3.19') format('woff'); +} + +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('Inter-Regular.woff2?v=3.19') format('woff2'), + url('Inter-Regular.woff?v=3.19') format('woff'); +} +@font-face { + font-family: 'Inter'; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url('Inter-Italic.woff2?v=3.19') format('woff2'), + url('Inter-Italic.woff?v=3.19') format('woff'); +} + +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url('Inter-Medium.woff2?v=3.19') format('woff2'), + url('Inter-Medium.woff?v=3.19') format('woff'); +} +@font-face { + font-family: 'Inter'; + font-style: italic; + font-weight: 500; + font-display: swap; + src: url('Inter-MediumItalic.woff2?v=3.19') format('woff2'), + url('Inter-MediumItalic.woff?v=3.19') format('woff'); +} + +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url('Inter-SemiBold.woff2?v=3.19') format('woff2'), + url('Inter-SemiBold.woff?v=3.19') format('woff'); +} +@font-face { + font-family: 'Inter'; + font-style: italic; + font-weight: 600; + font-display: swap; + src: url('Inter-SemiBoldItalic.woff2?v=3.19') format('woff2'), + url('Inter-SemiBoldItalic.woff?v=3.19') format('woff'); +} + +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('Inter-Bold.woff2?v=3.19') format('woff2'), + url('Inter-Bold.woff?v=3.19') format('woff'); +} +@font-face { + font-family: 'Inter'; + font-style: italic; + font-weight: 700; + font-display: swap; + src: url('Inter-BoldItalic.woff2?v=3.19') format('woff2'), + url('Inter-BoldItalic.woff?v=3.19') format('woff'); +} + +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 800; + font-display: swap; + src: url('Inter-ExtraBold.woff2?v=3.19') format('woff2'), + url('Inter-ExtraBold.woff?v=3.19') format('woff'); +} +@font-face { + font-family: 'Inter'; + font-style: italic; + font-weight: 800; + font-display: swap; + src: url('Inter-ExtraBoldItalic.woff2?v=3.19') format('woff2'), + url('Inter-ExtraBoldItalic.woff?v=3.19') format('woff'); +} + +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 900; + font-display: swap; + src: url('Inter-Black.woff2?v=3.19') format('woff2'), + url('Inter-Black.woff?v=3.19') format('woff'); +} +@font-face { + font-family: 'Inter'; + font-style: italic; + font-weight: 900; + font-display: swap; + src: url('Inter-BlackItalic.woff2?v=3.19') format('woff2'), + url('Inter-BlackItalic.woff?v=3.19') format('woff'); +} + +/* ------------------------------------------------------- +Variable font. +Usage: + + html { font-family: 'Inter', sans-serif; } + @supports (font-variation-settings: normal) { + html { font-family: 'Inter var', sans-serif; } + } +*/ +@font-face { + font-family: 'Inter var'; + font-weight: 100 900; + font-display: swap; + font-style: normal; + font-named-instance: 'Regular'; + src: url('Inter-roman.var.woff2?v=3.19') format('woff2'); +} +@font-face { + font-family: 'Inter var'; + font-weight: 100 900; + font-display: swap; + font-style: italic; + font-named-instance: 'Italic'; + src: url('Inter-italic.var.woff2?v=3.19') format('woff2'); +} + +/* -------------------------------------------------------------------------- +[EXPERIMENTAL] Multi-axis, single variable font. + +Slant axis is not yet widely supported (as of February 2019) and thus this +multi-axis single variable font is opt-in rather than the default. + +When using this, you will probably need to set font-variation-settings +explicitly, e.g. + + * { font-variation-settings: "slnt" 0deg } + .italic { font-variation-settings: "slnt" 10deg } + +*/ +@font-face { + font-family: 'Inter var experimental'; + font-weight: 100 900; + font-display: swap; + font-style: oblique 0deg 10deg; + src: url('Inter.var.woff2?v=3.19') format('woff2'); +} diff --git a/src-migrate/common/styles/globals.css b/src-migrate/common/styles/globals.css new file mode 100644 index 00000000..ea20b247 --- /dev/null +++ b/src-migrate/common/styles/globals.css @@ -0,0 +1,674 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +* { + -webkit-tap-highlight-color: transparent; +} + +html, +body { + @apply w-screen + text-body-2 + text-gray_r-12 + bg-gray_r-1 + overflow-x-clip; +} + +#__next main { + @apply min-h-screen; +} + +button { + @apply block; +} + +@layer base { + input[type='number']::-webkit-inner-spin-button, + input[type='number']::-webkit-outer-spin-button { + -webkit-appearance: none; + margin: 0; + } + + input[type='number'] { + -moz-appearance: textfield; + } +} + +@layer components { + .badge-red, + .badge-solid-red, + .badge-gray, + .badge-yellow, + .badge-blue, + .badge-green, + .badge-solid-green { + @apply text-[11px] + leading-none + font-medium + px-1 + py-1 + rounded + w-fit; + } + + .badge-red { + @apply bg-danger-100 + text-danger-600; + } + + .badge-solid-red { + @apply bg-danger-500 + text-white; + } + + .badge-gray { + @apply bg-gray_r-5 + text-gray_r-10; + } + + .badge-yellow { + @apply bg-warning-500 + text-warning-900; + } + + .badge-blue { + @apply bg-blue-200 + text-blue-600; + } + + .badge-green { + @apply bg-success-100 + text-success-600; + } + + .badge-solid-green { + @apply bg-success-500 + text-white; + } + + .form-label { + @apply font-medium + block; + } + + .form-input { + @apply p-3 + rounded + border + text-gray_r-12 + border-gray_r-7 + !bg-white + bg-transparent + w-full + leading-none + focus:outline-none + focus:border-warning-500 + disabled:bg-gray_r-5; + } + + .form-input[aria-invalid] { + @apply border-danger-500 + focus:border-danger-500; + } + + .form-input[type='file'] { + @apply py-2; + } + + .btn-yellow, + .btn-light, + .btn-red, + .btn-solid-red { + @apply block + w-fit + py-3 + px-6 + rounded + border + text-center + font-medium + ease-linear + duration-150; + } + + .btn-yellow { + @apply bg-warning-500 + border-warning-500 + hover:bg-warning-500/80 + disabled:text-gray_r-10 + disabled:bg-warning-200 + disabled:border-warning-200; + } + + .btn-red { + @apply bg-danger-100 + border-danger-300 + text-danger-500 + disabled:text-danger-400 + disabled:bg-danger-200; + } + + .btn-solid-red { + @apply bg-danger-500 + border-danger-500 + text-gray_r-1 + hover:bg-danger-500/80 + disabled:text-gray_r-1 + disabled:bg-danger-200 + disabled:border-danger-200; + } + + .btn-light { + @apply bg-gray_r-3 + border-gray_r-6 + disabled:text-gray_r-10 + disabled:bg-gray_r-6; + } + + .product-card { + @apply w-full + h-full + border + border-gray_r-3 + shadow + bg-white + rounded + relative + flex + flex-col; + } + + .product-card__image { + @apply w-full + h-[160px] + object-contain + object-center + border-b + border-gray_r-6; + } + + .product-card__content { + @apply p-2 + pb-3 + flex-1; + } + + .product-card__title { + @apply text-caption-1 + text-gray_r-12 + leading-5; + } + + .product-card__brand { + @apply text-caption-1 + mb-1 + block; + } + + .product__description { + @apply text-gray_r-12/90; + } + + .product__description br { + @apply block my-1; + } + + .product__description b { + @apply font-semibold; + } +} + +@layer utilities { + .wrap-line-ellipsis-1, + .wrap-line-ellipsis-2, + .wrap-line-ellipsis-3 { + display: -webkit-box; + -webkit-box-orient: vertical; + overflow: hidden; + text-overflow: ellipsis; + } + + .wrap-line-ellipsis-1 { + -webkit-line-clamp: 1; + } + + .wrap-line-ellipsis-2 { + -webkit-line-clamp: 2; + } + + .wrap-line-ellipsis-3 { + -webkit-line-clamp: 3; + } +} + +.menu-wrapper { + @apply fixed + top-0 + left-0 + bg-white + w-[80%] + h-full + z-[60] + overflow-y-auto + translate-x-[-100%] + ease-linear + duration-150; +} + +.menu-wrapper.active { + @apply translate-x-0; +} + +.overlay { + @apply fixed + top-0 + left-0 + w-full + h-full + z-[55] + bg-gray_r-12/40; +} + +.sticky-header { + @apply px-4 + py-3 + bg-gray_r-1/90 + backdrop-blur-lg + sticky + top-0 + border-b + border-gray_r-7 + z-50; +} + +.content-container { + @apply max-w-full + overflow-x-hidden; +} + +#indoteknik_toast { + @apply fixed + bottom-4 + translate-y-[200%] + left-[50%] + translate-x-[-50%] + z-[100] + flex + items-center + p-4 + mb-4 + w-[90%] + text-gray-500 + bg-white + border + border-gray-300 + rounded-lg + shadow + ease-linear + duration-300; +} + +#indoteknik_toast.active { + @apply translate-y-0; +} + +.category-menu { + @apply hidden; +} + +.swiper-slide { + @apply !h-auto; +} + +.lazy-load-image-background { + @apply !block + w-full; +} + +.swiper-pagination-bullet-active { + @apply !bg-danger-500; +} + +.pagination { + @apply flex + justify-center + gap-x-1; +} + +.pagination-item { + @apply p-1 + flex + justify-center + items-center + w-10 + rounded + ease-linear + duration-150 + border + border-gray_r-6 + bg-gray_r-3 + hover:bg-gray_r-5 + text-gray_r-12; +} + +.pagination-item--active { + @apply border-warning-500 + bg-warning-500 + hover:bg-warning-500; +} + +.pagination-dots { + @apply p-1 + flex + justify-center + items-end + w-10 + rounded + ease-linear + bg-gray_r-3 + text-caption-2; +} + +.idt-transition { + @apply transition-all + ease-out + duration-200; +} + +.form-select__placeholder { + @apply !text-gray_r-9; +} + +.form-select__control { + @apply !shadow-none + !border-gray_r-7; +} + +.form-select__control--menu-is-open { + @apply !border-warning-500; +} + +.table-specification { + @apply max-h-[500px] overflow-y-auto border border-gray_r-6; +} + +.table-specification > table { + @apply table-auto + border-collapse + w-full; +} + +.table-specification > table > thead > tr > th:last-child { + @apply w-3/12; +} + +.table-specification > table > thead { + @apply sticky top-0 border-b; +} + +.table-specification > table > thead > tr { + @apply bg-gray_r-1/80 backdrop-blur-lg; +} + +.table-specification th { + @apply font-semibold; +} + +.table-specification th, +.table-specification td { + @apply p-4 text-center; +} + +.table-specification > table > tbody > tr { + @apply odd:bg-gray_r-3 even:bg-gray_r-1; +} + +.table-cart, +.table-checkout { + @apply w-full + table-auto + border-collapse; +} + +.table-cart tr, +.table-checkout tr { + @apply border-y + border-gray_r-6 + first:border-t-0; +} + +.table-cart th, +.table-cart td, +.table-checkout th, +.table-checkout td { + @apply py-4 + px-3 + text-center + text-gray_r-12/90; +} + +.table-cart th, +.table-cart td { + @apply first:w-12; +} + +.table-cart th, +.table-checkout th { + @apply font-medium; +} + +.table-data { + @apply w-full + table-auto + border-collapse; +} + +.table-data thead tr { + @apply bg-gray_r-3; +} + +.table-data thead th { + @apply font-medium whitespace-nowrap; +} + +.table-data thead th, +.table-data tbody td { + @apply px-3 + py-4 + text-center; +} + +.table-data tbody td { + @apply text-gray_r-12/90; +} + +.table-data tbody tr { + @apply border-b + border-gray_r-6; +} + +.navbar-user-dropdown-button { + @apply flex-1 + flex + gap-x-2 + p-4 + items-center + bg-danger-500 + font-medium + !text-gray_r-1 + rounded-none + rounded-t-xl; +} + +.navbar-user-dropdown-button span { + @apply line-clamp-1; +} + +.navbar-user-dropdown-wrapper a, +.navbar-user-dropdown-wrapper button { + @apply text-gray_r-12/80 hover:bg-gray_r-5 font-medium py-2 px-4 w-full text-left; +} + +.navbar-user-dropdown { + @apply bg-white + border + border-gray_r-6 + py-2 + w-full + shadow; +} + +.category-mega-box-wrapper, +.navbar-user-dropdown-wrapper { + @apply absolute + opacity-0 + left-0 + top-[125%] + flex + w-full + z-10 + transition-all + ease-in + duration-200 + pointer-events-none + text-left; +} + +.category-mega-box-wrapper.show, +.navbar-user-dropdown-button:hover ~ .navbar-user-dropdown-wrapper, +.navbar-user-dropdown-wrapper:hover { + @apply top-[100%] + opacity-100 + pointer-events-auto; +} + +.category-mega-box { + @apply relative + py-2 + border + border-t-0 + bg-white + border-gray_r-6 + h-full + w-full; +} + +.category-mega-box > div { + @apply text-gray_r-12/80; +} + +.category-mega-box > div:hover .category-mega-box__parent { + @apply bg-gray_r-5; +} + +.category-mega-box > div:hover .category-mega-box__child-wrapper { + @apply opacity-100 + top-0 + pointer-events-auto; +} + +.category-mega-box .category-mega-box__parent { + @apply py-2.5 + px-4 + text-gray_r-12/80 + font-normal; +} + +.category-mega-box__child-wrapper { + @apply absolute + left-[100%] + top-12 + w-[40vw] + bg-gray_r-1/90 + backdrop-blur-md + border + border-gray_r-6 + p-6 + opacity-0 + h-full + transition-all + ease-in + duration-200 + pointer-events-none + z-50; +} + +.category-mega-box__child-one { + @apply text-gray_r-12/80 + hover:text-danger-500 + transition-colors + ease-linear + duration-100 + font-semibold; +} + +.category-mega-box__child-two { + @apply text-gray_r-12/80 + hover:text-danger-500 + transition-colors + ease-linear + duration-100 + font-normal; +} + +@keyframes page-loader { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} + +.page-loader { + animation-name: page-loader; + animation-duration: 1000ms; + animation-delay: 50ms; + animation-timing-function: ease-in-out; + animation-iteration-count: infinite; +} + +@keyframes shake { + 0% { + transform: translateX(0); + } + 10%, + 90% { + transform: translateX(-10px); + } + 20%, + 80% { + transform: translateX(10px); + } + 30%, + 50%, + 70% { + transform: translateX(-10px); + } + 40%, + 60% { + transform: translateX(10px); + } + 100% { + transform: translateX(0); + } +} + +.blink-color-flash-sale { + @apply text-body-1 md:text-title-sm; + transform: rotateY(180deg) rotateZ(120deg); + animation-name: blink-color-flash-sale; + animation-duration: 300ms; + animation-iteration-count: infinite; + animation-timing-function: linear; +} + +@keyframes blink-color-flash-sale { + from { + @apply text-danger-500; + } + to { + @apply text-warning-500; + } +} diff --git a/src-migrate/common/types/auth.ts b/src-migrate/common/types/auth.ts new file mode 100644 index 00000000..63fac6e0 --- /dev/null +++ b/src-migrate/common/types/auth.ts @@ -0,0 +1,32 @@ +import { OdooApiProps } from './odoo'; + +export type AuthProps = { + id: number; + parent_id: number; + parent_name: string; + partner_id: number; + name: string; + email: string; + phone: string; + mobile: string; + external: boolean; + company: boolean; + pricelist: string | null; + token: string; +}; + +export type AuthApiProps = OdooApiProps & { result: AuthProps }; + +export type RegisterProps = { + name: string; + email: string; + password: string; + company: string; +}; + +export type RegisterApiProps = OdooApiProps & { + result: { + register: boolean; + reason?: 'EMAIL_USED'; + }; +}; diff --git a/src-migrate/common/types/nav.ts b/src-migrate/common/types/nav.ts new file mode 100644 index 00000000..ba97b1bf --- /dev/null +++ b/src-migrate/common/types/nav.ts @@ -0,0 +1,4 @@ +export type SecondaryNavItemProps = { + label: string + href: string +} diff --git a/src-migrate/common/types/odoo.ts b/src-migrate/common/types/odoo.ts new file mode 100644 index 00000000..b34bc667 --- /dev/null +++ b/src-migrate/common/types/odoo.ts @@ -0,0 +1,6 @@ +export type OdooApiProps = { + status: { + code: number; + description: string; + }; +}; diff --git a/src-migrate/common/types/pageContent.ts b/src-migrate/common/types/pageContent.ts new file mode 100644 index 00000000..4361deb7 --- /dev/null +++ b/src-migrate/common/types/pageContent.ts @@ -0,0 +1,5 @@ +export type PageContentProps = { + id: number; + url_path: string; + content: string; +} | null; diff --git a/src-migrate/images/LOGO-INDOTEKNIK-GEAR-GREY.png b/src-migrate/images/LOGO-INDOTEKNIK-GEAR-GREY.png new file mode 100644 index 00000000..2ab07ff5 Binary files /dev/null and b/src-migrate/images/LOGO-INDOTEKNIK-GEAR-GREY.png differ diff --git a/src-migrate/images/LOGO-INDOTEKNIK-GEAR.png b/src-migrate/images/LOGO-INDOTEKNIK-GEAR.png new file mode 100644 index 00000000..89f749ec Binary files /dev/null and b/src-migrate/images/LOGO-INDOTEKNIK-GEAR.png differ diff --git a/src-migrate/images/logo.png b/src-migrate/images/logo.png new file mode 100644 index 00000000..dccc0cd6 Binary files /dev/null and b/src-migrate/images/logo.png differ diff --git a/src-migrate/modules/header/components/HeaderDesktop.tsx b/src-migrate/modules/header/components/HeaderDesktop.tsx new file mode 100644 index 00000000..3860bded --- /dev/null +++ b/src-migrate/modules/header/components/HeaderDesktop.tsx @@ -0,0 +1,82 @@ +import Logo from "~/images/logo.png"; +import { DocumentCheckIcon, HeartIcon } from "@heroicons/react/24/outline"; + +import Image from 'next/image' +import Link from 'next/link' + +// Components +import SearchBar from "./SearchBar"; + +// Constants +import { SECONDARY_MENU_ITEMS } from "~/common/constants/menu"; + +const LOGO_WIDTH = 210; +const LOGO_HEIGHT = LOGO_WIDTH / 3; + +const HeaderDesktop = () => { + return ( +
+ +
+ ) +} + +export default HeaderDesktop \ No newline at end of file diff --git a/src-migrate/modules/header/components/HeaderMobile.tsx b/src-migrate/modules/header/components/HeaderMobile.tsx new file mode 100644 index 00000000..626f30d7 --- /dev/null +++ b/src-migrate/modules/header/components/HeaderMobile.tsx @@ -0,0 +1,7 @@ +const HeaderMobile = () => { + return ( +
HeaderMobile
+ ) +} + +export default HeaderMobile \ No newline at end of file diff --git a/src-migrate/modules/header/components/SearchBar.tsx b/src-migrate/modules/header/components/SearchBar.tsx new file mode 100644 index 00000000..ec17abe8 --- /dev/null +++ b/src-migrate/modules/header/components/SearchBar.tsx @@ -0,0 +1,24 @@ + +import { MagnifyingGlassIcon } from '@heroicons/react/24/outline' + +const SearchBar = () => { + return ( +
+ + + + +
+ ) +} + +export default SearchBar \ No newline at end of file diff --git a/src-migrate/modules/header/index.tsx b/src-migrate/modules/header/index.tsx new file mode 100644 index 00000000..5c0e2933 --- /dev/null +++ b/src-migrate/modules/header/index.tsx @@ -0,0 +1,13 @@ +import React from 'react' +import { useWindowSize } from "usehooks-ts" + +import HeaderDesktop from './components/HeaderDesktop' +import HeaderMobile from './components/HeaderMobile' + +const Header = () => { + const { width } = useWindowSize() + + return width > 768 ? : +} + +export default Header \ No newline at end of file diff --git a/src-migrate/modules/home/components/Home.tsx b/src-migrate/modules/home/components/Home.tsx new file mode 100644 index 00000000..5d3bf104 --- /dev/null +++ b/src-migrate/modules/home/components/Home.tsx @@ -0,0 +1,15 @@ +import VerticalBanner from "./VerticalBanner" + +const Home = () => { + return ( + <> +
+
+ +
+
+ + ) +} + +export default Home \ No newline at end of file diff --git a/src-migrate/modules/home/components/VerticalBanner.tsx b/src-migrate/modules/home/components/VerticalBanner.tsx new file mode 100644 index 00000000..57328037 --- /dev/null +++ b/src-migrate/modules/home/components/VerticalBanner.tsx @@ -0,0 +1,7 @@ +const VerticalBanner = () => { + return ( +
VerticalBanner
+ ) +} + +export default VerticalBanner \ No newline at end of file diff --git a/src-migrate/modules/home/index.tsx b/src-migrate/modules/home/index.tsx new file mode 100644 index 00000000..6993d9cf --- /dev/null +++ b/src-migrate/modules/home/index.tsx @@ -0,0 +1,3 @@ +import Home from "./components/Home"; + +export default Home \ No newline at end of file diff --git a/src-migrate/modules/page-content/index.tsx b/src-migrate/modules/page-content/index.tsx new file mode 100644 index 00000000..cbd58633 --- /dev/null +++ b/src-migrate/modules/page-content/index.tsx @@ -0,0 +1,22 @@ +import { useQuery } from "react-query" +import PageContentSkeleton from "~/common/components/skeleton/PageContentSkeleton" +import { PageContentProps } from "~/common/types/pageContent" +import { getPageContent } from "~/services/pageContent" + +type Props = { + path: string +} + +const PageContent = ({ path }: Props) => { + const { data, isLoading } = useQuery(`page-content:${path}`, async () => await getPageContent({ path })) + + if (isLoading) { + return + } + + return ( +
+ ) +} + +export default PageContent \ No newline at end of file diff --git a/src-migrate/modules/register/components/Form.tsx b/src-migrate/modules/register/components/Form.tsx new file mode 100644 index 00000000..ac194b46 --- /dev/null +++ b/src-migrate/modules/register/components/Form.tsx @@ -0,0 +1,117 @@ +import { ChangeEvent } from "react"; +import { useMutation } from "react-query"; +import { useRegisterStore } from "~/common/stores/useRegisterStore"; +import { RegisterProps } from "~/common/types/auth"; +import { registerUser } from "~/services/auth"; + +const Form = () => { + const { form, isValid, isCheckedTNC, updateForm, toggleCheckTNC, openTNC } = useRegisterStore() + + const handleInputChange = (event: ChangeEvent) => { + const { name, value } = event.target; + updateForm(name, value) + } + + const mutation = useMutation({ + mutationFn: (data: RegisterProps) => registerUser(data) + }) + + const handleSubmit = async (e: ChangeEvent) => { + e.preventDefault() + + const response = await mutation.mutateAsync(form) + console.log(response); + + } + + return ( +
+
+ + + +
+ +
+ + + +
+ +
+ + + +
+ +
+ + +
+ +
+ + + {' '} + + syarat dan ketentuan + + +
+ + +
+ ) +} + +export default Form \ No newline at end of file diff --git a/src-migrate/modules/register/components/Register.tsx b/src-migrate/modules/register/components/Register.tsx new file mode 100644 index 00000000..e3e29b9f --- /dev/null +++ b/src-migrate/modules/register/components/Register.tsx @@ -0,0 +1,39 @@ +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 new file mode 100644 index 00000000..304ffd69 --- /dev/null +++ b/src-migrate/modules/register/components/TermCondition.tsx @@ -0,0 +1,16 @@ +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() + + return ( + + + + ) +} + +export default TermCondition \ No newline at end of file diff --git a/src-migrate/modules/register/index.tsx b/src-migrate/modules/register/index.tsx new file mode 100644 index 00000000..ba5efa3a --- /dev/null +++ b/src-migrate/modules/register/index.tsx @@ -0,0 +1,3 @@ +import Register from "./components/Register"; + +export default Register \ No newline at end of file diff --git a/src-migrate/pages/_app.tsx b/src-migrate/pages/_app.tsx new file mode 100644 index 00000000..2dc82559 --- /dev/null +++ b/src-migrate/pages/_app.tsx @@ -0,0 +1,7 @@ +import '~/common/styles/fonts/Inter/inter.css' +import '~/common/styles/globals.css' +import type { AppProps } from "next/app" + +export default function MyApp({ Component, pageProps }: AppProps) { + return +} \ No newline at end of file diff --git a/src-migrate/pages/register.tsx b/src-migrate/pages/register.tsx new file mode 100644 index 00000000..4ba72cbc --- /dev/null +++ b/src-migrate/pages/register.tsx @@ -0,0 +1,13 @@ + +import BasicLayout from "@/core/components/layouts/BasicLayout" +import Register from "~/modules/register" + +const RegisterPage = () => { + return ( + + + + ) +} + +export default RegisterPage \ No newline at end of file diff --git a/src-migrate/services/auth.ts b/src-migrate/services/auth.ts new file mode 100644 index 00000000..f2fd7761 --- /dev/null +++ b/src-migrate/services/auth.ts @@ -0,0 +1,10 @@ +import odooApi from '~/common/libs/odooApi'; +import { RegisterApiProps, RegisterProps } from '~/common/types/auth'; + +export const registerUser = async ( + data: RegisterProps +): Promise => { + const response = await odooApi('POST', '/api/v1/user/register', data); + + return response; +}; diff --git a/src-migrate/services/banner.ts b/src-migrate/services/banner.ts new file mode 100644 index 00000000..e69de29b diff --git a/src-migrate/services/pageContent.ts b/src-migrate/services/pageContent.ts new file mode 100644 index 00000000..24f2c2f0 --- /dev/null +++ b/src-migrate/services/pageContent.ts @@ -0,0 +1,14 @@ +import odooApi from '~/common/libs/odooApi'; + +export const getPageContent = async ({ path }: { path: string }) => { + const params = new URLSearchParams({ + url_path: path, + }); + + const pageContent = await odooApi( + 'GET', + `/api/v1/page-content?${params.toString()}` + ); + + return pageContent; +}; -- cgit v1.2.3 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 --- 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 ++++++++- 15 files changed, 336 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 (limited to 'src-migrate') 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 From c82110f7d3a2f85de99045fde7b579e369f15b2c Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Sat, 28 Oct 2023 09:05:00 +0700 Subject: Update authentication feature --- src-migrate/common/components/elements/Modal.tsx | 4 +- src-migrate/common/types/auth.ts | 11 +- .../account-activation/components/FormEmail.tsx | 91 ++++++++++++++++- .../account-activation/components/FormOTP.tsx | 112 ++++++++++++++++++++- .../account-activation/components/FormToken.tsx | 24 ++--- src-migrate/modules/account-activation/index.tsx | 4 + src-migrate/modules/register/components/Form.tsx | 35 ++++++- src-migrate/modules/register/index.tsx | 7 ++ src-migrate/services/auth.ts | 14 +++ 9 files changed, 278 insertions(+), 24 deletions(-) (limited to 'src-migrate') diff --git a/src-migrate/common/components/elements/Modal.tsx b/src-migrate/common/components/elements/Modal.tsx index 91421251..8e488a3a 100644 --- a/src-migrate/common/components/elements/Modal.tsx +++ b/src-migrate/common/components/elements/Modal.tsx @@ -1,5 +1,6 @@ import { XMarkIcon } from "@heroicons/react/24/outline"; import { AnimatePresence, motion } from "framer-motion" +import { useRouter } from "next/router"; import { useEffect, useState } from "react"; import ReactDOM from "react-dom"; import { useWindowSize } from "usehooks-ts"; @@ -23,6 +24,7 @@ const Modal = ({ className, mode }: Props) => { + const router = useRouter() const { width } = useWindowSize() const [rendered, setRendered] = useState(false) @@ -49,7 +51,7 @@ const Modal = ({ } return rendered && ReactDOM.createPortal( - + {active && ( { + const router = useRouter() + const { query } = router + const [active, setActive] = useState(false) + const [isBtnDisabled, setIsBtnDisabled] = useState(true) + const [email, setEmail] = useState('') + + const { form } = useRegisterStore() + + useEffect(() => { + if (form) setEmail(form?.email) + }, [form]) + + useEffect(() => { + setIsBtnDisabled(email === '') + }, [email]) + + useEffect(() => { + setActive(query?.activation === 'email') + }, [query.activation]) + + const mutation = useMutation({ + mutationFn: (data: ActivationReqProps) => activationReq(data) + }) + + const handleSubmit = (e: ChangeEvent) => { + e.preventDefault() + mutation.data = undefined + mutation.mutate({ email }) + } + + useEffect(() => { + if (mutation.data?.activation_request === true) { + const urlParams = new URLSearchParams({ + activation: 'otp', + email + }) + router.push(`${router.route}?${urlParams}`) + } + }, [mutation.data?.activation_request, router, email]) + return ( -
FormEmail
+ router.push(router.route)} title="Aktivasi Akun"> + + { + mutation.data !== undefined && + !mutation.data?.activation_request && + ( + + + {mutation.data?.reason === 'ACTIVE' && 'Akun sudah aktif.'} + {mutation.data?.reason === 'NOT_FOUND' && 'Akun tidak ditemukan.'} + + {mutation.data?.reason === 'ACTIVE' && ( + Klik untuk masuk akun anda + )} + + {mutation.data?.reason === 'NOT_FOUND' && ( + Klik untuk daftar akun baru + )} + + ) + } + + setEmail(e.target.value)} + autoFocus + /> + + + ) } diff --git a/src-migrate/modules/account-activation/components/FormOTP.tsx b/src-migrate/modules/account-activation/components/FormOTP.tsx index 24e9e7f6..e38fa8fe 100644 --- a/src-migrate/modules/account-activation/components/FormOTP.tsx +++ b/src-migrate/modules/account-activation/components/FormOTP.tsx @@ -1,23 +1,127 @@ -import { HStack, PinInput, PinInputField } from "@chakra-ui/react" +import { HStack, PinInput, PinInputField, Spinner } from "@chakra-ui/react" +import Link from "next/link" +import { useRouter } from "next/router" +import { useEffect, useState } from "react" +import { useMutation } from "react-query" +import { useCountdown } from "usehooks-ts" import Modal from '~/common/components/elements/Modal' +import { ActivationOtpProps, ActivationReqProps } from "~/common/types/auth" +import { activationReq, activationUserOTP } from "~/services/auth" const FormOTP = () => { + const router = useRouter() + const { query } = router + const [active, setActive] = useState(false) + const [email, setEmail] = useState('') + const [otp, setOtp] = useState('') + const [count, { startCountdown, resetCountdown }] = useCountdown({ + countStart: 60 * 3, + intervalMs: 1000, + }) + + useEffect(() => { + setEmail((query?.email || '') as string) + }, [query?.email]) + + useEffect(() => { + setActive(query?.activation === 'otp') + }, [query.activation]) + + useEffect(() => { + !!active && startCountdown() + }, [active, startCountdown]) + + const mutationActivationReq = useMutation({ + mutationFn: (data: ActivationReqProps) => activationReq(data), + onSuccess: () => { + resetCountdown() + startCountdown() + } + }) + + const mutationActivation = useMutation({ + mutationFn: (data: ActivationOtpProps) => activationUserOTP(data) + }) + + useEffect(() => { + if (otp.length === 4 && !mutationActivation.data?.activation) { + mutationActivation.mutate({ email, otp }) + } + //eslint-disable-next-line + }, [otp]) + + // TODO: Save user to local storage + return ( - +
Masukan Kode OTP
- + setOtp(val)}> -
Kode OTP dikirimkan ke email anda
+
+ {mutationActivation.isLoading && } + + {!mutationActivation.isLoading && + mutationActivation.data?.reason === 'INVALID_OTP' && + ( + Mohon maaf kode OTP yand anda masukan salah + ) + } + + {mutationActivation.data?.activation && ( + Akun anda berhasil diaktifkan, selamat berbelanja di Indoteknik. + )} +
+ + {!mutationActivation.data?.activation && ( +
+ Kode verifikasi telah dikirimkan ke alamat email {email}. Silakan periksa kotak masuk atau folder spam. +
+ )} + +
+ {!mutationActivation.data?.activation && ( + <> + {count > 0 && timeFormat(count)} + + {!mutationActivation.data?.activation && count == 0 && ( + + )} + + )} + + {mutationActivation.data?.activation && ( + Kembali ke halaman utama + )} + + +
) } +const timeFormat = (time: number): string => { + const minute = Math.floor(time / 60); + const second = time % 60; + + const minuteFormat = minute < 10 ? `0${minute}` : `${minute}` + const secondFormat = second < 10 ? `0${second}` : `${second}` + + return `${minuteFormat}:${secondFormat}` +} + 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 index 041fbae8..6af24413 100644 --- a/src-migrate/modules/account-activation/components/FormToken.tsx +++ b/src-migrate/modules/account-activation/components/FormToken.tsx @@ -8,22 +8,12 @@ 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(() => { @@ -34,19 +24,21 @@ const FormToken = () => { //eslint-disable-next-line }, [query.activation, query.token]) + // TODO: Save user to local storage + return (
- {status === 'loading' && ( + {mutation.isLoading && ( <>
Mohon tunggu sedang memverifikasi akun
)} - {status !== 'loading' && ( + {!mutation.isLoading && ( { > - Aktivasi akun {status === 'success' ? 'berhasil' : 'gagal'} + Aktivasi akun {mutation.data?.activation ? 'berhasil' : 'gagal'} - {status === 'success' && ( + {mutation.data?.activation && ( <> Akun anda berhasil diaktifkan, selamat berbelanja di Indoteknik. Kembali ke halaman utama )} - {status === 'error' && mutation.data?.reason === 'INVALID_TOKEN' && ( + {!mutation.data?.activation && mutation.data?.reason === 'INVALID_TOKEN' && ( <> Token sudah kadaluwarsa, silahkan coba kembali. Aktivasi Akun diff --git a/src-migrate/modules/account-activation/index.tsx b/src-migrate/modules/account-activation/index.tsx index edcc6652..97c96953 100644 --- a/src-migrate/modules/account-activation/index.tsx +++ b/src-migrate/modules/account-activation/index.tsx @@ -1,10 +1,14 @@ import { useRouter } from "next/router" import FormToken from "./components/FormToken" +import FormEmail from "./components/FormEmail" +import FormOTP from "./components/FormOTP" const AccountActivation = () => { return ( <> + + ) } diff --git a/src-migrate/modules/register/components/Form.tsx b/src-migrate/modules/register/components/Form.tsx index fc1567ab..3227a549 100644 --- a/src-migrate/modules/register/components/Form.tsx +++ b/src-migrate/modules/register/components/Form.tsx @@ -1,10 +1,13 @@ -import { ChangeEvent } from "react"; +import { ChangeEvent, useEffect } from "react"; 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"; +import { useRouter } from "next/router"; +import { UseToastOptions, useToast } from "@chakra-ui/react"; +import Link from "next/link"; const Form = () => { const { @@ -14,6 +17,8 @@ const Form = () => { isValidCaptcha, updateForm, } = useRegisterStore() + const router = useRouter() + const toast = useToast() const handleInputChange = (event: ChangeEvent) => { const { name, value } = event.target; @@ -30,7 +35,35 @@ const Form = () => { const response = await mutation.mutateAsync(form) if (response?.register === true) { + const urlParams = new URLSearchParams({ + activation: 'otp', + email: form.email + }) + router.push(`${router.route}?${urlParams}`) + } + + const toastProps: UseToastOptions = { + duration: 5000, + isClosable: true + } + switch (response?.reason) { + case 'EMAIL_USED': + toast({ + ...toastProps, + title: 'Email sudah digunakan', + status: 'warning' + }) + break; + case 'NOT_ACTIVE': + const activationUrl = `${router.route}?activation=email` + toast({ + ...toastProps, + title: 'Akun belum aktif', + description: <>Akun sudah terdaftar namun belum aktif. Klik untuk aktivasi akun, + status: 'warning' + }) + break } } diff --git a/src-migrate/modules/register/index.tsx b/src-migrate/modules/register/index.tsx index 6325ee09..00931284 100644 --- a/src-migrate/modules/register/index.tsx +++ b/src-migrate/modules/register/index.tsx @@ -32,6 +32,13 @@ const Register = () => { Masuk
+ +
+ Akun anda belum aktif?{' '} + + Aktivasi + +
diff --git a/src-migrate/services/auth.ts b/src-migrate/services/auth.ts index feaabec8..a5d02754 100644 --- a/src-migrate/services/auth.ts +++ b/src-migrate/services/auth.ts @@ -6,6 +6,8 @@ import { ActivationTokenResApiProps, ActivationOtpProps, ActivationOtpResApiProps, + ActivationReqProps, + ActivationReqResApiProps, } from '~/common/types/auth'; const BASE_PATH = '/api/v1/user'; @@ -37,3 +39,15 @@ export const activationUserOTP = async ( return response; }; + +export const activationReq = async ( + params: ActivationReqProps +): Promise => { + const response = await odooApi( + 'POST', + `${BASE_PATH}/activation-request`, + params + ); + + return response; +}; -- cgit v1.2.3 From 1602cff06e13bb03e5c48e8369abf5c803426e4d Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Mon, 30 Oct 2023 09:53:40 +0700 Subject: Add set auth on authenticated --- src-migrate/modules/account-activation/components/FormOTP.tsx | 7 ++++++- src-migrate/modules/account-activation/components/FormToken.tsx | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'src-migrate') diff --git a/src-migrate/modules/account-activation/components/FormOTP.tsx b/src-migrate/modules/account-activation/components/FormOTP.tsx index e38fa8fe..a4775d61 100644 --- a/src-migrate/modules/account-activation/components/FormOTP.tsx +++ b/src-migrate/modules/account-activation/components/FormOTP.tsx @@ -5,6 +5,7 @@ import { useEffect, useState } from "react" import { useMutation } from "react-query" import { useCountdown } from "usehooks-ts" import Modal from '~/common/components/elements/Modal' +import { setAuth } from "~/common/libs/auth" import { ActivationOtpProps, ActivationReqProps } from "~/common/types/auth" import { activationReq, activationUserOTP } from "~/services/auth" @@ -50,7 +51,11 @@ const FormOTP = () => { //eslint-disable-next-line }, [otp]) - // TODO: Save user to local storage + useEffect(() => { + if (mutationActivation.data?.user) { + setAuth(mutationActivation.data.user) + } + }, [mutationActivation.data]) return ( diff --git a/src-migrate/modules/account-activation/components/FormToken.tsx b/src-migrate/modules/account-activation/components/FormToken.tsx index 6af24413..a1525fe6 100644 --- a/src-migrate/modules/account-activation/components/FormToken.tsx +++ b/src-migrate/modules/account-activation/components/FormToken.tsx @@ -7,6 +7,7 @@ import { useMutation } from "react-query" import Modal from "~/common/components/elements/Modal" import { ActivationTokenProps } from "~/common/types/auth" import { activationUserToken } from "~/services/auth" +import { setAuth } from "~/common/libs/auth" const FormToken = () => { const { query } = useRouter() @@ -24,7 +25,11 @@ const FormToken = () => { //eslint-disable-next-line }, [query.activation, query.token]) - // TODO: Save user to local storage + useEffect(() => { + if (mutation.data?.user) { + setAuth(mutation.data.user) + } + }, [mutation.data]) return ( -- cgit v1.2.3 From 6b221cccd58710682c99db7afbc29322da042880 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Tue, 31 Oct 2023 10:44:25 +0700 Subject: - Add redirect after activation - Add register form validation --- src-migrate/common/stores/useRegisterStore.ts | 28 +++++++++++++++++++++- src-migrate/common/types/auth.ts | 10 +++----- src-migrate/common/validations/auth.ts | 13 ++++++++++ .../account-activation/components/FormEmail.tsx | 3 ++- .../account-activation/components/FormOTP.tsx | 12 ++-------- .../account-activation/components/FormToken.tsx | 19 ++++++--------- src-migrate/modules/register/components/Form.tsx | 19 ++++++++++++++- 7 files changed, 72 insertions(+), 32 deletions(-) create mode 100644 src-migrate/common/validations/auth.ts (limited to 'src-migrate') diff --git a/src-migrate/common/stores/useRegisterStore.ts b/src-migrate/common/stores/useRegisterStore.ts index 725bbfda..d6c7db2a 100644 --- a/src-migrate/common/stores/useRegisterStore.ts +++ b/src-migrate/common/stores/useRegisterStore.ts @@ -1,8 +1,13 @@ import { create } from 'zustand'; import { RegisterProps } from '../types/auth'; +import { registerSchema } from '../validations/auth'; +import { ValidationError } from 'yup'; type State = { form: RegisterProps; + errors: { + [key in keyof RegisterProps]?: string; + }; isValid: boolean; isCheckedTNC: boolean; isOpenTNC: boolean; @@ -15,9 +20,10 @@ type Action = { toggleCheckTNC: () => void; openTNC: () => void; closeTNC: () => void; + validate: () => void; }; -export const useRegisterStore = create((set) => ({ +export const useRegisterStore = create((set, get) => ({ form: { company: '', name: '', @@ -25,6 +31,26 @@ export const useRegisterStore = create((set) => ({ password: '', phone: '', }, + errors: {}, + validate: () => + registerSchema + .validate(get().form, { abortEarly: false }) + .then(() => { + set({ + errors: {}, + isValid: false, + }); + }) + .catch((err: ValidationError) => { + const validationErrors: State['errors'] = {}; + err.inner.forEach( + (e) => (validationErrors[e.path as keyof RegisterProps] = e.message) + ); + set({ + errors: validationErrors, + isValid: false, + }); + }), isValid: false, isCheckedTNC: false, isOpenTNC: false, diff --git a/src-migrate/common/types/auth.ts b/src-migrate/common/types/auth.ts index ca7b562a..5909584a 100644 --- a/src-migrate/common/types/auth.ts +++ b/src-migrate/common/types/auth.ts @@ -1,4 +1,6 @@ +import { registerSchema } from '../validations/auth'; import { OdooApiProps } from './odoo'; +import * as yup from 'yup'; export type AuthProps = { id: number; @@ -17,13 +19,7 @@ export type AuthProps = { export type AuthApiProps = OdooApiProps & { result: AuthProps }; -export type RegisterProps = { - name: string; - email: string; - password: string; - company: string; - phone: string; -}; +export type RegisterProps = yup.InferType; export type RegisterResApiProps = { register: boolean; diff --git a/src-migrate/common/validations/auth.ts b/src-migrate/common/validations/auth.ts new file mode 100644 index 00000000..94b40849 --- /dev/null +++ b/src-migrate/common/validations/auth.ts @@ -0,0 +1,13 @@ +import { object, string } from 'yup'; + +export const registerSchema = object({ + name: string().required('Nama harus diisi'), + email: string() + .email('Email harus menggunakan format example@mail.com') + .required('Email harus diisi'), + password: string() + .min(6, 'Password minimal 6 karakter') + .required('Password harus diisi'), + company: string().optional(), + phone: string().required('Nomor telepon harus diisi'), +}); diff --git a/src-migrate/modules/account-activation/components/FormEmail.tsx b/src-migrate/modules/account-activation/components/FormEmail.tsx index cd917bc9..ec300ba4 100644 --- a/src-migrate/modules/account-activation/components/FormEmail.tsx +++ b/src-migrate/modules/account-activation/components/FormEmail.tsx @@ -43,7 +43,8 @@ const FormEmail = () => { if (mutation.data?.activation_request === true) { const urlParams = new URLSearchParams({ activation: 'otp', - email + email, + redirect: (router.query?.redirect || '/') as string }) router.push(`${router.route}?${urlParams}`) } diff --git a/src-migrate/modules/account-activation/components/FormOTP.tsx b/src-migrate/modules/account-activation/components/FormOTP.tsx index a4775d61..47c69329 100644 --- a/src-migrate/modules/account-activation/components/FormOTP.tsx +++ b/src-migrate/modules/account-activation/components/FormOTP.tsx @@ -54,8 +54,9 @@ const FormOTP = () => { useEffect(() => { if (mutationActivation.data?.user) { setAuth(mutationActivation.data.user) + router.push((query?.redirect || '/') as string) } - }, [mutationActivation.data]) + }, [mutationActivation.data, router, query.redirect]) return ( @@ -78,10 +79,6 @@ const FormOTP = () => { Mohon maaf kode OTP yand anda masukan salah ) } - - {mutationActivation.data?.activation && ( - Akun anda berhasil diaktifkan, selamat berbelanja di Indoteknik. - )}
{!mutationActivation.data?.activation && ( @@ -108,11 +105,6 @@ const FormOTP = () => { )} - {mutationActivation.data?.activation && ( - Kembali ke halaman utama - )} - -
diff --git a/src-migrate/modules/account-activation/components/FormToken.tsx b/src-migrate/modules/account-activation/components/FormToken.tsx index a1525fe6..b68b244f 100644 --- a/src-migrate/modules/account-activation/components/FormToken.tsx +++ b/src-migrate/modules/account-activation/components/FormToken.tsx @@ -10,7 +10,8 @@ import { activationUserToken } from "~/services/auth" import { setAuth } from "~/common/libs/auth" const FormToken = () => { - const { query } = useRouter() + const router = useRouter() + const { query } = router const [active, setActive] = useState(false) const mutation = useMutation({ @@ -28,8 +29,9 @@ const FormToken = () => { useEffect(() => { if (mutation.data?.user) { setAuth(mutation.data.user) + router.push((query?.redirect || '/') as string) } - }, [mutation.data]) + }, [mutation.data, router, query.redirect]) return ( @@ -41,7 +43,7 @@ const FormToken = () => { )} - {!mutation.isLoading && ( + {!mutation.isLoading && !mutation.data?.activation && ( { > - Aktivasi akun {mutation.data?.activation ? 'berhasil' : 'gagal'} + Aktivasi akun gagal - {mutation.data?.activation && ( - <> - Akun anda berhasil diaktifkan, selamat berbelanja di Indoteknik. - Kembali ke halaman utama - - )} - - {!mutation.data?.activation && mutation.data?.reason === 'INVALID_TOKEN' && ( + {mutation.data?.reason === 'INVALID_TOKEN' && ( <> Token sudah kadaluwarsa, silahkan coba kembali. Aktivasi Akun diff --git a/src-migrate/modules/register/components/Form.tsx b/src-migrate/modules/register/components/Form.tsx index 3227a549..5b51d6f4 100644 --- a/src-migrate/modules/register/components/Form.tsx +++ b/src-migrate/modules/register/components/Form.tsx @@ -8,6 +8,7 @@ import FormCaptcha from "./FormCaptcha"; import { useRouter } from "next/router"; import { UseToastOptions, useToast } from "@chakra-ui/react"; import Link from "next/link"; +import { registerSchema } from "~/common/validations/auth"; const Form = () => { const { @@ -15,7 +16,9 @@ const Form = () => { isValid, isCheckedTNC, isValidCaptcha, + errors, updateForm, + validate, } = useRegisterStore() const router = useRouter() const toast = useToast() @@ -23,6 +26,7 @@ const Form = () => { const handleInputChange = (event: ChangeEvent) => { const { name, value } = event.target; updateForm(name, value) + validate() } const mutation = useMutation({ @@ -37,7 +41,8 @@ const Form = () => { if (response?.register === true) { const urlParams = new URLSearchParams({ activation: 'otp', - email: form.email + email: form.email, + redirect: (router.query?.next || '/') as string }) router.push(`${router.route}?${urlParams}`) } @@ -97,7 +102,10 @@ const Form = () => { placeholder='Masukan nama lengkap anda' value={form.name} onChange={handleInputChange} + aria-invalid={!!errors.name} /> + + {!!errors.name && {errors.name}}
@@ -111,7 +119,10 @@ const Form = () => { placeholder='08xxxxxxxx' value={form.phone} onChange={handleInputChange} + aria-invalid={!!errors.phone} /> + + {!!errors.phone && {errors.phone}}
@@ -126,7 +137,10 @@ const Form = () => { value={form.email} onChange={handleInputChange} autoComplete="username" + aria-invalid={!!errors.email} /> + + {!!errors.email && {errors.email}}
@@ -140,7 +154,10 @@ const Form = () => { value={form.password} onChange={handleInputChange} autoComplete="current-password" + aria-invalid={!!errors.password} /> + + {!!errors.password && {errors.password}}
-- cgit v1.2.3 From 4bed066c03dc55a8f811ff2e23e019d8adc64495 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Fri, 10 Nov 2023 14:47:52 +0700 Subject: Refactor register form --- src-migrate/common/stores/useRegisterStore.ts | 45 ++++++------------------ src-migrate/modules/register/components/Form.tsx | 10 ++++-- 2 files changed, 17 insertions(+), 38 deletions(-) (limited to 'src-migrate') diff --git a/src-migrate/common/stores/useRegisterStore.ts b/src-migrate/common/stores/useRegisterStore.ts index d6c7db2a..ab2d7410 100644 --- a/src-migrate/common/stores/useRegisterStore.ts +++ b/src-migrate/common/stores/useRegisterStore.ts @@ -8,7 +8,6 @@ type State = { errors: { [key in keyof RegisterProps]?: string; }; - isValid: boolean; isCheckedTNC: boolean; isOpenTNC: boolean; isValidCaptcha: boolean; @@ -31,53 +30,29 @@ export const useRegisterStore = create((set, get) => ({ password: '', phone: '', }, + updateForm: (name, value) => + set((state) => ({ form: { ...state.form, [name]: value } })), + errors: {}, validate: () => registerSchema .validate(get().form, { abortEarly: false }) - .then(() => { - set({ - errors: {}, - isValid: false, - }); - }) + .then(() => set({ errors: {} })) .catch((err: ValidationError) => { const validationErrors: State['errors'] = {}; err.inner.forEach( (e) => (validationErrors[e.path as keyof RegisterProps] = e.message) ); - set({ - errors: validationErrors, - isValid: false, - }); + set({ errors: validationErrors }); }), - isValid: false, - isCheckedTNC: false, - isOpenTNC: false, - isValidCaptcha: false, - updateForm: (name, value) => - set((state) => { - const updatedForm = { ...state.form, [name]: value }; - - const fieldKeys = Object.keys( - updatedForm - ) as (keyof typeof updatedForm)[]; - - const allFieldsValid = fieldKeys.every((key) => { - const value = updatedForm[key]; - if (key === 'company') return true; - - return value !== ''; - }); - - return { - form: updatedForm, - isValid: allFieldsValid, - }; - }), + isCheckedTNC: false, toggleCheckTNC: () => set((state) => ({ isCheckedTNC: !state.isCheckedTNC })), + + isOpenTNC: false, openTNC: () => set(() => ({ isOpenTNC: true })), closeTNC: () => set(() => ({ isOpenTNC: false })), + + isValidCaptcha: false, updateValidCaptcha: (value) => set(() => ({ isValidCaptcha: value })), })); diff --git a/src-migrate/modules/register/components/Form.tsx b/src-migrate/modules/register/components/Form.tsx index 5b51d6f4..ddc3397f 100644 --- a/src-migrate/modules/register/components/Form.tsx +++ b/src-migrate/modules/register/components/Form.tsx @@ -1,4 +1,4 @@ -import { ChangeEvent, useEffect } from "react"; +import { ChangeEvent, useEffect, useMemo } from "react"; import { useMutation } from "react-query"; import { useRegisterStore } from "~/common/stores/useRegisterStore"; import { RegisterProps } from "~/common/types/auth"; @@ -13,13 +13,17 @@ import { registerSchema } from "~/common/validations/auth"; const Form = () => { const { form, - isValid, isCheckedTNC, isValidCaptcha, errors, updateForm, validate, } = useRegisterStore() + + const isFormValid = useMemo(() => { + return Object.keys(errors).length === 0 + }, [errors]) + const router = useRouter() const toast = useToast() @@ -167,7 +171,7 @@ const Form = () => { -- cgit v1.2.3 From 5a9210e560ca47d8f4d8c43b06fc93d1d064a6f6 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Fri, 10 Nov 2023 16:23:33 +0700 Subject: Add close on modal otp activation, change validation register schema, fix term and condition checkbox --- src-migrate/common/stores/useRegisterStore.ts | 26 ++++++++++++---------- src-migrate/common/types/auth.ts | 4 ++-- src-migrate/common/validations/auth.ts | 26 +++++++++++++--------- .../account-activation/components/FormOTP.tsx | 5 ++--- src-migrate/modules/register/components/Form.tsx | 7 ++---- .../modules/register/components/TermCondition.tsx | 2 +- 6 files changed, 36 insertions(+), 34 deletions(-) (limited to 'src-migrate') diff --git a/src-migrate/common/stores/useRegisterStore.ts b/src-migrate/common/stores/useRegisterStore.ts index ab2d7410..90ce8a2b 100644 --- a/src-migrate/common/stores/useRegisterStore.ts +++ b/src-migrate/common/stores/useRegisterStore.ts @@ -1,7 +1,7 @@ import { create } from 'zustand'; import { RegisterProps } from '../types/auth'; import { registerSchema } from '../validations/auth'; -import { ValidationError } from 'yup'; +import { ZodError } from 'zod'; type State = { form: RegisterProps; @@ -34,18 +34,20 @@ export const useRegisterStore = create((set, get) => ({ set((state) => ({ form: { ...state.form, [name]: value } })), errors: {}, - validate: () => - registerSchema - .validate(get().form, { abortEarly: false }) - .then(() => set({ errors: {} })) - .catch((err: ValidationError) => { - const validationErrors: State['errors'] = {}; - err.inner.forEach( - (e) => (validationErrors[e.path as keyof RegisterProps] = e.message) + validate: () => { + try { + registerSchema.parse(get().form); + set({ errors: {} }); + } catch (error) { + if (error instanceof ZodError) { + const errors: State['errors'] = {}; + error.errors.forEach( + (e) => (errors[e.path[0] as keyof RegisterProps] = e.message) ); - set({ errors: validationErrors }); - }), - + set({ errors }); + } + } + }, isCheckedTNC: false, toggleCheckTNC: () => set((state) => ({ isCheckedTNC: !state.isCheckedTNC })), diff --git a/src-migrate/common/types/auth.ts b/src-migrate/common/types/auth.ts index 5909584a..65fd06c7 100644 --- a/src-migrate/common/types/auth.ts +++ b/src-migrate/common/types/auth.ts @@ -1,6 +1,6 @@ import { registerSchema } from '../validations/auth'; import { OdooApiProps } from './odoo'; -import * as yup from 'yup'; +import { z } from 'zod'; export type AuthProps = { id: number; @@ -19,7 +19,7 @@ export type AuthProps = { export type AuthApiProps = OdooApiProps & { result: AuthProps }; -export type RegisterProps = yup.InferType; +export type RegisterProps = z.infer; export type RegisterResApiProps = { register: boolean; diff --git a/src-migrate/common/validations/auth.ts b/src-migrate/common/validations/auth.ts index 94b40849..78fc5e71 100644 --- a/src-migrate/common/validations/auth.ts +++ b/src-migrate/common/validations/auth.ts @@ -1,13 +1,17 @@ -import { object, string } from 'yup'; +import { z } from 'zod'; -export const registerSchema = object({ - name: string().required('Nama harus diisi'), - email: string() - .email('Email harus menggunakan format example@mail.com') - .required('Email harus diisi'), - password: string() - .min(6, 'Password minimal 6 karakter') - .required('Password harus diisi'), - company: string().optional(), - phone: string().required('Nomor telepon harus diisi'), +export const registerSchema = z.object({ + name: z.string().min(1, { message: 'Nama harus diisi' }), + email: z + .string() + .min(1, { message: 'Email harus diisi' }) + .email({ message: 'Email harus menggunakan format example@mail.com' }), + password: z.string().min(6, { message: 'Password minimal 6 karakter' }), + company: z.string().optional(), + phone: z + .string() + .min(1, { message: 'Nomor telepon harus diisi' }) + .refine((val) => /^\d{10,12}$/.test(val), { + message: 'Format nomor telepon tidak valid, contoh: 081234567890', + }), }); diff --git a/src-migrate/modules/account-activation/components/FormOTP.tsx b/src-migrate/modules/account-activation/components/FormOTP.tsx index 47c69329..9bc0fb23 100644 --- a/src-migrate/modules/account-activation/components/FormOTP.tsx +++ b/src-migrate/modules/account-activation/components/FormOTP.tsx @@ -1,5 +1,4 @@ import { HStack, PinInput, PinInputField, Spinner } from "@chakra-ui/react" -import Link from "next/link" import { useRouter } from "next/router" import { useEffect, useState } from "react" import { useMutation } from "react-query" @@ -16,7 +15,7 @@ const FormOTP = () => { const [email, setEmail] = useState('') const [otp, setOtp] = useState('') const [count, { startCountdown, resetCountdown }] = useCountdown({ - countStart: 60 * 3, + countStart: 60 * 1, intervalMs: 1000, }) @@ -59,7 +58,7 @@ const FormOTP = () => { }, [mutationActivation.data, router, query.redirect]) return ( - + setActive(false)} mode="desktop">
Masukan Kode OTP
diff --git a/src-migrate/modules/register/components/Form.tsx b/src-migrate/modules/register/components/Form.tsx index ddc3397f..dc9107b2 100644 --- a/src-migrate/modules/register/components/Form.tsx +++ b/src-migrate/modules/register/components/Form.tsx @@ -1,4 +1,4 @@ -import { ChangeEvent, useEffect, useMemo } from "react"; +import { ChangeEvent, useMemo } from "react"; import { useMutation } from "react-query"; import { useRegisterStore } from "~/common/stores/useRegisterStore"; import { RegisterProps } from "~/common/types/auth"; @@ -8,7 +8,6 @@ import FormCaptcha from "./FormCaptcha"; import { useRouter } from "next/router"; import { UseToastOptions, useToast } from "@chakra-ui/react"; import Link from "next/link"; -import { registerSchema } from "~/common/validations/auth"; const Form = () => { const { @@ -20,9 +19,7 @@ const Form = () => { validate, } = useRegisterStore() - const isFormValid = useMemo(() => { - return Object.keys(errors).length === 0 - }, [errors]) + const isFormValid = useMemo(() => Object.keys(errors).length === 0, [errors]) const router = useRouter() const toast = useToast() diff --git a/src-migrate/modules/register/components/TermCondition.tsx b/src-migrate/modules/register/components/TermCondition.tsx index aaba6604..6b95ba19 100644 --- a/src-migrate/modules/register/components/TermCondition.tsx +++ b/src-migrate/modules/register/components/TermCondition.tsx @@ -10,7 +10,7 @@ const TermCondition = () => { return ( <>
- +
{' '} -- cgit v1.2.3 From 6a6ce21e5a552b0dc6cd541710a87fd0a0fd9d20 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Fri, 17 Nov 2023 15:23:22 +0700 Subject: Update button resend otp on OTP Form --- .../account-activation/components/FormOTP.tsx | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'src-migrate') diff --git a/src-migrate/modules/account-activation/components/FormOTP.tsx b/src-migrate/modules/account-activation/components/FormOTP.tsx index 9bc0fb23..6815a088 100644 --- a/src-migrate/modules/account-activation/components/FormOTP.tsx +++ b/src-migrate/modules/account-activation/components/FormOTP.tsx @@ -1,4 +1,4 @@ -import { HStack, PinInput, PinInputField, Spinner } from "@chakra-ui/react" +import { Button, HStack, PinInput, PinInputField, Spinner } from "@chakra-ui/react" import { useRouter } from "next/router" import { useEffect, useState } from "react" import { useMutation } from "react-query" @@ -86,20 +86,22 @@ const FormOTP = () => {
)} -
+
{!mutationActivation.data?.activation && ( <> {count > 0 && timeFormat(count)} {!mutationActivation.data?.activation && count == 0 && ( - + <> +
Belum menerima kode apapun?
+ + + )} )} -- cgit v1.2.3