Compare commits

..
Author SHA1 Message Date
ArraysID 4f394bacf1 chore: remove pnpm, accidentally added out of habit 2025-04-27 06:22:02 +07:00
ArraysID d4bd7f4e1a feat: initialize index file to export atoms 2025-04-27 06:16:32 +07:00
ArraysID 1b6b442078 chore: add tailwind-animate-css package 2025-04-27 06:14:43 +07:00
ArraysID f9191708f4 feat: update dependencies in package.json and package-lock.json 2025-04-27 06:11:39 +07:00
ArraysID 10da7d0a16 feat: migrate simple theme switcher component 2025-04-27 05:56:52 +07:00
ArraysID c07c75f23b feat: migrate footer component 2025-04-27 05:51:58 +07:00
ArraysID e9622234fe feat: migrate call to action component 2025-04-27 05:50:47 +07:00
ArraysID aedc2b5dfb feat: migrate testimonials component 2025-04-27 05:49:50 +07:00
ArraysID 5b1b700ccb feat: migrate learning resources component 2025-04-27 05:47:43 +07:00
ArraysID 0478d12ffc feat: migrate community component 2025-04-27 05:44:11 +07:00
ArraysID 5929c5a667 feat: migrate features component 2025-04-27 05:38:34 +07:00
ArraysID 206a426120 feat: migrate hero component 2025-04-27 05:35:49 +07:00
ArraysID b4708ab2aa feat: migrate header component 2025-04-27 05:28:37 +07:00
ArraysID 53b069f242 feat: remove unused tw-animate-css import 2025-04-26 22:51:49 +07:00
ArraysID c2a50a86bd feat: add path mapping for shadcn-ui atoms in tsconfig 2025-04-26 22:38:50 +07:00
ArraysID edec847eee feat: update landing app with shadcnI integration 2025-04-26 22:38:28 +07:00
ArraysID 9182440ab3 feat(shadcn-ui): initialize shadcn ui libs 2025-04-26 22:37:29 +07:00
ArraysID 259262a1ef chore: cleanup nextjs project 2025-04-26 21:13:19 +07:00
196 changed files with 2220 additions and 10520 deletions
+1
View File
@@ -0,0 +1 @@
VITE_API_URL=https://api.example.com
-9
View File
@@ -24,14 +24,5 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: npm install run: npm install
- name: Set env vars
run: |
echo "CMS_SECRET=${{ secrets.CMS_SECRET }}" >> $GITHUB_ENV
echo "CMS_STORAGE_REGION=${{ secrets.CMS_STORAGE_REGION }}" >> $GITHUB_ENV
echo "CMS_STORAGE_ENDPOINT=${{ secrets.CMS_STORAGE_ENDPOINT }}" >> $GITHUB_ENV
echo "CMS_STORAGE_ACCESS_KEY_ID=${{ secrets.CMS_STORAGE_ACCESS_KEY_ID }}" >> $GITHUB_ENV
echo "CMS_STORAGE_SECRET_ACCESS_KEY=${{ secrets.CMS_STORAGE_SECRET_ACCESS_KEY }}" >> $GITHUB_ENV
echo "CMS_POSTGRES_URL=${{ secrets.CMS_POSTGRES_URL }}" >> $GITHUB_ENV
- name: Nx Build - name: Nx Build
run: npx nx run-many --target=build --all run: npx nx run-many --target=build --all
+4 -25
View File
@@ -6,10 +6,9 @@
This repository is a **monorepo** for all frontend services of IMPHNEN. The monorepo includes three main applications: This repository is a **monorepo** for all frontend services of IMPHNEN. The monorepo includes three main applications:
1. **Gacha** - Application for <a href="https://gacha.imphnen.dev/" target="_blank">Gacha Website</a>. 1. **Gacha** - Application for <a href="https://gacha.imphnen.dev/" target="_blank">gacha website</a>.
2. **Backoffice** - Application for <a href="https://gacha.imphnen.dev/" target="_blank">Internal Management Website</a>. 2. **Backoffice** - Application for internal management.
3. **Dimentorin** - Application for <a href="https://dimentorin.imphnen.dev/" target="_blank">Mentoring Service</a>. 3. **Dimentorin** - Application for mentoring services.
4. **Landing Page** - Application for <a href="https://imphnen.dev/" target="_blank">Landing Page</a>.
## How to install ## How to install
@@ -25,15 +24,6 @@ This repository is a **monorepo** for all frontend services of IMPHNEN. The mono
## How to run ## How to run
### Setup Environment Variables
Before running the applications, you need to set up the environment variables. You can do this by copying the `.env.example` file to `.env` and modifying the values according to your needs.
```sh
cd apps/{appname}
cp .env.example .env
```
### Development ### Development
Use the following commands to run in development mode: Use the following commands to run in development mode:
@@ -50,10 +40,6 @@ Use the following commands to run in development mode:
```sh ```sh
npm run dimentorin:dev npm run dimentorin:dev
``` ```
- **Landing Page**:
```sh
npm run landing:dev
```
### Build ### Build
@@ -71,10 +57,6 @@ Use the following commands to build the applications:
```sh ```sh
npm run dimentorin:build npm run dimentorin:build
``` ```
- **Landing Page**:
```sh
npm run landing:build
```
### Production ### Production
@@ -92,10 +74,6 @@ Use the following commands to run the applications in production mode:
```sh ```sh
npm run dimentorin:prod npm run dimentorin:prod
``` ```
- **Landing Page**:
```sh
npm run landing:prod
```
### Storybook ### Storybook
@@ -109,6 +87,7 @@ This repository uses Storybook to develop, test, and document UI components in a
npm run ui:storybook npm run ui:storybook
``` ```
- **Run Unit Test** - **Run Unit Test**
This command runs unit tests for the UI components to ensure they function as expected. This command runs unit tests for the UI components to ensure they function as expected.
-1
View File
@@ -1 +0,0 @@
VITE_API_URL=
@@ -2,19 +2,24 @@ import { useForm } from 'react-hook-form';
import { import {
authLoginSchema, authLoginSchema,
TLoginRequest, TLoginRequest,
usePostLogin,
} from '@imphnen-frontend-service/service'; } from '@imphnen-frontend-service/service';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import { useSession } from '@imphnen-frontend-service/utils'; import { toast } from 'sonner';
export const useLogin = () => { export const useLogin = () => {
const postLogin = usePostLogin();
const form = useForm<TLoginRequest>({ const form = useForm<TLoginRequest>({
resolver: zodResolver(authLoginSchema), resolver: zodResolver(authLoginSchema),
mode: 'all', mode: 'all',
}); });
const { signIn } = useSession(); const onSubmit = form.handleSubmit((data) => {
postLogin.mutate(data, {
const onSubmit = form.handleSubmit((data) => signIn(data)); onSuccess: () => toast.success("Login sukses"),
onError: (error) => toast.error(error.message),
});
});
return { return {
form, form,
@@ -18,7 +18,7 @@ import {
RowSelectionState, RowSelectionState,
} from '@tanstack/react-table'; } from '@tanstack/react-table';
import ModalEditAccount from './_components/modal-edit-account'; import ModalEditAccount from './_components/modal-edit-account';
import { useQueryState } from '@imphnen-frontend-service/utils'; import { useQueryState } from '../../hook/use-query-state';
interface Account { interface Account {
id: number; id: number;
@@ -166,7 +166,7 @@ export const Components: FC = (): ReactElement => {
</Button> </Button>
{showFilter && ( {showFilter && (
<div className="absolute right-0 top-[calc(100%+12px)] z-10 shadow-lg"> <div className="absolute right-0 top-[calc(100%+12px)] z-10 shadow-lg">
<Filter onClose={() => setShowFilter(false)} options={[]} /> <Filter onClose={() => setShowFilter(false)} />
</div> </div>
)} )}
</div> </div>
@@ -0,0 +1,18 @@
import { FC, ReactElement } from 'react';
import { Outlet } from 'react-router-dom';
import { BackofficeSidebar } from '@imphnen-frontend-service/ui/organisms';
export const AppLayout: FC = (): ReactElement => {
return (
<div className="bg-primary-50 min-h-screen flex justify-center">
<div className="bg-primary-50 min-h-screen w-full flex">
<BackofficeSidebar />
<div className="flex-1 overflow-auto lg:max-w-[1000px] 2xl:max-w-[1280px] mx-auto">
<Outlet />
</div>
</div>
</div>
);
};
export default AppLayout;
@@ -10,7 +10,7 @@ import { FC, Fragment, ReactElement, useState } from 'react';
import ModalAddItem from './_components/modal-add-item'; import ModalAddItem from './_components/modal-add-item';
import ModalEditItem from './_components/modal-edit-item'; import ModalEditItem from './_components/modal-edit-item';
import ModalDeleteItem from './_components/modal-delete-item'; import ModalDeleteItem from './_components/modal-delete-item';
import { useQueryState } from '@imphnen-frontend-service/utils'; import { useQueryState } from '../../hook/use-query-state';
export const Components: FC = (): ReactElement => { export const Components: FC = (): ReactElement => {
const [showModalAddItem, setShowModalAddItem] = useState(false); const [showModalAddItem, setShowModalAddItem] = useState(false);
@@ -0,0 +1,18 @@
import { FC, ReactElement } from 'react';
import { Outlet } from 'react-router-dom';
import { BackofficeSidebar } from '@imphnen-frontend-service/ui/organisms';
export const AppLayout: FC = (): ReactElement => {
return (
<div className="bg-primary-50 min-h-screen flex justify-center">
<div className="bg-primary-50 min-h-screen w-full flex">
<BackofficeSidebar />
<div className="flex-1 overflow-auto lg:max-w-[1000px] 2xl:max-w-[1280px] mx-auto">
<Outlet />
</div>
</div>
</div>
);
};
export default AppLayout;
@@ -32,16 +32,7 @@ export const Components: FC = (): ReactElement => {
size="lg" size="lg"
className="w-full" className="w-full"
/> />
<Button <Button type="submit" size="md" className="w-full">
disabled={
form.formState.isSubmitting ||
form.formState.isValidating ||
!form.formState.isValid
}
type="submit"
size="md"
className="w-full"
>
Login Login
</Button> </Button>
</form> </form>
@@ -0,0 +1,18 @@
import { FC, ReactElement } from 'react';
import { Outlet } from 'react-router-dom';
import { BackofficeSidebar } from '@imphnen-frontend-service/ui/organisms';
export const AppLayout: FC = (): ReactElement => {
return (
<div className="bg-primary-50 min-h-screen flex justify-center">
<div className="bg-primary-50 min-h-screen w-full flex">
<BackofficeSidebar />
<div className="flex-1 overflow-auto lg:max-w-[1000px] 2xl:max-w-[1280px] mx-auto">
<Outlet />
</div>
</div>
</div>
);
};
export default AppLayout;
+18
View File
@@ -0,0 +1,18 @@
import { FC, ReactElement } from 'react';
import { Outlet } from 'react-router-dom';
import { BackofficeSidebar } from '@imphnen-frontend-service/ui/organisms';
export const AppLayout: FC = (): ReactElement => {
return (
<div className="bg-primary-50 min-h-screen flex justify-center">
<div className="bg-primary-50 min-h-screen w-full flex">
<BackofficeSidebar />
<div className="flex-1 overflow-auto lg:max-w-[1000px] 2xl:max-w-[1280px] mx-auto">
<Outlet />
</div>
</div>
</div>
);
};
export default AppLayout;
+18
View File
@@ -0,0 +1,18 @@
import { FC, ReactElement } from 'react';
import { Outlet } from 'react-router-dom';
import { BackofficeSidebar } from '@imphnen-frontend-service/ui/organisms';
export const AppLayout: FC = (): ReactElement => {
return (
<div className="bg-primary-50 min-h-screen flex justify-center">
<div className="bg-primary-50 min-h-screen w-full flex">
<BackofficeSidebar />
<div className="flex-1 overflow-auto lg:max-w-[1000px] 2xl:max-w-[1280px] mx-auto">
<Outlet />
</div>
</div>
</div>
);
};
export default AppLayout;
@@ -0,0 +1,18 @@
import { FC, ReactElement } from 'react';
import { Outlet } from 'react-router-dom';
import { BackofficeSidebar } from '@imphnen-frontend-service/ui/organisms';
export const AppLayout: FC = (): ReactElement => {
return (
<div className="bg-primary-50 min-h-screen flex justify-center">
<div className="bg-primary-50 min-h-screen w-full flex">
<BackofficeSidebar />
<div className="flex-1 overflow-auto lg:max-w-[1000px] 2xl:max-w-[1280px] mx-auto">
<Outlet />
</div>
</div>
</div>
);
};
export default AppLayout;
+3 -100
View File
@@ -1,108 +1,11 @@
import { import { LoaderFunctionArgs } from 'react-router-dom';
PERMISSIONS,
SessionToken,
SessionUser,
} from '@imphnen-frontend-service/utils';
import { LoaderFunctionArgs, redirect } from 'react-router';
const mappingPublicRoutes = [
'/auth/login',
'/auth/forgot',
'/auth/new-password',
];
const mappingRoutePermissions = [
{
path: '/dashboard',
permissions: [],
},
{
path: '/users',
permissions: [PERMISSIONS.USERS.READ_LIST],
},
{
path: '/users/create',
permissions: [PERMISSIONS.USERS.CREATE],
},
{
path: '/users/update',
permissions: [PERMISSIONS.USERS.UPDATE],
},
{
path: '/users/detail',
permissions: [PERMISSIONS.USERS.READ_DETAIL],
},
{
path: '/roles',
permissions: [PERMISSIONS.ROLES.READ_LIST],
},
{
path: '/roles/create',
permissions: [PERMISSIONS.ROLES.CREATE],
},
{
path: '/roles/update',
permissions: [PERMISSIONS.ROLES.UPDATE],
},
{
path: '/roles/detail',
permissions: [PERMISSIONS.ROLES.READ_DETAIL],
},
{
path: '/permissions',
permissions: [PERMISSIONS.PERMISSIONS.READ_LIST],
},
{
path: '/permissions/create',
permissions: [PERMISSIONS.PERMISSIONS.CREATE],
},
{
path: '/permissions/update',
permissions: [PERMISSIONS.PERMISSIONS.UPDATE],
},
{
path: '/permissions/detail',
permissions: [PERMISSIONS.PERMISSIONS.READ_DETAIL],
},
];
//TODO : Fix this later
// const redirectToFirstAccessibleRoute = (userPermissions: string[]) => {
// const fallback = mappingRoutePermissions.find((route) =>
// route.permissions.some((perm) => userPermissions.includes(perm))
// );
// return redirect(fallback?.path ?? '/auth/login');
// };
export const middleware = async ({ request }: LoaderFunctionArgs) => { export const middleware = async ({ request }: LoaderFunctionArgs) => {
const url = new URL(request.url); const url = new URL(request.url);
const pathname = url.pathname; const pathname = url.pathname;
const session = SessionUser.get();
const session_token = SessionToken.get();
const token = session_token?.token?.access_token;
const userPermissions =
session?.role?.permissions?.map?.((perm) => perm?.name) ?? [];
if (mappingPublicRoutes.includes(pathname)) { if (pathname) return null;
if (token) return redirect('/dashboard');
return null;
}
if (!session) return redirect('/auth/login');
const matchedRoute = mappingRoutePermissions.find(
(route) => route.path === pathname
);
if (matchedRoute) {
const hasPermission =
!matchedRoute.permissions ||
matchedRoute.permissions.some((perm) => userPermissions.includes(perm));
if (!hasPermission) {
return '/dashboard';
}
}
return null; return null;
}; };
-1
View File
@@ -1 +0,0 @@
VITE_API_URL=
+9 -5
View File
@@ -2,23 +2,27 @@ import { useForm } from 'react-hook-form';
import { import {
authLoginSchema, authLoginSchema,
TLoginRequest, TLoginRequest,
usePostLogin,
} from '@imphnen-frontend-service/service'; } from '@imphnen-frontend-service/service';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import { useSession } from '@imphnen-frontend-service/utils'; import { toast } from 'sonner';
export const useLogin = () => { export const useLogin = () => {
const postLogin = usePostLogin();
const form = useForm<TLoginRequest>({ const form = useForm<TLoginRequest>({
resolver: zodResolver(authLoginSchema), resolver: zodResolver(authLoginSchema),
mode: 'all', mode: 'all',
}); });
const { signIn, isLoading } = useSession(); const onSubmit = form.handleSubmit((data) => {
postLogin.mutate(data, {
const onSubmit = form.handleSubmit((data) => signIn(data)); onSuccess: () => toast.success("Login sukses"),
onError: (error) => toast.error(error.message),
});
});
return { return {
form, form,
onSubmit, onSubmit,
isLoading
}; };
}; };
-24
View File
@@ -1,24 +0,0 @@
import { useForm } from 'react-hook-form';
import {
TVerifyOtpRequest,
verifyEmailSchema,
} from '@imphnen-frontend-service/service';
import { zodResolver } from '@hookform/resolvers/zod';
import { useOtp } from '@imphnen-frontend-service/utils';
export const useOtpHook = () => {
const form = useForm<TVerifyOtpRequest>({
resolver: zodResolver(verifyEmailSchema),
mode: 'all',
});
const { otp, isLoading } = useOtp();
const onSubmit = form.handleSubmit((data) => otp(data));
return {
form,
onSubmit,
isLoading
};
};
@@ -1,24 +0,0 @@
import { useForm } from 'react-hook-form';
import {
authRegisterSchema,
TRegisterRequest,
} from '@imphnen-frontend-service/service';
import { zodResolver } from '@hookform/resolvers/zod';
import { useRegister } from '@imphnen-frontend-service/utils';
export const useRegisterHook = () => {
const form = useForm<TRegisterRequest>({
resolver: zodResolver(authRegisterSchema),
mode: 'all',
});
const { register, isLoading } = useRegister();
const onSubmit = form.handleSubmit((data) => register(data));
return {
form,
onSubmit,
isLoading
};
};
@@ -1,10 +0,0 @@
import { useSendOTP } from '@imphnen-frontend-service/utils';
export const useResendOtpHook = () => {
const { resendOTP, isLoading } = useSendOTP();
return {
resendOTP
};
};
+2 -9
View File
@@ -5,7 +5,7 @@ import { useLogin } from '../../_hooks/use-login';
import { ArrowLeftOutlined } from '@ant-design/icons'; import { ArrowLeftOutlined } from '@ant-design/icons';
export const Components: FC = (): ReactElement => { export const Components: FC = (): ReactElement => {
const { form, onSubmit, isLoading } = useLogin(); const { form, onSubmit } = useLogin();
return ( return (
<div className="flex flex-col justify-center items-center min-h-screen py-[60px] px-[80px]"> <div className="flex flex-col justify-center items-center min-h-screen py-[60px] px-[80px]">
@@ -42,7 +42,7 @@ export const Components: FC = (): ReactElement => {
Lupa Password ? Lupa Password ?
</a> </a>
</div> </div>
<Button className="w-full" type='submit' disabled={(!form.formState.isValid || isLoading)}>Enter Isekai</Button> <Button className="w-full" type='submit'>Enter Isekai</Button>
</form> </form>
<div className="flex my-3 gap-3 justify-center"> <div className="flex my-3 gap-3 justify-center">
@@ -74,13 +74,6 @@ export const Components: FC = (): ReactElement => {
<ArrowLeftOutlined /> <ArrowLeftOutlined />
Kembali Ke Homepage Kembali Ke Homepage
</Button> </Button>
<Button
className='xl:hidden w-full gap-3'
variant='secondary'
>
<ArrowLeftOutlined />
Kembali Ke Homepage
</Button>
</div> </div>
</div> </div>
</div> </div>
@@ -1,12 +0,0 @@
import { FC, ReactElement } from 'react';
import { Outlet } from 'react-router-dom';
export const AppLayout: FC = (): ReactElement => {
return (
<main className="bg-primary-50 min-h-screen">
<Outlet />
</main>
);
};
export default AppLayout;
@@ -1,70 +0,0 @@
import { FC, ReactElement, useEffect, useState } from 'react';
import { ControlledInputField, RegisterResetBanner } from "@imphnen-frontend-service/ui/organisms";
import { useOtpHook } from '../../../_hooks/use-otp';
import { Button } from '@imphnen-frontend-service/ui/atoms';
import { useSearchParams } from 'react-router-dom';
import { useResendOtpHook } from '../../../_hooks/use-resend-otp';
export const Components: FC = (): ReactElement => {
const { form, onSubmit, isLoading } = useOtpHook()
const [ searchParams ] = useSearchParams()
const [ disabled, setDisabled] = useState(true);
const [ timeLeft, setTimeLeft ] = useState(5 * 60);
const { resendOTP } = useResendOtpHook()
useEffect(() => {
if (disabled) {
const interval = setInterval(() => {
setTimeLeft(prev => {
if (prev <= 1) {
clearInterval(interval);
setDisabled(false);
return 0;
}
return prev - 1;
});
}, 1000);
return () => clearInterval(interval);
}
}, [disabled]);
const formatTime = (seconds: number) => {
const m = Math.floor(seconds / 60);
const s = seconds % 60;
return `${m}:${s.toString().padStart(2, '0')}`;
};
return (
<div className='flex flex-col justify-center items-center min-h-screen py-[60px] px-[80px]'>
<div className='bg-white min-w-[1120px] min-h-[712px] p-10 rounded-2xl shadow-md flex gap-6'>
<RegisterResetBanner />
<div className='border-2 border-primary-500/50 w-[596px] rounded-lg py-[70px] px-[48px] flex flex-col justify-center'>
<h3 className='mt-5 text-3xl font-semibold text-primary-500'>Verifikasi Email</h3>
<h5 className='mt-2 text-xl font-medium text-primary-500'>Yeay~! Pesan dari dunia lain sudah dikirimkan ke {searchParams.get("email")}</h5>
<form onSubmit={onSubmit} className='mt-7'>
<ControlledInputField
label="OTP"
size="lg"
className="w-full"
placeholder="XXXXXX"
max={6}
name={'otp'}
maxLength={6}
control={form.control}
/>
<Button type="button" className="mt-2" disabled={disabled} onClick={
() => {
resendOTP({email: searchParams.get("email") || ""})
setDisabled(true)
setTimeLeft(5 * 60)
}
}>Kirim ulang otp {disabled? `(${formatTime(timeLeft)})`: ""}</Button>
<Button className="xl:w-full mt-2" disabled={(!form.formState.isValid || isLoading)}>Linked Start !!!</Button>
</form>
</div>
</div>
</div>
);
};
export default Components;
+32 -44
View File
@@ -1,69 +1,60 @@
import { FC, ReactElement } from 'react'; import { FC, ReactElement } from 'react';
import { ControlledInputField, RegisterResetBanner } from '@imphnen-frontend-service/ui/organisms'; import { RegisterResetBanner } from '@imphnen-frontend-service/ui/organisms';
import { Button } from '@imphnen-frontend-service/ui/atoms'; import { Button, Input } from '@imphnen-frontend-service/ui/atoms';
import { ArrowLeftOutlined } from '@ant-design/icons'; import { InputField } from '@imphnen-frontend-service/ui/molecules';
import { useRegisterHook } from '../../_hooks/use-register';
export const Components: FC = (): ReactElement => { export const Components: FC = (): ReactElement => {
const { form, onSubmit, isLoading } = useRegisterHook();
return ( return (
<div className="flex flex-col justify-center items-center min-h-screen py-[60px] px-[80px]"> <div className="flex flex-col justify-center items-center min-h-screen py-[60px] px-[80px]">
<div className="bg-white xl:min-w-[1130px] min-h-[712px] p-10 rounded-2xl shadow-md flex gap-6"> <div className="bg-white min-w-[1120px] min-h-[712px] p-10 rounded-2xl shadow-md flex gap-6">
<RegisterResetBanner /> <RegisterResetBanner />
<div className="xl:border-2 xl:border-primary-500/50 xl:w-[726px] rounded-lg py-[32px] px-[48px] flex justify-center"> <div className="border-2 border-primary-500/50 w-[696px] rounded-lg py-[32px] px-[48px] flex justify-center">
<div className="xl:w-[714px]"> <div className="w-[704px]">
<Button
className='xl:hidden gap-3'
variant='secondary'
>
<ArrowLeftOutlined />
Login
</Button>
<h3 className="mt-5 text-3xl font-semibold text-primary-500"> <h3 className="mt-5 text-3xl font-semibold text-primary-500">
Register Register
</h3> </h3>
<h5 className="text-primary-500 font-medium"> <h5 className="text-primary-500 font-medium">
Yosha~! Saatnya Bergabung dengan Dimentorin Yosha~! Saatnya Bergabung dengan Dimentorin
</h5> </h5>
<form onSubmit={onSubmit}> <div className="grid grid-flow-row-dense my-7 grid-cols-2 gap-2">
<div className='my-7'> <InputField
<ControlledInputField label="First Name"
label="Full name" size="lg"
placeholder="Nama Depan"
/>
<InputField
label="Last Name"
size="lg" size="lg"
className="w-full" className="w-full"
placeholder="Nama Lengkap" placeholder="Nama Belakang"
name={'fullname'}
control={form.control}
/> />
</div> <InputField
<div className="grid grid-flow-row-dense my-7 lg:grid-cols-2 gap-2">
<ControlledInputField
label="Email" label="Email"
size="lg" size="lg"
className="w-full"
placeholder="Contoh : yourname@mail.com" placeholder="Contoh : yourname@mail.com"
type='email'
control={form.control}
name={'email'}
/> />
<ControlledInputField <div className="flex gap-2">
label="No Hp" <div className="flex flex-col gap-2">
size="lg" <h4 className="font-medium">OTP Code</h4>
<Input
className="w-full" className="w-full"
placeholder="Contoh : 088877665544" size="lg"
control={form.control} placeholder="Kode OTP"
name={'phone_number'}
/> />
</div> </div>
<ControlledInputField <div className="flex flex-col">
<Button size="sm" className="mt-[30px]">
Kirim OTP
</Button>
</div>
</div>
</div>
<InputField
label="Password" label="Password"
className="w-full" className="w-full"
size="lg" size="lg"
type="password" type="password"
placeholder="Buat password sekeren jurus ultimate-mu!" placeholder="Buat password sekeren jurus ultimate-mu!"
control={form.control}
name={'password'}
/> />
<h6 className="text-sm text-gray-500 mt-1"> <h6 className="text-sm text-gray-500 mt-1">
"Senpai~! Pastikan password-mu sekuat pertahanan kastil!" "Senpai~! Pastikan password-mu sekuat pertahanan kastil!"
@@ -92,17 +83,14 @@ export const Components: FC = (): ReactElement => {
🚨 🚨
</span> </span>
</h6> </h6>
<ControlledInputField <InputField
label="Repeat Password" label="Repeat Password"
className="w-full" className="w-full"
size="lg" size="lg"
type="password" type="password"
placeholder="Ulangi password-mu, Senpai~!" placeholder="Ulangi password-mu, Senpai~!"
control={form.control}
name={"confirm_password"}
/> />
<Button className="xl:w-full mt-2" disabled={(!form.formState.isValid || isLoading)}>Linked Start !!!</Button> <Button className="w-full mt-2">Linked Start !!!</Button>
</form>
</div> </div>
</div> </div>
</div> </div>

Some files were not shown because too many files have changed in this diff Show More