From 07eb2e7d649ac5a806d0ee06270a90a2fd2b90ab Mon Sep 17 00:00:00 2001 From: mehrzadghdev Date: Fri, 15 Aug 2025 22:08:31 +0330 Subject: [PATCH] chore: useApi added to the components --- .env | 3 +- public/locales/en/authentication.json | 3 +- public/locales/en/common.json | 4 ++ public/locales/fa/authentication.json | 3 +- public/locales/fa/common.json | 3 +- .../authorization/api/authorizationAPI.ts | 42 ++++------- .../AuthenticationSteps.tsx | 16 ++--- .../AuthenticationSteps/CompleteSignUp.tsx | 11 ++- .../AuthenticationSteps/EnterPasswordForm.tsx | 40 +++++------ .../GoogleAuthentication.tsx | 32 +++++---- .../AuthenticationSteps/LoginRegiserForm.tsx | 32 +++++---- .../AuthenticationSteps/OtpVerifyForm.tsx | 45 ++++++------ .../AuthenticationSteps/VerifyPhoneNumber.tsx | 33 ++++----- .../ForgetPassword/ChangePassword.tsx | 23 ++++--- .../ForgetPassword/ForgetPasswordOtp.tsx | 38 +++++----- .../ForgetPassword/ForgettedPasswordInfo.tsx | 21 +++--- src/hooks/useApi.ts | 69 +++++++++++++++---- src/lib/apiClient.ts | 4 +- 18 files changed, 237 insertions(+), 185 deletions(-) diff --git a/.env b/.env index 2731127..a0621ec 100644 --- a/.env +++ b/.env @@ -1 +1,2 @@ -VITE_GOOGLE_CLIENT_ID=https://272098283932-bft2gvlgjn8edopg0lnqjq1i9ekdmipt.apps.googleusercontent.com/ \ No newline at end of file +VITE_GOOGLE_CLIENT_ID=https://272098283932-bft2gvlgjn8edopg0lnqjq1i9ekdmipt.apps.googleusercontent.com/ +VITE_DEFUALT_AUTH_RETURN_URL=/setting/profile \ No newline at end of file diff --git a/public/locales/en/authentication.json b/public/locales/en/authentication.json index 6633ea7..9faa2fc 100644 --- a/public/locales/en/authentication.json +++ b/public/locales/en/authentication.json @@ -7,7 +7,8 @@ "loginWithGoogle": "Login with google", "emailIsInvalid": "Email is invalid", "phoneNumberIsInvalid": "Phone number is invalid", - "thisFieldIsRequired": "This field is requried" + "thisFieldIsRequired": "This field is requried", + "googleAuthenticationFailed": "Login with google failed" }, "verify": { "verify": "Verify", diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 72b36d8..5196be8 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -246,5 +246,9 @@ "yemen": "Yemen", "zambia": "Zambia", "zimbabwe": "Zimbabwe" + }, + "messages": { + "noResualtFound": "No result found.", + "serverError": "Internal server error" } } diff --git a/public/locales/fa/authentication.json b/public/locales/fa/authentication.json index bfb8610..f688171 100644 --- a/public/locales/fa/authentication.json +++ b/public/locales/fa/authentication.json @@ -7,7 +7,8 @@ "loginWithGoogle": "ورود با گوگل", "emailIsInvalid": "ایمیل وارد شده نامعتبر میباشد", "phoneNumberIsInvalid": "شماره وارد شده نامعتبر میباشد", - "thisFieldIsRequired": "این فیلد الزامی است" + "thisFieldIsRequired": "این فیلد الزامی است", + "googleAuthenticationFailed": "ورود با گوگل با خطا مواجه شد" }, "verify": { "verify": "اعتبارسنجی", diff --git a/public/locales/fa/common.json b/public/locales/fa/common.json index 60424fb..878b22d 100644 --- a/public/locales/fa/common.json +++ b/public/locales/fa/common.json @@ -185,7 +185,8 @@ "zimbabwe": "زیمبابوه" }, "messages": { - "noResualtFound": "نتیجه ای یافت نشد." + "noResualtFound": "نتیجه ای یافت نشد.", + "serverError": "خطای سمت سرور" }, "side": { "account": "حساب کاربری", diff --git a/src/features/authorization/api/authorizationAPI.ts b/src/features/authorization/api/authorizationAPI.ts index 51b1dfe..ebdff53 100644 --- a/src/features/authorization/api/authorizationAPI.ts +++ b/src/features/authorization/api/authorizationAPI.ts @@ -19,75 +19,61 @@ import type { SendForgetPassCodeRequest, SendSmsOtpRequest, } from '../types/userTypes'; - -const API_URL = 'https://accounts.business-harmony.com/api'; - -export const fetchRequest = ( - url: string, - body: Object | null, -): FetchPromise => { - return fetch(`${API_URL}/${url}`, { - body: JSON.stringify(body), - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - }); -}; +import apiClient from '@/lib/apiClient'; // GetUserStatusByPhoneNumberOrEmail export const getUserStatusByPhoneNumberOrEmail = async ( body: GetUserStatusByPhoneNumberOrEmailRequest, ) => { - return fetchRequest( + return apiClient.post( 'User/GetUserStatusByPhoneNumberOrEmail', body, ); }; export const loginOrSignUpWithOtp = async (body: LoginRequest) => { - return fetchRequest('User/LoginOrSignUpWithOtp', body); + return apiClient.post('User/LoginOrSignUpWithOtp', body); }; export const loginWithPassword = async (body: PasswordLoginRequest) => { - return fetchRequest('User/LoginWithPassword', body); + return apiClient.post('User/LoginWithPassword', body); }; export const sendSmsOtp = async (body: SendSmsOtpRequest) => { - return fetchRequest('User/SendSmsOtp', body); + return apiClient.post('User/SendSmsOtp', body); }; export const sendEmailOtp = async (body: SendEmailOtpRequest) => { - return fetchRequest('User/SendEmailOtp', body); + return apiClient.post('User/SendEmailOtp', body); }; export const confirmSmsOtp = async (body: ConfirmSmsOtpRequest) => { - return fetchRequest('User/ConfirmSmsOtp', body); + return apiClient.post('User/ConfirmSmsOtp', body); }; export const confirmEmailOtp = async (body: ConfirmEmailOtpRequest) => { - return fetchRequest('User/ConfirmEmailOtp', body); + return apiClient.post('User/ConfirmEmailOtp', body); }; export const resetPassword = async (body: ResetPasswordRequest) => { - return fetchRequest('User/ResetPassword', body); + return apiClient.post('User/ResetPassword', body); }; export const sendForgetPassCode = async (body: SendForgetPassCodeRequest) => { - return fetchRequest('User/SendForgetPassCode', body); + return apiClient.post('User/SendForgetPassCode', body); }; export const confirmForgetPassCode = async ( body: ConfirmForgetPassCodeRequest, ) => { - return fetchRequest('User/ConfirmForgetPassCode', body); + return apiClient.post('User/ConfirmForgetPassCode', body); }; export const loginOrSignUpWithGoogle = async ( body: LoginOrSignUpWithGoogleRequest, ) => { - return fetchRequest( + return apiClient.post( 'User/LoginOrSignUpWithGoogle', body, ); @@ -96,9 +82,9 @@ export const loginOrSignUpWithGoogle = async ( export const completeUserInformation = async ( body: CompleteUserInformationRequest, ) => { - return fetchRequest('User/CompleteUserInformation', body); + return apiClient.post('User/CompleteUserInformation', body); }; export const logOut = async () => { - return fetchRequest('User/LogOut', {}); + return apiClient.post('User/LogOut', {}); }; diff --git a/src/features/authorization/components/AuthenticationSteps/AuthenticationSteps.tsx b/src/features/authorization/components/AuthenticationSteps/AuthenticationSteps.tsx index e02bd3a..e0ff695 100644 --- a/src/features/authorization/components/AuthenticationSteps/AuthenticationSteps.tsx +++ b/src/features/authorization/components/AuthenticationSteps/AuthenticationSteps.tsx @@ -12,10 +12,10 @@ import { useNavigate, useSearchParams } from 'react-router-dom'; export const AuthenticationSteps = (): JSX.Element => { const navigate = useNavigate(); - const DEFAULT_RETURN_URL = '/profile'; const [searchParams] = useSearchParams(); - const authReturnUrl: string = - searchParams.get('returnUrl') ?? DEFAULT_RETURN_URL; + const authReturnUrl: string | null = searchParams.get('returnUrl'); + const authReturnUrlOrDefault: string = + authReturnUrl ?? import.meta.env.VITE_DEFUALT_AUTH_RETURN_URL; const [authMode, setAuthMode] = useState('register'); const [authType, setAuthType] = useState('phone'); const [currentStep, setCurrentStep] = useState('emailOrPhone'); @@ -66,8 +66,8 @@ export const AuthenticationSteps = (): JSX.Element => { }; const redirectToReturnUrl = () => { - if (authReturnUrl === DEFAULT_RETURN_URL) { - navigate(DEFAULT_RETURN_URL); + if (!authReturnUrl) { + navigate(import.meta.env.VITE_DEFUALT_AUTH_RETURN_URL); } else { location.href = authReturnUrl; } @@ -77,7 +77,7 @@ export const AuthenticationSteps = (): JSX.Element => { <> {currentStep === 'emailOrPhone' && ( { {currentStep === 'verify' && ( setCurrentStep('emailOrPhone')} @@ -104,7 +104,7 @@ export const AuthenticationSteps = (): JSX.Element => { {currentStep === 'enterPassword' && ( (null); const [touched, setTouched] = useState(false); const inputError: boolean = touched && !!error; - const [sendOtpLoading, setSendOtpLoading] = useState(false); + const { loading: sendSmsLoading, execute: sendSmsCall } = useApi(sendSmsOtp); const isPhoneValid = (code: string, phone: string) => { const phoneNumber = parsePhoneNumberFromString(code + phone); @@ -61,12 +62,8 @@ export const CompleteSignUp = ({ if (!value || !isPhoneValid(countryCode, value)) { inputRef.current?.focus(); } else { - setSendOtpLoading(true); - - await sendSmsOtp({ phoneNumber: countryCode + value }); + await sendSmsCall({ phoneNumber: countryCode + value }); onCompleteSignUp(countryCode, value); - - setSendOtpLoading(false); } }; @@ -110,7 +107,7 @@ export const CompleteSignUp = ({ sx={{ my: 4 }} /> - diff --git a/src/features/authorization/components/AuthenticationSteps/EnterPasswordForm.tsx b/src/features/authorization/components/AuthenticationSteps/EnterPasswordForm.tsx index 5d3fd1c..47f4c95 100644 --- a/src/features/authorization/components/AuthenticationSteps/EnterPasswordForm.tsx +++ b/src/features/authorization/components/AuthenticationSteps/EnterPasswordForm.tsx @@ -20,6 +20,7 @@ import { } from '../../api/authorizationAPI'; import type { PasswordLoginRequest } from '../../types/userTypes'; import { Icon, useToast } from '@rkheftan/harmony-ui'; +import { useApi } from '@/hooks/useApi'; export interface EnterPasswordFormProps { onEditValue: () => void; @@ -47,10 +48,16 @@ export const EnterPasswordForm = ({ const [inputTouched, setInputTouched] = useState(false); const [showPassword, setShowPassword] = useState(false); const inputRef = useRef(null); - const [loginLoading, setLoginLoading] = useState(false); - const [isLoginStatusSuccess, setIsLoginStatusSuccess] = useState(); - const [sendOtpLoading, setSendOtpLoading] = useState(false); const toast = useToast(); + const { loading: smsResendLoading, execute: smsResendCall } = + useApi(sendSmsOtp); + const { loading: emailResendLoading, execute: emailResendCall } = + useApi(sendEmailOtp); + const { + data: loginWithPassResult, + loading: loginWithPassLoading, + execute: loginWithPassCall, + } = useApi(loginWithPassword); const handleBlur = () => { setInputTouched(true); @@ -60,8 +67,6 @@ export const EnterPasswordForm = ({ if (!passValue) { inputRef.current?.focus(); } else { - setLoginLoading(true); - const apiRequest: PasswordLoginRequest = { phoneNumber: authType === 'phone' ? countryCode + loginRegisterValue : undefined, @@ -69,38 +74,33 @@ export const EnterPasswordForm = ({ password: passValue, returnUrl: authReturnUrl, }; - const result = await loginWithPassword(apiRequest); - const jsonRes = await result.json(); + await loginWithPassCall(apiRequest); - if (jsonRes.success) { - setIsLoginStatusSuccess(true); - onLoggedIn(jsonRes.userId); + if (!loginWithPassResult) return; + + if (loginWithPassResult.success) { + onLoggedIn(loginWithPassResult.userId); toast({ message: t('verify.youHaveSuccessfullyLoggedIn'), severity: 'success', }); } else { - setIsLoginStatusSuccess(false); toast({ - message: jsonRes.message, + message: loginWithPassResult.message, severity: 'error', }); } - setLoginLoading(false); } }; const handleLoginWithOtp = async () => { - setSendOtpLoading(true); - if (authType === 'phone') { - await sendSmsOtp({ phoneNumber: countryCode + loginRegisterValue }); + await smsResendCall({ phoneNumber: countryCode + loginRegisterValue }); } else { - await sendEmailOtp({ email: loginRegisterValue }); + await emailResendCall({ email: loginRegisterValue }); } - setSendOtpLoading(false); onLoginWithOTP(); }; @@ -171,14 +171,14 @@ export const EnterPasswordForm = ({ onClick={handleLoginWithOtp} sx={{ width: 'auto', mb: 2 }} variant="text" - loading={sendOtpLoading} + loading={emailResendLoading || smsResendLoading} endIcon={} > {t('enterPassword.loginWithOneTimeCode')} - diff --git a/src/features/authorization/components/AuthenticationSteps/GoogleAuthentication.tsx b/src/features/authorization/components/AuthenticationSteps/GoogleAuthentication.tsx index d422b11..906c39b 100644 --- a/src/features/authorization/components/AuthenticationSteps/GoogleAuthentication.tsx +++ b/src/features/authorization/components/AuthenticationSteps/GoogleAuthentication.tsx @@ -1,11 +1,12 @@ import { Button } from '@mui/material'; -import { useEffect, useRef, useState } from 'react'; +import { useEffect, useRef } from 'react'; import { useTranslation } from 'react-i18next'; import type { GoogleCodeClientResponse } from '../../types/userTypes'; import { loginOrSignUpWithGoogle } from '../../api/authorizationAPI'; import type { GUID } from '@/types/commonTypes'; import { Google } from 'iconsax-react'; -import { Icon } from '@rkheftan/harmony-ui'; +import { Icon, useToast } from '@rkheftan/harmony-ui'; +import { useApi } from '@/hooks/useApi'; export interface GoogleAuthenticationProps { disabled: boolean; @@ -19,9 +20,12 @@ export const GoogleAuthentication = ({ onGoogleAuthenticated, }: GoogleAuthenticationProps) => { const { t } = useTranslation('authentication'); - const [loginWithGoogleLoading, setLoginWithGoogleLoading] = - useState(false); - + const { + data: loginWithGoogleResult, + loading: loginWithGoogleLoading, + execute: loginWithGoogleCall, + } = useApi(loginOrSignUpWithGoogle); + const toast = useToast(); const clientRef = useRef(null); useEffect(() => { @@ -38,21 +42,21 @@ export const GoogleAuthentication = ({ ux_mode: 'popup', response_type: 'id_token', callback: async (resp: GoogleCodeClientResponse) => { - setLoginWithGoogleLoading(true); - - const result = await loginOrSignUpWithGoogle({ + await loginWithGoogleCall({ idToken: resp.id_token, returnUrl: authReturnUrl, }); - const jsonRes = await result.json(); - if (jsonRes.success) { - onGoogleAuthenticated(jsonRes.userId); + if (!loginWithGoogleResult) return; + + if (loginWithGoogleResult.success) { + onGoogleAuthenticated(loginWithGoogleResult.userId); } else { - // Todo: Add useToast to handle error + toast({ + message: t('loginForm.googleAuthenticationFailed'), + severity: 'error', + }); } - - setLoginWithGoogleLoading(false); }, }); }; diff --git a/src/features/authorization/components/AuthenticationSteps/LoginRegiserForm.tsx b/src/features/authorization/components/AuthenticationSteps/LoginRegiserForm.tsx index bb98e61..524649a 100644 --- a/src/features/authorization/components/AuthenticationSteps/LoginRegiserForm.tsx +++ b/src/features/authorization/components/AuthenticationSteps/LoginRegiserForm.tsx @@ -8,11 +8,11 @@ import { AuthenticationCard } from '../AuthenticationCard'; import { CountryCodeSelector } from '../CountryCodeSelector'; import type { UserStatus } from '../../types/userTypes'; import { getUserStatusByPhoneNumberOrEmail } from '../../api/authorizationAPI'; -import { Toast } from '@/components/Toast'; import type { CountryCode, GUID } from '@/types/commonTypes'; import { GoogleAuthentication } from './GoogleAuthentication'; import { isPhoneNumber } from '@/utils/regexes/isValidPhoneNumber'; import { useToast } from '@rkheftan/harmony-ui'; +import { useApi } from '@/hooks/useApi'; export interface LoginRegisterFormProps { loginRegisterValue: string; @@ -37,7 +37,6 @@ export function LoginRegisterForm({ authReturnUrl, onGoogleAuthenticated, }: LoginRegisterFormProps) { - const [checkStatusLoading, setCheckStatusLoading] = useState(false); const { t } = useTranslation('authentication'); const textFieldRef = useRef(null); const inputRef = useRef(null); @@ -45,6 +44,11 @@ export function LoginRegisterForm({ const [touched, setTouched] = useState(false); const inputError: boolean = touched && !!error; const toast = useToast(); + const { + data: userStatus, + loading: userStatusLoading, + execute: execUserStatus, + } = useApi(getUserStatusByPhoneNumberOrEmail); const handleInputChange = (event: React.ChangeEvent) => { const newValue = event.target.value; @@ -89,23 +93,21 @@ export function LoginRegisterForm({ const handleSubmit = async () => { if (validateInput(loginRegisterValue, authType, false)) { - setCheckStatusLoading(true); - const result = await getUserStatusByPhoneNumberOrEmail({ + await execUserStatus({ phoneNumber: authType === 'phone' ? countryCode + loginRegisterValue : undefined, email: authType === 'email' ? loginRegisterValue : undefined, }); - const jsonResult = await result.json(); - if (jsonResult.success) { - onLoginRegisterSubmit(loginRegisterValue, jsonResult.userStatus); - } else { - toast({ - message: jsonResult.message, - severity: 'error', - }); + if (!userStatus) { + return; + } + + if (userStatus.success) { + onLoginRegisterSubmit(loginRegisterValue, userStatus.userStatus); + } else { + toast({ message: userStatus.message, severity: 'error' }); } - setCheckStatusLoading(false); } else { inputRef.current?.focus(); validateInput(loginRegisterValue, authType); @@ -151,14 +153,14 @@ export function LoginRegisterForm({ /> - diff --git a/src/features/authorization/components/AuthenticationSteps/OtpVerifyForm.tsx b/src/features/authorization/components/AuthenticationSteps/OtpVerifyForm.tsx index 08dfba7..53a207b 100644 --- a/src/features/authorization/components/AuthenticationSteps/OtpVerifyForm.tsx +++ b/src/features/authorization/components/AuthenticationSteps/OtpVerifyForm.tsx @@ -4,7 +4,6 @@ import { Edit2 } from 'iconsax-react'; import DigitInput from '@/components/components/DigitsInput'; import type { AuthMode, AuthType } from '../../types/authTypes'; import { useEffect, useState } from 'react'; -import { Toast } from '@/components/Toast'; import { AuthenticationCard } from '../AuthenticationCard'; import type { LoginRequest } from '../../types/userTypes'; import { @@ -14,6 +13,7 @@ import { } from '../../api/authorizationAPI'; import type { CountryCode, GUID } from '@/types/commonTypes'; import { Icon, useToast } from '@rkheftan/harmony-ui'; +import { useApi } from '@/hooks/useApi'; interface OtpVerifyFormProps { value: string; @@ -39,13 +39,19 @@ export function OtpVerifyForm({ const [otpCode, setOtpCode] = useState(''); const [otpDigitInvalid, setOtpDigitInvalid] = useState(false); const [isStatusSuccess, setIsStatusSuccess] = useState(); - const [verifyStatusLoading, setVerifyStatusLoading] = - useState(false); const { t } = useTranslation('authentication'); const [resendTimer, setResendTimer] = useState(120); const [canResend, setCanResend] = useState(false); - const [resendLoading, setResendLoading] = useState(false); const toast = useToast(); + const { loading: smsResendLoading, execute: smsResendCall } = + useApi(sendSmsOtp); + const { loading: emailResendLoading, execute: emailResendCall } = + useApi(sendEmailOtp); + const { + data: loginSignUpResult, + loading: loginSignUpLoading, + execute: loginSignUpCall, + } = useApi(loginOrSignUpWithOtp); useEffect(() => { let interval: NodeJS.Timeout; @@ -61,17 +67,14 @@ export function OtpVerifyForm({ }, [resendTimer]); const handleResendOTPCode = async () => { - setResendLoading(true); - if (authType === 'phone') { - await sendSmsOtp({ phoneNumber: countryCode + value }); + await smsResendCall({ phoneNumber: countryCode + value }); } else { - await sendEmailOtp({ email: value }); + await emailResendCall({ email: value }); } setResendTimer(120); setCanResend(false); - setResendLoading(false); }; const formatTime = (seconds: number) => { @@ -90,7 +93,6 @@ export function OtpVerifyForm({ const handleLoginOrSignUp = async () => { setOtpDigitInvalid(false); - setVerifyStatusLoading(true); const loginRequest: LoginRequest = { otpCode: otpCode, @@ -98,16 +100,19 @@ export function OtpVerifyForm({ email: authType === 'email' ? value : undefined, returnUrl: authReturnUrl, }; - const result = await loginOrSignUpWithOtp(loginRequest); - const jsonRes = await result.json(); + await loginSignUpCall(loginRequest); - if (jsonRes.success) { + if (!loginSignUpResult) { + return; + } + + if (loginSignUpResult && loginSignUpResult.success) { setIsStatusSuccess(true); - if (jsonRes.registeredWithOutPhoneNumber) { - onVerifyPhoneNumber(jsonRes.userId); + if (loginSignUpResult.registeredWithOutPhoneNumber) { + onVerifyPhoneNumber(loginSignUpResult.userId); } else { - onOTPVerified(jsonRes.userId); + onOTPVerified(loginSignUpResult.userId); } toast({ @@ -121,12 +126,10 @@ export function OtpVerifyForm({ setIsStatusSuccess(false); toast({ - message: jsonRes.message, + message: loginSignUpResult.message, severity: 'error', }); } - - setVerifyStatusLoading(false); }; const otpMessageFn = (): string => { @@ -189,7 +192,7 @@ export function OtpVerifyForm({ onChange={(value) => setOtpCode(value)} /> - @@ -151,7 +152,7 @@ export function VerifyPhoneNumber({ diff --git a/src/features/authorization/components/ForgetPassword/ForgetPasswordOtp.tsx b/src/features/authorization/components/ForgetPassword/ForgetPasswordOtp.tsx index b4f01da..a1fd5f3 100644 --- a/src/features/authorization/components/ForgetPassword/ForgetPasswordOtp.tsx +++ b/src/features/authorization/components/ForgetPassword/ForgetPasswordOtp.tsx @@ -16,6 +16,7 @@ import { sendForgetPassCode, } from '../../api/authorizationAPI'; import { Icon, useToast } from '@rkheftan/harmony-ui'; +import { useApi } from '@/hooks/useApi'; interface ForgetPasswordOtpProps { forgettedPasswordInfo: string; @@ -35,13 +36,20 @@ export function ForgetPasswordOtp({ const [otpCode, setOtpCode] = useState(''); const [otpDigitInvalid, setOtpDigitInvalid] = useState(false); const [isStatusSuccess, setIsStatusSuccess] = useState(); - const [verifyStatusLoading, setVerifyStatusLoading] = - useState(false); const { t } = useTranslation('authentication'); const [resendTimer, setResendTimer] = useState(120); const [canResend, setCanResend] = useState(false); - const [resendLoading, setResendLoading] = useState(false); const toast = useToast(); + const { + data: sendForgetPassCodeData, + loading: sendForgetPassCodeLoading, + execute: sendForgetPassCodeCall, + } = useApi(sendForgetPassCode); + const { + data: confirmForgetPassCodeData, + loading: confirmForgetPassCodeLoading, + execute: confirmForgetPassCodeCall, + } = useApi(confirmForgetPassCode); useEffect(() => { let interval: NodeJS.Timeout; @@ -57,18 +65,15 @@ export function ForgetPasswordOtp({ }, [resendTimer]); const handleResendOTPCode = async () => { - setResendLoading(true); - const sendCodeRequest: SendForgetPassCodeRequest = { email: infoType === 'email' ? forgettedPasswordInfo : undefined, phoneNumber: infoType === 'phone' ? countryCode + forgettedPasswordInfo : undefined, }; - const result = await sendForgetPassCode(sendCodeRequest); + await sendForgetPassCodeCall(sendCodeRequest); setResendTimer(120); setCanResend(false); - setResendLoading(false); }; const formatTime = (seconds: number) => { @@ -82,7 +87,6 @@ export function ForgetPasswordOtp({ setOtpDigitInvalid(true); } else { setOtpDigitInvalid(false); - setVerifyStatusLoading(true); // Change setTimeout to api call const apiRequest: ConfirmForgetPassCodeRequest = { @@ -94,21 +98,20 @@ export function ForgetPasswordOtp({ code: otpCode, }; - const result = await confirmForgetPassCode(apiRequest); - const jsonRes = await result.json(); + confirmForgetPassCodeCall(apiRequest); - if (jsonRes.success) { + if (!confirmForgetPassCodeData) return; + + if (confirmForgetPassCodeData.success) { setIsStatusSuccess(true); onOTPVerified(otpCode); } else { setIsStatusSuccess(false); toast({ - message: jsonRes.message, + message: confirmForgetPassCodeData.message, severity: 'error', }); } - - setVerifyStatusLoading(false); } }; @@ -158,7 +161,10 @@ export function ForgetPasswordOtp({ onChange={(value) => setOtpCode(value)} /> - @@ -175,7 +181,7 @@ export function ForgetPasswordOtp({ diff --git a/src/hooks/useApi.ts b/src/hooks/useApi.ts index e5c3cee..ff7c394 100644 --- a/src/hooks/useApi.ts +++ b/src/hooks/useApi.ts @@ -1,24 +1,65 @@ -import { useState } from 'react'; +import type { ApiResponse } from '@/types/apiResponse'; +import { useToast } from '@rkheftan/harmony-ui'; +import type { AxiosError } from 'axios'; +import { useState, useEffect, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useNavigate } from 'react-router-dom'; -type ApiFunction = (params?: P) => Promise<{ data: T }>; +// Define options for the hook +interface UseApiOptions { + // If true, the API call will be executed immediately on mount + immediate?: boolean; +} -export function useApi(apiFunction: ApiFunction) { +export function useApi( + apiFunction: (...args: P) => Promise<{ data: T }>, + options: UseApiOptions = {}, +) { + const toast = useToast(); + const navigate = useNavigate(); + const { t } = useTranslation(); const [data, setData] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); - const execute = async (params?: P) => { - setLoading(true); - setError(null); - try { - const response = await apiFunction(params); - setData(response.data); - } catch (err) { - setError(err); - } finally { - setLoading(false); + const execute = useCallback( + async (...args: P) => { + setLoading(true); + setError(null); + + try { + const response = await apiFunction(...args); + + setData(response.data); + } catch (err: unknown) { + const axisoError: AxiosError = err as AxiosError; + + if (axisoError.response?.status === 401) { + navigate('/login'); + } + + if (axisoError.response?.status === 500) { + toast({ + message: t('messages.serverError'), + severity: 'error', + }); + } + + setError(err); + } finally { + setLoading(false); + } + }, + [apiFunction, navigate, t, toast], + ); + + // If the 'immediate' option is true, execute the function on mount + useEffect(() => { + if (options.immediate) { + // We pass undefined as params for the initial call. + execute(...(undefined as unknown as P)); } - }; + }, [execute, options.immediate]); return { data, loading, error, execute }; } diff --git a/src/lib/apiClient.ts b/src/lib/apiClient.ts index 76be7cf..e8f460d 100644 --- a/src/lib/apiClient.ts +++ b/src/lib/apiClient.ts @@ -1,11 +1,11 @@ import axios from 'axios'; // Function to get the token from local storage or state management -const getToken = () => localStorage.getItem('authToken'); +const getToken = () => sessionStorage.getItem('authToken'); const apiClient = axios.create({ // Define the base URL for all API requests - baseURL: 'https://accounts.business-harmony.com/swagger/index.html', + baseURL: 'https://accounts.business-harmony.com/api/', // Set a timeout for requests (e.g., 10 seconds) timeout: 10000,