feat: add signin
This commit is contained in:
@@ -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;
|
||||||
|
}
|
||||||
@@ -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 (
|
||||||
|
<Form {...form}>
|
||||||
|
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
|
||||||
|
{error && (
|
||||||
|
<div className="p-2 text-xs bg-red-50 border border-red-200 text-red-800 rounded-sm">
|
||||||
|
{(error as Error).message}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="email"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Email</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input placeholder="emailmu@mail.com" {...field} />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="password"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Password</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input type="password" placeholder="••••••••" {...field} />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button type="submit" disabled={isPending} className="w-full font-bold">
|
||||||
|
{isPending ? <LuLoader className="h-5 w-5 animate-spin" /> : 'Masuk'}
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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<SignInValidationType>({
|
||||||
|
resolver: zodResolver(signInValidationSchema),
|
||||||
|
defaultValues: {
|
||||||
|
email: '',
|
||||||
|
password: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -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<typeof useFormSignin>) {
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: SigninAction,
|
||||||
|
onSuccess: () => {
|
||||||
|
router.push('/');
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
form.resetField('password');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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<typeof signInValidationSchema>;
|
||||||
@@ -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() {
|
export default function Page() {
|
||||||
return <></>;
|
return (
|
||||||
|
<>
|
||||||
|
<div className="flex flex-col items-center gap-4">
|
||||||
|
<LogoSimple />
|
||||||
|
|
||||||
|
<p className="text-muted-foreground text-center text-sm sm:text-base">
|
||||||
|
Masuk untuk mengakses akunmu
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<SigninForm />
|
||||||
|
|
||||||
|
<div className="w-full space-y-4">
|
||||||
|
<p className="text-muted-foreground px-4 text-center text-xs leading-5 text-balance sm:text-sm">
|
||||||
|
Belum punya akun?{' '}
|
||||||
|
<Link
|
||||||
|
href="/signup"
|
||||||
|
className="font-bold hover:underline hover:underline-offset-4 transition-colors text-primary-500"
|
||||||
|
>
|
||||||
|
Daftar
|
||||||
|
</Link>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user