diff options
Diffstat (limited to 'src-migrate/modules')
16 files changed, 767 insertions, 0 deletions
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..ec300ba4 --- /dev/null +++ b/src-migrate/modules/account-activation/components/FormEmail.tsx @@ -0,0 +1,97 @@ +import { Alert, AlertIcon } from "@chakra-ui/react" +import Link from "next/link" +import { useRouter } from "next/router" +import { ChangeEvent, useEffect, useState } from "react" +import { useMutation } from "react-query" +import Modal from "~/common/components/elements/Modal" +import { useRegisterStore } from "~/common/stores/useRegisterStore" +import { ActivationReqProps } from "~/common/types/auth" +import { activationReq } from "~/services/auth" + +const FormEmail = () => { + const router = useRouter() + const { query } = router + const [active, setActive] = useState<boolean>(false) + const [isBtnDisabled, setIsBtnDisabled] = useState<boolean>(true) + const [email, setEmail] = useState<string>('') + + 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<HTMLFormElement>) => { + e.preventDefault() + mutation.data = undefined + mutation.mutate({ email }) + } + + useEffect(() => { + if (mutation.data?.activation_request === true) { + const urlParams = new URLSearchParams({ + activation: 'otp', + email, + redirect: (router.query?.redirect || '/') as string + }) + router.push(`${router.route}?${urlParams}`) + } + }, [mutation.data?.activation_request, router, email]) + + return ( + <Modal active={active} mode="desktop" close={() => router.push(router.route)} title="Aktivasi Akun"> + <form onSubmit={handleSubmit} className="py-3 flex flex-col items-center gap-y-4"> + { + mutation.data !== undefined && + !mutation.data?.activation_request && + ( + <Alert status="warning"> + <AlertIcon /> + {mutation.data?.reason === 'ACTIVE' && 'Akun sudah aktif.'} + {mutation.data?.reason === 'NOT_FOUND' && 'Akun tidak ditemukan.'} + + {mutation.data?.reason === 'ACTIVE' && ( + <Link href="/login" className="ml-1 text-yellow-700 underline">Klik untuk masuk akun anda</Link> + )} + + {mutation.data?.reason === 'NOT_FOUND' && ( + <Link href="/register" className="ml-1 text-yellow-700 underline">Klik untuk daftar akun baru</Link> + )} + </Alert> + ) + } + + <input + type="text" + className="form-input w-full" + placeholder="Masukan alamat email anda" + autoComplete="email" + value={email} + onChange={(e) => setEmail(e.target.value)} + autoFocus + /> + <button + type="submit" + className="btn-yellow w-full" + disabled={isBtnDisabled || mutation.isLoading} + > + {mutation.isLoading ? 'Loading...' : 'Kirim Aktivasi'} + </button> + </form> + </Modal> + ) +} + +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..6815a088 --- /dev/null +++ b/src-migrate/modules/account-activation/components/FormOTP.tsx @@ -0,0 +1,125 @@ +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" +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" + +const FormOTP = () => { + const router = useRouter() + const { query } = router + const [active, setActive] = useState<boolean>(false) + const [email, setEmail] = useState<string>('') + const [otp, setOtp] = useState<string>('') + const [count, { startCountdown, resetCountdown }] = useCountdown({ + countStart: 60 * 1, + 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]) + + useEffect(() => { + if (mutationActivation.data?.user) { + setAuth(mutationActivation.data.user) + router.push((query?.redirect || '/') as string) + } + }, [mutationActivation.data, router, query.redirect]) + + return ( + <Modal active={active} className="w-10/12 md:w-fit px-10" close={() => setActive(false)} mode="desktop"> + <div className="pb-8 flex flex-col items-center"> + <div className="text-title-sm font-medium mb-4">Masukan Kode OTP</div> + <HStack> + <PinInput size='lg' otp onChange={(val) => setOtp(val)}> + <PinInputField autoFocus /> + <PinInputField /> + <PinInputField /> + <PinInputField /> + </PinInput> + </HStack> + <div className="mt-4 text-center"> + {mutationActivation.isLoading && <Spinner size='sm' />} + + {!mutationActivation.isLoading && + mutationActivation.data?.reason === 'INVALID_OTP' && + ( + <span className="text-red-700">Mohon maaf kode OTP yand anda masukan salah</span> + ) + } + </div> + + {!mutationActivation.data?.activation && ( + <div className="mt-4 text-center text-gray-700"> + Kode verifikasi telah dikirimkan ke alamat email <span className="font-medium">{email}</span>. Silakan periksa kotak masuk atau folder spam. + </div> + )} + + <div className="mt-4 flex flex-col items-center"> + {!mutationActivation.data?.activation && ( + <> + {count > 0 && timeFormat(count)} + + {!mutationActivation.data?.activation && count == 0 && ( + <> + <div className="mb-3">Belum menerima kode apapun?</div> + + <Button + onClick={() => mutationActivationReq.mutate({ email })} + disabled={mutationActivationReq.isLoading} + > + {mutationActivationReq.isLoading ? <Spinner size='sm' /> : 'Kirim Ulang'} + </Button> + </> + )} + </> + )} + + </div> + </div> + </Modal> + ) +} + +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 new file mode 100644 index 00000000..b68b244f --- /dev/null +++ b/src-migrate/modules/account-activation/components/FormToken.tsx @@ -0,0 +1,75 @@ +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" +import { setAuth } from "~/common/libs/auth" + +const FormToken = () => { + const router = useRouter() + const { query } = router + const [active, setActive] = useState<boolean>(false) + + const mutation = useMutation({ + mutationFn: (data: ActivationTokenProps) => activationUserToken(data), + }) + + useEffect(() => { + if (query?.activation === 'token' && typeof query?.token === 'string') { + mutation.mutate({ token: query.token }) + setActive(true) + } + //eslint-disable-next-line + }, [query.activation, query.token]) + + useEffect(() => { + if (mutation.data?.user) { + setAuth(mutation.data.user) + router.push((query?.redirect || '/') as string) + } + }, [mutation.data, router, query.redirect]) + + return ( + <Modal active={active} mode="desktop"> + <div className="pb-6 flex flex-col items-center gap-y-6"> + {mutation.isLoading && ( + <> + <Spinner size='lg' borderWidth='3px' /> + <div className="text-h-sm">Mohon tunggu sedang memverifikasi akun</div> + </> + )} + + {!mutation.isLoading && !mutation.data?.activation && ( + <Alert + status={mutation.data?.activation ? 'success' : 'error'} + flexDirection="column" + alignItems="center" + justifyContent="center" + textAlign="center" + height="200px" + bg="transparent" + > + <AlertIcon boxSize="40px" mr={0} /> + <AlertTitle className="mt-4 mb-1 text-h-sm"> + Aktivasi akun gagal + </AlertTitle> + <AlertDescription maxWidth="sm"> + {mutation.data?.reason === 'INVALID_TOKEN' && ( + <> + Token sudah kadaluwarsa, silahkan coba kembali. + <Link href='/register?activation=email' className="block mt-8 text-red-700 underline">Aktivasi Akun</Link> + </> + )} + </AlertDescription> + </Alert> + )} + </div> + </Modal> + ) +} + +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..97c96953 --- /dev/null +++ b/src-migrate/modules/account-activation/index.tsx @@ -0,0 +1,16 @@ +import { useRouter } from "next/router" +import FormToken from "./components/FormToken" +import FormEmail from "./components/FormEmail" +import FormOTP from "./components/FormOTP" + +const AccountActivation = () => { + return ( + <> + <FormEmail /> + <FormToken /> + <FormOTP /> + </> + ) +} + +export default AccountActivation
\ No newline at end of file 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 ( + <header> + <nav className="pt-6 sticky top-0 z-50 bg-white border-b-2 border-danger-500"> + <div className="container flex items-center gap-x-6"> + <Link href='/'> + <Image src={Logo} alt="Logo Indoteknik.com" width={LOGO_WIDTH} height={LOGO_HEIGHT} /> + </Link> + + <SearchBar /> + + <div className="flex gap-x-4 items-center"> + <Link + target='_blank' + rel='noreferrer' + href='/my/transactions' + className='flex items-center gap-x-2 !text-gray-900' + > + <DocumentCheckIcon className='w-7' /> + Daftar<br />Quotation + </Link> + + <Link + target='_blank' + rel='noreferrer' + href='/my/wishlist' + className='flex items-center gap-x-2 !text-gray-900' + > + <HeartIcon className='w-7' /> + Wishlist + </Link> + + <a + href={''} + target='_blank' + rel='noreferrer' + className='flex items-center gap-x-1 !text-gray_r-12/80' + > + <Image src='/images/socials/Whatsapp-2.png' alt='Whatsapp' width={48} height={48} /> + <div> + <div className='font-semibold'>Whatsapp</div> + 0812 8080 622 (Chat) + </div> + </a> + </div> + </div> + + <div className="container mt-6 flex"> + <button type="button" className="w-3/12 p-4 font-semibold border border-gray_r-6 rounded-t-xl flex items-center relative"> + Kategori + </button> + + <nav className="w-6/12 flex px-1 divide-x divide-gray-200"> + {SECONDARY_MENU_ITEMS.map((item, index) => ( + <Link key={index} href={item.href} target="_blank" rel="noreferrer" className="font-medium text-center p-4 flex-1 !text-gray-800 hover:bg-gray-100 transition-all"> + {item.label} + </Link> + ))} + </nav> + + </div> + </nav> + </header> + ) +} + +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 ( + <div>HeaderMobile</div> + ) +} + +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 ( + <form className="flex-1 flex items-center"> + <input + type="text" + className="form-input p-3 rounded-r-none border-r-0 border-gray-300 focus:border-gray-300" + placeholder="Ketik nama / part number / merk" + /> + + <button + type="submit" + className="rounded-r border border-l-0 border-gray-300 px-2 py-2.5" + > + <MagnifyingGlassIcon className='w-6' /> + </button> + + </form> + ) +} + +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 ? <HeaderDesktop /> : <HeaderMobile /> +} + +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 ( + <> + <div className="container grid"> + <div className="col-span-2"> + <VerticalBanner /> + </div> + </div> + </> + ) +} + +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 ( + <div>VerticalBanner</div> + ) +} + +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<PageContentProps>(`page-content:${path}`, async () => await getPageContent({ path })) + + if (isLoading) { + return <PageContentSkeleton /> + } + + return ( + <div dangerouslySetInnerHTML={{ __html: data?.content || '' }}></div> + ) +} + +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..dc9107b2 --- /dev/null +++ b/src-migrate/modules/register/components/Form.tsx @@ -0,0 +1,179 @@ +import { ChangeEvent, useMemo } 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 { + form, + isCheckedTNC, + isValidCaptcha, + errors, + updateForm, + validate, + } = useRegisterStore() + + const isFormValid = useMemo(() => Object.keys(errors).length === 0, [errors]) + + const router = useRouter() + const toast = useToast() + + const handleInputChange = (event: ChangeEvent<HTMLInputElement>) => { + const { name, value } = event.target; + updateForm(name, value) + validate() + } + + const mutation = useMutation({ + mutationFn: (data: RegisterProps) => registerUser(data) + }) + + const handleSubmit = async (e: ChangeEvent<HTMLFormElement>) => { + e.preventDefault() + + const response = await mutation.mutateAsync(form) + + if (response?.register === true) { + const urlParams = new URLSearchParams({ + activation: 'otp', + email: form.email, + redirect: (router.query?.next || '/') as string + }) + 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. <Link href={activationUrl} className="underline">Klik untuk aktivasi akun</Link></>, + status: 'warning' + }) + break + } + } + + return ( + <form className="mt-6 grid grid-cols-1 gap-y-4" onSubmit={handleSubmit}> + <div> + <label htmlFor="company"> + Nama Perusahaan <span className='text-gray_r-11'>(opsional)</span> + </label> + + <input + type="text" + name="company" + id="company" + className="form-input mt-3" + placeholder="cth: INDOTEKNIK DOTCOM GEMILANG" + autoCapitalize="true" + value={form.company} + onChange={handleInputChange} + /> + </div> + + <div> + <label htmlFor='name'>Nama Lengkap</label> + + <input + type='text' + id='name' + name='name' + className='form-input mt-3' + placeholder='Masukan nama lengkap anda' + value={form.name} + onChange={handleInputChange} + aria-invalid={!!errors.name} + /> + + {!!errors.name && <span className="form-msg-danger">{errors.name}</span>} + </div> + + <div> + <label htmlFor='phone'>No Handphone</label> + + <input + type='tel' + id='phone' + name='phone' + className='form-input mt-3' + placeholder='08xxxxxxxx' + value={form.phone} + onChange={handleInputChange} + aria-invalid={!!errors.phone} + /> + + {!!errors.phone && <span className="form-msg-danger">{errors.phone}</span>} + </div> + + <div> + <label htmlFor='email'>Alamat Email</label> + + <input + type='text' + id='email' + name='email' + className='form-input mt-3' + placeholder='Masukan alamat email anda' + value={form.email} + onChange={handleInputChange} + autoComplete="username" + aria-invalid={!!errors.email} + /> + + {!!errors.email && <span className="form-msg-danger">{errors.email}</span>} + </div> + + <div> + <label htmlFor='password'>Kata Sandi</label> + <input + type='password' + name='password' + id='password' + className='form-input mt-3' + placeholder='••••••••••••' + value={form.password} + onChange={handleInputChange} + autoComplete="current-password" + aria-invalid={!!errors.password} + /> + + {!!errors.password && <span className="form-msg-danger">{errors.password}</span>} + </div> + + <FormCaptcha /> + + <TermCondition /> + + <button + type="submit" + className="btn-yellow w-full mt-2" + disabled={!isFormValid || !isCheckedTNC || mutation.isLoading || !isValidCaptcha} + > + {mutation.isLoading ? 'Loading...' : 'Daftar'} + </button> + </form> + ) +} + +export default Form
\ No newline at end of file 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 <ReCaptcha onChange={handleCaptchaChange} /> +} + +export default FormCaptcha
\ 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..6b95ba19 --- /dev/null +++ b/src-migrate/modules/register/components/TermCondition.tsx @@ -0,0 +1,34 @@ +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, isCheckedTNC, toggleCheckTNC, openTNC } = useRegisterStore() + + return ( + <> + <div className="mt-4 flex items-center gap-x-2"> + <Checkbox id='tnc' name='tnc' isChecked={isCheckedTNC} onChange={toggleCheckTNC} /> + <div> + <label htmlFor="tnc" className="cursor-pointer">Dengan ini saya menyetujui</label> + {' '} + <span + className="font-medium text-danger-500 cursor-pointer" + onClick={openTNC} + > + syarat dan ketentuan + </span> + <label htmlFor="tnc" className="ml-2 cursor-pointer">yang berlaku</label> + </div> + </div> + + <Modal active={isOpenTNC} close={closeTNC} > + <PageContent path='/register#tnd' /> + </Modal> + </> + ) +} + +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..00931284 --- /dev/null +++ b/src-migrate/modules/register/index.tsx @@ -0,0 +1,54 @@ +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 ( + <div className="container"> + <div className="grid grid-cols-1 md:grid-cols-2 gap-x-10 pt-10 px-2 md:pt-16"> + <section> + <Link href='/' className="block md:hidden"> + <Image src={IndoteknikLogo} alt='Logo Indoteknik' width={LOGO_WIDTH} height={LOGO_HEIGHT} className="mx-auto mb-4 w-auto h-auto" priority /> + </Link> + + <h1 className="text-2xl font-semibold text-center md:text-left"> + Daftar Akun Indoteknik + </h1> + <h2 className="text-gray_r-11 mt-1 mb-10 text-center md:text-left"> + Buat akun sekarang lebih mudah dan terverifikasi + </h2> + + <Form /> + + <div className='text-gray_r-11 mt-4 text-center md:text-left'> + Sudah punya akun Indoteknik?{' '} + <Link href='/login' className='inline font-medium text-danger-500'> + Masuk + </Link> + </div> + + <div className='text-gray_r-11 mt-4 text-center md:text-left'> + Akun anda belum aktif?{' '} + <Link href='/register?activation=email' className='inline font-medium text-danger-500'> + Aktivasi + </Link> + </div> + </section> + + <section className="my-10 md:my-0"> + <PageContent path="/register" /> + </section> + </div> + + <AccountActivation /> + </div> + ) +} + +export default Register
\ No newline at end of file |
