From a7cf85dd66941f3701398f0ae43620856f8c8b9e Mon Sep 17 00:00:00 2001 From: boboiazumi Date: Fri, 30 May 2025 14:19:36 +0700 Subject: [PATCH] feat: OTP Resend --- .../src/app/_hooks/use-resend-otp.ts | 10 ++++++ .../src/app/auth/register/otp/page.tsx | 35 ++++++++++++++++++- apps/dimentorin/src/middleware.ts | 2 +- libs/service/src/api/auth/index.ts | 12 +++++++ libs/service/src/hooks/auth/index.ts | 16 ++++++++- libs/service/src/types/auth/index.ts | 4 +++ libs/utils/src/hooks/index.ts | 3 +- libs/utils/src/hooks/use-send-otp.ts | 25 +++++++++++++ 8 files changed, 103 insertions(+), 4 deletions(-) create mode 100644 apps/dimentorin/src/app/_hooks/use-resend-otp.ts create mode 100644 libs/utils/src/hooks/use-send-otp.ts diff --git a/apps/dimentorin/src/app/_hooks/use-resend-otp.ts b/apps/dimentorin/src/app/_hooks/use-resend-otp.ts new file mode 100644 index 0000000..29f0b71 --- /dev/null +++ b/apps/dimentorin/src/app/_hooks/use-resend-otp.ts @@ -0,0 +1,10 @@ +import { useSendOTP } from '@imphnen-frontend-service/utils'; + +export const useResendOtpHook = () => { + + const { resendOTP, isLoading } = useSendOTP(); + + return { + resendOTP + }; +}; \ No newline at end of file diff --git a/apps/dimentorin/src/app/auth/register/otp/page.tsx b/apps/dimentorin/src/app/auth/register/otp/page.tsx index 249ca78..a79b525 100644 --- a/apps/dimentorin/src/app/auth/register/otp/page.tsx +++ b/apps/dimentorin/src/app/auth/register/otp/page.tsx @@ -1,12 +1,38 @@ -import { FC, ReactElement } from 'react'; +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 (
@@ -26,6 +52,13 @@ export const Components: FC = (): ReactElement => { maxLength={6} control={form.control} /> +
diff --git a/apps/dimentorin/src/middleware.ts b/apps/dimentorin/src/middleware.ts index c172d70..c9e06f7 100644 --- a/apps/dimentorin/src/middleware.ts +++ b/apps/dimentorin/src/middleware.ts @@ -10,7 +10,7 @@ const mappingPublicRoutes = [ '/auth/login', '/auth/forgot', '/auth/forgot/otp', - '/auth/register', + '/auth/register', '/auth/register/otp', '/auth/register/success', '/auth/new-password', diff --git a/libs/service/src/api/auth/index.ts b/libs/service/src/api/auth/index.ts index efec499..08cb959 100644 --- a/libs/service/src/api/auth/index.ts +++ b/libs/service/src/api/auth/index.ts @@ -3,6 +3,7 @@ import { TLoginRequest, TLoginResponse, TRegisterRequest, + TSendOTPRequest, TVerifyEmailRequest, } from '../../types/auth'; import { TResponseMessage } from '../../types/common'; @@ -39,3 +40,14 @@ export const postVerifyEmail = async ( }); return data; }; + +export const postSendOtp = async ( + payload: TSendOTPRequest +): Promise => { + const { data } = await api({ + method: 'POST', + url: '/auth/send-otp', + data: payload, + }); + return data; +}; diff --git a/libs/service/src/hooks/auth/index.ts b/libs/service/src/hooks/auth/index.ts index 4c2540b..67570b8 100644 --- a/libs/service/src/hooks/auth/index.ts +++ b/libs/service/src/hooks/auth/index.ts @@ -1,9 +1,10 @@ import { useMutation, UseMutationResult } from '@tanstack/react-query'; -import { postLogin, postRegister, postVerifyEmail } from '../../api/auth'; +import { postLogin, postRegister, postSendOtp, postVerifyEmail } from '../../api/auth'; import { TLoginRequest, TLoginResponse, TRegisterRequest, + TSendOTPRequest, TVerifyEmailRequest, } from '../../types/auth'; @@ -44,3 +45,16 @@ export const usePostVerifyEmail = (): UseMutationResult< mutationFn: async (payload) => await postVerifyEmail(payload), }); }; + +export const usePostSendOTP = (): UseMutationResult< + TResponseMessage, + TResponseError, + TSendOTPRequest, + unknown +> => { + return useMutation({ + mutationKey: ['post-send-otp'], + mutationFn: async (payload) => await postSendOtp(payload), + }); +}; + diff --git a/libs/service/src/types/auth/index.ts b/libs/service/src/types/auth/index.ts index 8471fcd..b1b133c 100644 --- a/libs/service/src/types/auth/index.ts +++ b/libs/service/src/types/auth/index.ts @@ -27,3 +27,7 @@ export type TVerifyEmailRequest = { email: string; otp: string; }; + +export type TSendOTPRequest = { + email: string +}; \ No newline at end of file diff --git a/libs/utils/src/hooks/index.ts b/libs/utils/src/hooks/index.ts index 40fb27d..749ecb2 100644 --- a/libs/utils/src/hooks/index.ts +++ b/libs/utils/src/hooks/index.ts @@ -3,4 +3,5 @@ export * from './use-auth-store'; export * from './use-modal-login'; export * from './use-session'; export * from './use-register'; -export * from './use-otp'; \ No newline at end of file +export * from './use-otp'; +export * from './use-send-otp'; \ No newline at end of file diff --git a/libs/utils/src/hooks/use-send-otp.ts b/libs/utils/src/hooks/use-send-otp.ts new file mode 100644 index 0000000..107197c --- /dev/null +++ b/libs/utils/src/hooks/use-send-otp.ts @@ -0,0 +1,25 @@ +import { TSendOTPRequest, usePostSendOTP } from '@imphnen-frontend-service/service'; +import { toast } from 'sonner'; + +export const useSendOTP = () => { + const { mutate, isPending } = usePostSendOTP(); + + const resendOTP = (payload: TSendOTPRequest) => { + mutate(payload, { + onSuccess: (data) => { + toast.success('Berhasil Mengirim Ulang OTP'); + }, + onError: (err) => { + toast.error( + err?.response?.data?.message ?? + 'Terjadi Kesalahan yang tidak diketahui' + ); + }, + }); + }; + + return { + resendOTP, + isLoading: isPending, + }; +};