diff --git a/apps/landing/src/app/(auth)/forgot-password/_actions/forgot-password-action.ts b/apps/landing/src/app/(auth)/forgot-password/_actions/forgot-password-action.ts new file mode 100644 index 0000000..07958f8 --- /dev/null +++ b/apps/landing/src/app/(auth)/forgot-password/_actions/forgot-password-action.ts @@ -0,0 +1,25 @@ +'use server'; + +import { fetcher } from '@/lib/fetcher'; +import { + forgotPasswordValidationSchema, + ForgotPasswordValidationType, +} from '../_validation/forgot-password-validation'; + +export async function ForgotPasswordAction( + request: ForgotPasswordValidationType +) { + const validRequest = forgotPasswordValidationSchema.parse(request); + + const { data, error } = await fetcher.POST('/v1/auth/forgot', { + body: { + email: validRequest.email, + }, + }); + + console.log(data, error); + + if (error) throw new Error(error.message); + + return data; +} diff --git a/apps/landing/src/app/(auth)/forgot-password/_components/forgot-password-form.tsx b/apps/landing/src/app/(auth)/forgot-password/_components/forgot-password-form.tsx new file mode 100644 index 0000000..cca4bc3 --- /dev/null +++ b/apps/landing/src/app/(auth)/forgot-password/_components/forgot-password-form.tsx @@ -0,0 +1,78 @@ +'use client'; + +import { + Button, + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, + Input, +} from '@components'; +import { Turnstile, TurnstileInstance } from '@marsidev/react-turnstile'; +import { useRef } from 'react'; +import { LuLoader } from 'react-icons/lu'; +import { useFormForgotPassword } from '../_hooks/use-form-forgot-password'; +import { usePostForgotPassowrd } from '../_hooks/use-post-forgot-password'; +import { ForgotPasswordValidationType } from '../_validation/forgot-password-validation'; + +export function ForgotPasswordForm() { + const ref = useRef(null); + + const form = useFormForgotPassword(); + const { mutate, isPending, error } = usePostForgotPassowrd(form); + + const onSubmit = (values: ForgotPasswordValidationType) => { + mutate(values); + }; + + return ( +
+ + {error && ( +
+ {(error as Error).message} +
+ )} + + ( + + Email + + + + + + )} + /> + + form.setValue('token', token)} + options={{ + theme: 'light', + size: 'flexible', + language: 'id', + }} + /> + + + + + ); +} diff --git a/apps/landing/src/app/(auth)/forgot-password/_hooks/use-form-forgot-password.ts b/apps/landing/src/app/(auth)/forgot-password/_hooks/use-form-forgot-password.ts new file mode 100644 index 0000000..ebf9172 --- /dev/null +++ b/apps/landing/src/app/(auth)/forgot-password/_hooks/use-form-forgot-password.ts @@ -0,0 +1,16 @@ +import { zodResolver } from '@hookform/resolvers/zod'; +import { useForm } from 'react-hook-form'; +import { + forgotPasswordValidationSchema, + type ForgotPasswordValidationType, +} from '../_validation/forgot-password-validation'; + +export function useFormForgotPassword() { + return useForm({ + resolver: zodResolver(forgotPasswordValidationSchema), + defaultValues: { + email: '', + token: '', + }, + }); +} diff --git a/apps/landing/src/app/(auth)/forgot-password/_hooks/use-post-forgot-password.ts b/apps/landing/src/app/(auth)/forgot-password/_hooks/use-post-forgot-password.ts new file mode 100644 index 0000000..f9995d8 --- /dev/null +++ b/apps/landing/src/app/(auth)/forgot-password/_hooks/use-post-forgot-password.ts @@ -0,0 +1,16 @@ +import { useMutation } from '@tanstack/react-query'; +import { toast } from 'sonner'; +import { ForgotPasswordAction } from '../_actions/forgot-password-action'; +import { useFormForgotPassword } from './use-form-forgot-password'; + +export function usePostForgotPassowrd( + form: ReturnType +) { + return useMutation({ + mutationFn: ForgotPasswordAction, + onSuccess: ({ message }) => { + form.reset(); + toast(message); + }, + }); +} diff --git a/apps/landing/src/app/(auth)/forgot-password/_http/fetch-post-forgot-password.ts b/apps/landing/src/app/(auth)/forgot-password/_http/fetch-post-forgot-password.ts new file mode 100644 index 0000000..e69de29 diff --git a/apps/landing/src/app/(auth)/forgot-password/_validation/forgot-password-validation.ts b/apps/landing/src/app/(auth)/forgot-password/_validation/forgot-password-validation.ts new file mode 100644 index 0000000..d99d3a1 --- /dev/null +++ b/apps/landing/src/app/(auth)/forgot-password/_validation/forgot-password-validation.ts @@ -0,0 +1,9 @@ +import { z } from 'zod'; + +export const forgotPasswordValidationSchema = z.object({ + email: z.string().email({ message: 'Format email tidak valid' }), + token: z.string(), +}); +export type ForgotPasswordValidationType = z.infer< + typeof forgotPasswordValidationSchema +>; diff --git a/apps/landing/src/app/(auth)/forgot-password/page.tsx b/apps/landing/src/app/(auth)/forgot-password/page.tsx new file mode 100644 index 0000000..36cccf8 --- /dev/null +++ b/apps/landing/src/app/(auth)/forgot-password/page.tsx @@ -0,0 +1,23 @@ +import { LogoSimple } from '@/app/_components/logo'; +import { Metadata } from 'next'; +import { ForgotPasswordForm } from './_components/forgot-password-form'; + +export const metadata: Metadata = { + title: 'IMPHNEN - Signin', +}; + +export default function Page() { + return ( + <> +
+ + +

+ Reset password akunmu +

+ + +
+ + ); +} diff --git a/apps/landing/src/app/_components/toaster.tsx b/apps/landing/src/app/_components/toaster.tsx new file mode 100644 index 0000000..ee824a2 --- /dev/null +++ b/apps/landing/src/app/_components/toaster.tsx @@ -0,0 +1,28 @@ +'use client'; + +import { Toaster as Sonner } from 'sonner'; + +type ToasterProps = React.ComponentProps; + +const Toaster = ({ ...props }: ToasterProps) => { + return ( + + ); +}; + +export { Toaster }; diff --git a/apps/landing/src/app/layout.tsx b/apps/landing/src/app/layout.tsx index 5853ac6..5bea567 100644 --- a/apps/landing/src/app/layout.tsx +++ b/apps/landing/src/app/layout.tsx @@ -3,6 +3,7 @@ import '@/styles/globals.css'; import { cn } from '@utils'; import { type Metadata } from 'next'; import { Providers } from './_components/providers'; +import { Toaster } from './_components/toaster'; export const metadata: Metadata = { title: 'IMPHNEN - Ingin Menjadi Programmer Handal?', @@ -24,6 +25,7 @@ export default function RootLayout({ disableTransitionOnChange > {children} +