forget pass apis added
This commit is contained in:
@@ -3,6 +3,7 @@ import './App.css';
|
||||
import { LanguageManager } from './components/LanguageManager';
|
||||
import { AuthenticationPage } from './features/authorization/routes/AuthenticationPage';
|
||||
import { BrowserRouter, Navigate, Route, Routes } from 'react-router';
|
||||
import { ForgetPasswordPage } from './features/authorization/routes/ForgetPasswordPage';
|
||||
|
||||
function App() {
|
||||
return (
|
||||
@@ -13,6 +14,7 @@ function App() {
|
||||
<Routes>
|
||||
<Route path="/" element={<Navigate to="auth" />} />
|
||||
<Route path="/auth" element={<AuthenticationPage />} />
|
||||
<Route path="/forget-password" element={<ForgetPasswordPage />} />
|
||||
</Routes>
|
||||
</BrowserRouter>
|
||||
,
|
||||
|
||||
@@ -77,7 +77,7 @@ export const sendForgetPassCode = async (body: SendForgetPassCodeRequest) => {
|
||||
return fetchRequest<ApiResponse>('User/SendForgetPassCode', body);
|
||||
};
|
||||
|
||||
export const ConfirmForgetPassCode = async (
|
||||
export const confirmForgetPassCode = async (
|
||||
body: ConfirmForgetPassCodeRequest,
|
||||
) => {
|
||||
return fetchRequest<ConfirmOtpResponse>('User/ConfirmForgetPassCode', body);
|
||||
|
||||
@@ -7,7 +7,7 @@ import { CompleteSignUp } from './CompleteSignUp';
|
||||
import { EnterPasswordForm } from './EnterPasswordForm';
|
||||
import { getUserStatusByPhoneNumberOrEmail } from '../../api/authorizationAPI';
|
||||
import { UserStatus } from '../../types/userTypes';
|
||||
import type { CountryCode } from '@/types/commonTypes';
|
||||
import type { CountryCode, GUID } from '@/types/commonTypes';
|
||||
import { VerifyPhoneNumber } from './VerifyPhoneNumber';
|
||||
|
||||
export const AuthenticationSteps = (): JSX.Element => {
|
||||
@@ -48,7 +48,11 @@ export const AuthenticationSteps = (): JSX.Element => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleOTPVerfied = (registeredWithoutPhoneNumber: boolean = false) => {
|
||||
const handleOTPVerfied = (
|
||||
registeredWithoutPhoneNumber: boolean = false,
|
||||
userId: GUID,
|
||||
) => {
|
||||
localStorage.setItem('userID', userId);
|
||||
// if (registeredWithoutPhoneNumber) {
|
||||
// setCurrentStep('addPhoneNumber');
|
||||
// }
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
sendEmailOtp,
|
||||
sendSmsOtp,
|
||||
} from '../../api/authorizationAPI';
|
||||
import type { CountryCode } from '@/types/commonTypes';
|
||||
import type { CountryCode, GUID } from '@/types/commonTypes';
|
||||
|
||||
interface OtpVerifyFormProps {
|
||||
value: string;
|
||||
@@ -21,7 +21,7 @@ interface OtpVerifyFormProps {
|
||||
authType: AuthType;
|
||||
authMode: AuthMode;
|
||||
onEditValue: () => void;
|
||||
onOTPVerified: (registeredWithoutPhoneNumber: boolean) => void;
|
||||
onOTPVerified: (registeredWithoutPhoneNumber: boolean, userID: GUID) => void;
|
||||
}
|
||||
|
||||
export function OtpVerifyForm({
|
||||
@@ -104,7 +104,7 @@ export function OtpVerifyForm({
|
||||
|
||||
if (jsonRes.success) {
|
||||
setVerifyStatus('success');
|
||||
onOTPVerified(jsonRes.registeredWithOutPhoneNumber);
|
||||
onOTPVerified(jsonRes.registeredWithOutPhoneNumber, jsonRes.userId);
|
||||
} else {
|
||||
setVerifyStatus('failed');
|
||||
setErrorMessage(jsonRes.message);
|
||||
|
||||
@@ -23,17 +23,25 @@ import { containsNumber } from '@/utils/regexes/containsNumber';
|
||||
import { containsSymbol } from '@/utils/regexes/containsSymbol';
|
||||
import { least8Chars } from '@/utils/regexes/least8Chars';
|
||||
import { hasUpperAndLowerLetter } from '@/utils/regexes/hasUpperAndLowerLetter';
|
||||
import type { ResetPasswordRequest } from '../../types/userTypes';
|
||||
import type { AuthType } from '../../types/authTypes';
|
||||
import type { CountryCode } from '@/types/commonTypes';
|
||||
import { resetPassword } from '../../api/authorizationAPI';
|
||||
|
||||
export interface ChangePasswordProps {
|
||||
onEditInfo: () => void;
|
||||
onPasswordChanged: () => void;
|
||||
forgettedPasswordInfo: string;
|
||||
infoType: AuthType;
|
||||
countryCode: CountryCode;
|
||||
}
|
||||
|
||||
export const ChangePassword = ({
|
||||
onEditInfo,
|
||||
onPasswordChanged,
|
||||
forgettedPasswordInfo,
|
||||
infoType,
|
||||
countryCode,
|
||||
}: ChangePasswordProps) => {
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation('authentication');
|
||||
@@ -75,7 +83,7 @@ export const ChangePassword = ({
|
||||
setConfirmInputTouched(true);
|
||||
};
|
||||
|
||||
const handleSubmit = () => {
|
||||
const handleSubmit = async () => {
|
||||
if (!passValue || !isValidPassword(passValue)) {
|
||||
setInputTouched(true);
|
||||
inputRef.current?.focus();
|
||||
@@ -85,15 +93,29 @@ export const ChangePassword = ({
|
||||
} else {
|
||||
setChangePasswordLoading(true);
|
||||
|
||||
// Change setTimeout to api call
|
||||
setTimeout(() => {
|
||||
setChangePassAlertOpen(true);
|
||||
// setLoginStatus('success');
|
||||
// setLoginStatus('failed');
|
||||
// setLoginFailedMessage('رمز عبور اشتباه میباشد');
|
||||
const apiRequest: ResetPasswordRequest = {
|
||||
email: infoType === 'email' ? forgettedPasswordInfo : undefined,
|
||||
phoneNumber:
|
||||
infoType === 'phone'
|
||||
? countryCode + forgettedPasswordInfo
|
||||
: undefined,
|
||||
newPassword: passValue,
|
||||
confirmNewPassword: confirmPassValue,
|
||||
};
|
||||
|
||||
const result = await resetPassword(apiRequest);
|
||||
const jsonRes = await result.json();
|
||||
|
||||
if (jsonRes.success) {
|
||||
setChangePasswordStatus('success');
|
||||
onPasswordChanged();
|
||||
setChangePasswordLoading(false);
|
||||
}, 1000);
|
||||
} else {
|
||||
setChangePasswordStatus('failed');
|
||||
setChangePassFailedMessage(jsonRes.message);
|
||||
}
|
||||
setChangePassAlertOpen(true);
|
||||
|
||||
setChangePasswordLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { AuthType } from '../../types/authTypes';
|
||||
import { ForgettedPasswordInfo } from './ForgettedPasswordInfo';
|
||||
import { ForgetPasswordOtp } from './ForgetPasswordOtp';
|
||||
import { ChangePassword } from './ChangePassword';
|
||||
import type { CountryCode } from '@/types/commonTypes';
|
||||
|
||||
export const ForgetPasswordContainer = () => {
|
||||
const [forgetPassCurrentStep, setForgetPassCurrentStep] = useState<
|
||||
@@ -10,10 +11,10 @@ export const ForgetPasswordContainer = () => {
|
||||
>('enterInfo');
|
||||
const [forgettedPasswordInfo, setForgettedPasswordInfo] =
|
||||
useState<string>('');
|
||||
const [infoCountryCode, setInfoCountryCode] = useState<CountryCode>('+98');
|
||||
const [infoType, setInfoType] = useState<AuthType>('email');
|
||||
|
||||
const handleSendForgetPassOtp = (value: string) => {
|
||||
console.log(value);
|
||||
const handleVerifyOtp = (value: string) => {
|
||||
setForgetPassCurrentStep('verifyOtp');
|
||||
};
|
||||
|
||||
@@ -37,12 +38,15 @@ export const ForgetPasswordContainer = () => {
|
||||
setInfoType={setInfoType}
|
||||
forgettedPasswordInfo={forgettedPasswordInfo}
|
||||
setForgettedPasswordInfo={setForgettedPasswordInfo}
|
||||
onSendOtp={handleSendForgetPassOtp}
|
||||
onVerifyOtp={handleVerifyOtp}
|
||||
countryCode={infoCountryCode}
|
||||
setCountryCode={setInfoCountryCode}
|
||||
/>
|
||||
)}
|
||||
|
||||
{forgetPassCurrentStep === 'verifyOtp' && (
|
||||
<ForgetPasswordOtp
|
||||
countryCode={infoCountryCode}
|
||||
infoType={infoType}
|
||||
onEditInfo={handleEditInfo}
|
||||
forgettedPasswordInfo={forgettedPasswordInfo}
|
||||
@@ -55,6 +59,8 @@ export const ForgetPasswordContainer = () => {
|
||||
onEditInfo={handleEditInfo}
|
||||
forgettedPasswordInfo={forgettedPasswordInfo}
|
||||
onPasswordChanged={handlePasswordChanged}
|
||||
infoType={infoType}
|
||||
countryCode={infoCountryCode}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -6,10 +6,14 @@ import type { AuthMode, AuthType } from '../../types/authTypes';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Toast } from '@/components/Toast';
|
||||
import { AuthenticationCard } from '../AuthenticationCard';
|
||||
import type { ConfirmForgetPassCodeRequest } from '../../types/userTypes';
|
||||
import type { CountryCode } from '@/types/commonTypes';
|
||||
import { confirmForgetPassCode } from '../../api/authorizationAPI';
|
||||
|
||||
interface ForgetPasswordOtpProps {
|
||||
forgettedPasswordInfo: string;
|
||||
infoType: AuthType;
|
||||
countryCode: CountryCode;
|
||||
onEditInfo: () => void;
|
||||
onOTPVerified: (otpCode: string) => void;
|
||||
}
|
||||
@@ -17,6 +21,7 @@ interface ForgetPasswordOtpProps {
|
||||
export function ForgetPasswordOtp({
|
||||
forgettedPasswordInfo,
|
||||
infoType,
|
||||
countryCode,
|
||||
onEditInfo,
|
||||
onOTPVerified,
|
||||
}: ForgetPasswordOtpProps) {
|
||||
@@ -25,7 +30,7 @@ export function ForgetPasswordOtp({
|
||||
const [verifyStatus, setVerifyStatus] = useState<'failed' | 'success'>();
|
||||
const [verifyStatusLoading, setVerifyStatusLoading] =
|
||||
useState<boolean>(false);
|
||||
const [verifyAlertOpen, setVerifyAlertOpen] = useState<boolean>(false);
|
||||
const [verifyAlertMessage, setVerifyAlertMessage] = useState<string>();
|
||||
const { t } = useTranslation('authentication');
|
||||
const [resendTimer, setResendTimer] = useState<number>(120);
|
||||
const [canResend, setCanResend] = useState(false);
|
||||
@@ -70,7 +75,7 @@ export function ForgetPasswordOtp({
|
||||
setOtpCode(formattedValue);
|
||||
};
|
||||
|
||||
const handleVerifyOTP = () => {
|
||||
const handleVerifyOTP = async () => {
|
||||
if (!otpCode || otpCode.length < 4) {
|
||||
setOtpDigitInvalid(true);
|
||||
} else {
|
||||
@@ -78,11 +83,25 @@ export function ForgetPasswordOtp({
|
||||
setVerifyStatusLoading(true);
|
||||
|
||||
// Change setTimeout to api call
|
||||
setTimeout(() => {
|
||||
setVerifyAlertOpen(false);
|
||||
const apiRequest: ConfirmForgetPassCodeRequest = {
|
||||
email: infoType === 'email' ? forgettedPasswordInfo : undefined,
|
||||
phoneNumber:
|
||||
infoType === 'phone'
|
||||
? countryCode + forgettedPasswordInfo
|
||||
: undefined,
|
||||
code: otpCode,
|
||||
};
|
||||
|
||||
const result = await confirmForgetPassCode(apiRequest);
|
||||
const jsonRes = await result.json();
|
||||
|
||||
if (jsonRes.success) {
|
||||
onOTPVerified(otpCode);
|
||||
setVerifyStatusLoading(false);
|
||||
}, 1000);
|
||||
} else {
|
||||
setVerifyAlertMessage(jsonRes.message);
|
||||
}
|
||||
|
||||
setVerifyStatusLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -90,11 +109,11 @@ export function ForgetPasswordOtp({
|
||||
<Stack alignItems="center">
|
||||
<AuthenticationCard>
|
||||
<Toast
|
||||
open={verifyAlertOpen}
|
||||
onClose={() => setVerifyAlertOpen(false)}
|
||||
open={!!verifyAlertMessage}
|
||||
onClose={() => setVerifyAlertMessage(undefined)}
|
||||
color={'error'}
|
||||
>
|
||||
{t('verify.theVerificationCodeIsIncorrect')}
|
||||
{verifyAlertMessage}
|
||||
</Toast>
|
||||
|
||||
<Box
|
||||
|
||||
@@ -15,13 +15,19 @@ import { isEmail } from '@/utils/regexes/isEmail';
|
||||
import parsePhoneNumberFromString from 'libphonenumber-js';
|
||||
import { AuthenticationCard } from '../AuthenticationCard';
|
||||
import { CountryCodeSelector } from '../CountryCodeSelector';
|
||||
import type { CountryCode } from '@/types/commonTypes';
|
||||
import { sendForgetPassCode } from '../../api/authorizationAPI';
|
||||
import type { SendForgetPassCodeRequest } from '../../types/userTypes';
|
||||
import { Toast } from '@/components/Toast';
|
||||
|
||||
export interface ForgettedPasswordInfoProps {
|
||||
forgettedPasswordInfo: string;
|
||||
setForgettedPasswordInfo: Dispatch<string>;
|
||||
infoType: AuthType;
|
||||
setInfoType: Dispatch<AuthType>;
|
||||
onSendOtp: (value: string) => void;
|
||||
onVerifyOtp: (value: string) => void;
|
||||
countryCode: CountryCode;
|
||||
setCountryCode: Dispatch<CountryCode>;
|
||||
}
|
||||
|
||||
export function ForgettedPasswordInfo({
|
||||
@@ -29,15 +35,18 @@ export function ForgettedPasswordInfo({
|
||||
setForgettedPasswordInfo,
|
||||
infoType,
|
||||
setInfoType,
|
||||
onSendOtp,
|
||||
onVerifyOtp,
|
||||
countryCode,
|
||||
setCountryCode,
|
||||
}: ForgettedPasswordInfoProps) {
|
||||
const { t, i18n } = useTranslation('authentication');
|
||||
const [countryCode, setCountryCode] = useState('+98');
|
||||
const textFieldRef = useRef<HTMLDivElement>(null);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const dir = i18n.dir();
|
||||
const [error, setError] = useState<string>();
|
||||
const [touched, setTouched] = useState<boolean>(false);
|
||||
const [errorMessage, setErrorMessage] = useState<string>();
|
||||
const [sendCodeLoading, setSendCodeLoading] = useState<boolean>(false);
|
||||
const inputError: boolean = touched && !!error;
|
||||
|
||||
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
@@ -75,25 +84,42 @@ export function ForgettedPasswordInfo({
|
||||
return phoneNumber && phoneNumber.isValid();
|
||||
};
|
||||
|
||||
const isInputValid = (value: string, authType: AuthType): boolean => {
|
||||
const isInputValid = (value: string, infoType: AuthType): boolean => {
|
||||
if (!value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (authType === 'email' && !isEmail(value)) {
|
||||
if (infoType === 'email' && !isEmail(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (authType === 'phone' && !isPhoneValid(countryCode, value)) {
|
||||
if (infoType === 'phone' && !isPhoneValid(countryCode, value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const handleSubmit = () => {
|
||||
const handleSubmit = async () => {
|
||||
if (isInputValid(forgettedPasswordInfo, infoType)) {
|
||||
onSendOtp(forgettedPasswordInfo);
|
||||
setSendCodeLoading(true);
|
||||
|
||||
const sendCodeRequest: SendForgetPassCodeRequest = {
|
||||
email: infoType === 'email' ? forgettedPasswordInfo : undefined,
|
||||
phoneNumber:
|
||||
infoType === 'phone'
|
||||
? countryCode + forgettedPasswordInfo
|
||||
: undefined,
|
||||
};
|
||||
const result = await sendForgetPassCode(sendCodeRequest);
|
||||
const jsonRes = await result.json();
|
||||
|
||||
if (!jsonRes.success) {
|
||||
setErrorMessage(jsonRes.message);
|
||||
}
|
||||
|
||||
setSendCodeLoading(false);
|
||||
onVerifyOtp(forgettedPasswordInfo);
|
||||
} else {
|
||||
inputRef.current?.focus();
|
||||
validateInput(forgettedPasswordInfo, infoType);
|
||||
@@ -105,6 +131,14 @@ export function ForgettedPasswordInfo({
|
||||
|
||||
return (
|
||||
<AuthenticationCard>
|
||||
<Toast
|
||||
color="error"
|
||||
onClose={() => setErrorMessage(undefined)}
|
||||
open={!!errorMessage}
|
||||
>
|
||||
{errorMessage}
|
||||
</Toast>
|
||||
|
||||
<Stack spacing={1}>
|
||||
<Typography variant="h5">
|
||||
{t('forgetPassword.forgetPassword')}
|
||||
@@ -144,7 +178,9 @@ export function ForgettedPasswordInfo({
|
||||
/>
|
||||
|
||||
<Stack spacing={2}>
|
||||
<Button onClick={handleSubmit}>{t('forgetPassword.confirm')}</Button>
|
||||
<Button loading={sendCodeLoading} onClick={handleSubmit}>
|
||||
{t('forgetPassword.confirm')}
|
||||
</Button>
|
||||
</Stack>
|
||||
</AuthenticationCard>
|
||||
);
|
||||
|
||||
23
src/features/authorization/routes/ForgetPasswordPage.tsx
Normal file
23
src/features/authorization/routes/ForgetPasswordPage.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import { FlexBox } from '@/components/components/common/FlexBox';
|
||||
import Logo from '@/components/Logo';
|
||||
import { Paper } from '@mui/material';
|
||||
import { useState } from 'react';
|
||||
import { AuthenticationSteps } from '../components/AuthenticationSteps/AuthenticationSteps';
|
||||
import { ForgetPasswordContainer } from '../components/ForgetPassword/ForgetPasswordContainer';
|
||||
|
||||
export function ForgetPasswordPage() {
|
||||
return (
|
||||
<FlexBox
|
||||
direction="column"
|
||||
align="center"
|
||||
justify="center"
|
||||
sx={{
|
||||
minHeight: '100vh',
|
||||
gap: 3,
|
||||
}}
|
||||
>
|
||||
<Logo />
|
||||
<ForgetPasswordContainer />
|
||||
</FlexBox>
|
||||
);
|
||||
}
|
||||
@@ -86,8 +86,8 @@ export interface SendForgetPassCodeRequest {
|
||||
// ConfirmForgetPassCode
|
||||
|
||||
export interface ConfirmForgetPassCodeRequest {
|
||||
email: string;
|
||||
phoneNumber: string;
|
||||
email?: string;
|
||||
phoneNumber?: string;
|
||||
code: string;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user