diff --git a/apps/landing/src/app/(auth)/signin/_actions/signin-action.ts b/apps/landing/src/app/(auth)/signin/_actions/signin-action.ts
new file mode 100644
index 0000000..0a42b5f
--- /dev/null
+++ b/apps/landing/src/app/(auth)/signin/_actions/signin-action.ts
@@ -0,0 +1,24 @@
+'use server';
+
+import { setAccessToken, setRefreshToken } from '@/lib/cookies';
+import { fetchPostSignin } from '../_http/fetch-post-signin';
+import {
+ type SignInValidationType,
+ signInValidationSchema,
+} from '../_validation/signin-validation';
+
+export async function SigninAction(request: SignInValidationType) {
+ console.log(request);
+
+ const validRequest = signInValidationSchema.parse(request);
+
+ const { data } = await fetchPostSignin(validRequest);
+
+ const accessToken = data.token.access_token;
+ const refreshToken = data.token.refresh_token;
+
+ await setAccessToken(accessToken);
+ await setRefreshToken(refreshToken);
+
+ return data;
+}
diff --git a/apps/landing/src/app/(auth)/signin/_components/signin-form.tsx b/apps/landing/src/app/(auth)/signin/_components/signin-form.tsx
new file mode 100644
index 0000000..3b08022
--- /dev/null
+++ b/apps/landing/src/app/(auth)/signin/_components/signin-form.tsx
@@ -0,0 +1,69 @@
+'use client';
+
+import {
+ Button,
+ Form,
+ FormControl,
+ FormField,
+ FormItem,
+ FormLabel,
+ FormMessage,
+ Input,
+} from '@components';
+import { LuLoader } from 'react-icons/lu';
+import { useFormSignin } from '../_hooks/use-form-signin';
+import { usePostSignin } from '../_hooks/use-post-signin';
+import { type SignInValidationType } from '../_validation/signin-validation';
+
+export function SigninForm() {
+ const form = useFormSignin();
+ const { mutate, isPending, error } = usePostSignin(form);
+
+ const onSubmit = (values: SignInValidationType) => {
+ mutate(values);
+ };
+
+ return (
+
+
+ );
+}
diff --git a/apps/landing/src/app/(auth)/signin/_hooks/use-form-signin.ts b/apps/landing/src/app/(auth)/signin/_hooks/use-form-signin.ts
new file mode 100644
index 0000000..76af058
--- /dev/null
+++ b/apps/landing/src/app/(auth)/signin/_hooks/use-form-signin.ts
@@ -0,0 +1,16 @@
+import { zodResolver } from '@hookform/resolvers/zod';
+import { useForm } from 'react-hook-form';
+import {
+ signInValidationSchema,
+ type SignInValidationType,
+} from '../_validation/signin-validation';
+
+export function useFormSignin() {
+ return useForm({
+ resolver: zodResolver(signInValidationSchema),
+ defaultValues: {
+ email: '',
+ password: '',
+ },
+ });
+}
diff --git a/apps/landing/src/app/(auth)/signin/_hooks/use-post-signin.ts b/apps/landing/src/app/(auth)/signin/_hooks/use-post-signin.ts
new file mode 100644
index 0000000..217b35e
--- /dev/null
+++ b/apps/landing/src/app/(auth)/signin/_hooks/use-post-signin.ts
@@ -0,0 +1,18 @@
+import { useMutation } from '@tanstack/react-query';
+import { useRouter } from 'next/navigation';
+import { SigninAction } from '../_actions/signin-action';
+import { useFormSignin } from './use-form-signin';
+
+export function usePostSignin(form: ReturnType) {
+ const router = useRouter();
+
+ return useMutation({
+ mutationFn: SigninAction,
+ onSuccess: () => {
+ router.push('/');
+ },
+ onError: () => {
+ form.resetField('password');
+ },
+ });
+}
diff --git a/apps/landing/src/app/(auth)/signin/_http/fetch-post-signin.ts b/apps/landing/src/app/(auth)/signin/_http/fetch-post-signin.ts
new file mode 100644
index 0000000..52fbab2
--- /dev/null
+++ b/apps/landing/src/app/(auth)/signin/_http/fetch-post-signin.ts
@@ -0,0 +1,24 @@
+import { fetcher } from '@/lib/fetcher';
+import { SignInValidationType } from '../_validation/signin-validation';
+
+export async function fetchPostSignin({
+ email,
+ password,
+}: SignInValidationType) {
+ const { data, error, response } = await fetcher.POST('/v1/auth/login', {
+ body: {
+ email,
+ password,
+ },
+ });
+
+ if (error) {
+ throw new Error(error.message);
+ }
+
+ if (!response.ok) {
+ throw new Error('Someting went wrong, please try again later');
+ }
+
+ return data;
+}
diff --git a/apps/landing/src/app/(auth)/signin/_validation/signin-validation.ts b/apps/landing/src/app/(auth)/signin/_validation/signin-validation.ts
new file mode 100644
index 0000000..0bb050b
--- /dev/null
+++ b/apps/landing/src/app/(auth)/signin/_validation/signin-validation.ts
@@ -0,0 +1,18 @@
+import { z } from 'zod';
+
+export const signInValidationSchema = z.object({
+ email: z
+ .string({
+ required_error: 'Email tidak boleh kosong',
+ invalid_type_error: 'Email harus berupa string',
+ })
+ .min(1, 'Email tidak boleh kosong')
+ .email('Email harus valid'),
+ password: z
+ .string({
+ required_error: 'Password tidak boleh kosong',
+ invalid_type_error: 'Password harus berupa string',
+ })
+ .min(1, 'Password tidak boleh kosong'),
+});
+export type SignInValidationType = z.infer;
diff --git a/apps/landing/src/app/(auth)/signin/page.tsx b/apps/landing/src/app/(auth)/signin/page.tsx
index 7d1e0b7..40647b1 100644
--- a/apps/landing/src/app/(auth)/signin/page.tsx
+++ b/apps/landing/src/app/(auth)/signin/page.tsx
@@ -1,3 +1,38 @@
+import { LogoSimple } from '@/app/_components/logo';
+import { Metadata } from 'next';
+import Link from 'next/link';
+import { SigninForm } from './_components/signin-form';
+
+export const metadata: Metadata = {
+ title: 'IMPHNEN - Signin',
+ description:
+ 'Komunitas Ingin Menjadi Programmer Handal Namung Enggan Ngonding',
+};
+
export default function Page() {
- return <>>;
+ return (
+ <>
+
+
+
+
+ Masuk untuk mengakses akunmu
+
+
+
+
+
+
+
+ Belum punya akun?{' '}
+
+ Daftar
+
+
+
+ >
+ );
}