feat: login with password apis added

This commit is contained in:
مهرزاد قدرتی
2025-08-10 12:21:41 +03:30
parent 08bfe5c979
commit 945aa379ea
8 changed files with 108 additions and 23 deletions

View File

@@ -12,6 +12,7 @@ import type {
LoginOrSignUpWithGoogleResponse,
LoginRequest,
LoginResponse,
PasswordLoginRequest,
ResetPasswordRequest,
ResetPasswordResponse,
SendEmailOtpRequest,
@@ -49,7 +50,7 @@ export const loginOrSignUpWithOtp = async (body: LoginRequest) => {
return fetchRequest<LoginResponse>('User/LoginOrSignUpWithOtp', body);
};
export const loginWithPassword = async (body: LoginRequest) => {
export const loginWithPassword = async (body: PasswordLoginRequest) => {
return fetchRequest<LoginResponse>('User/LoginWithPassword', body);
};

View File

@@ -5,7 +5,11 @@ import { OtpVerifyForm } from './OtpVerifyForm';
import { isNumeric } from '@/utils/regexes/isNumeric';
import { CompleteSignUp } from './CompleteSignUp';
import { EnterPasswordForm } from './EnterPasswordForm';
import { getUserStatusByPhoneNumberOrEmail } from '../../api/authorizationAPI';
import {
getUserStatusByPhoneNumberOrEmail,
sendEmailOtp,
sendSmsOtp,
} from '../../api/authorizationAPI';
import { UserStatus } from '../../types/userTypes';
import type { CountryCode, GUID } from '@/types/commonTypes';
import { VerifyPhoneNumber } from './VerifyPhoneNumber';
@@ -51,11 +55,16 @@ export const AuthenticationSteps = (): JSX.Element => {
const handleOTPVerfied = (
registeredWithoutPhoneNumber: boolean = false,
userId: GUID,
returnUrl?: string,
) => {
localStorage.setItem('userID', userId);
// if (registeredWithoutPhoneNumber) {
// setCurrentStep('addPhoneNumber');
// }
if (returnUrl) {
location.href = returnUrl;
}
};
const handleEditValue = () => {
@@ -74,7 +83,17 @@ export const AuthenticationSteps = (): JSX.Element => {
setCurrentStep('emailOrPhone');
};
const handleLoggedInWithPassowrd = () => {};
const handleLoggedInWithPassowrd = (userId: GUID, returnUrl?: string) => {
localStorage.setItem('userID', userId);
if (returnUrl) {
location.href = returnUrl;
}
};
const handleLoginWithOtpInsteadOfPassword = async () => {
setCurrentStep('verify');
};
return (
<>
@@ -103,9 +122,12 @@ export const AuthenticationSteps = (): JSX.Element => {
{currentStep === 'enterPassword' && (
<EnterPasswordForm
loginRegisterValue={loginRegisterValue}
countryCode={countryCode}
authType={authType}
onLoggedIn={handleLoggedInWithPassowrd}
onEditValue={handleEditValue}
onLoginWithOTP={() => setCurrentStep('verify')}
onLoginWithOTP={handleLoginWithOtpInsteadOfPassword}
emailOrPhone={loginRegisterValue}
/>
)}

View File

@@ -11,12 +11,24 @@ import {
} from '@mui/material';
import { useTranslation } from 'react-i18next';
import { Toast } from '@/components/Toast';
import { Link, Navigate, useSearchParams } from 'react-router';
import type { AuthType } from '../../types/authTypes';
import type { CountryCode, GUID } from '@/types/commonTypes';
import {
loginWithPassword,
sendEmailOtp,
sendSmsOtp,
} from '../../api/authorizationAPI';
import type { LoginRequest, PasswordLoginRequest } from '../../types/userTypes';
export interface EnterPasswordFormProps {
onEditValue: () => void;
onLoginWithOTP: () => void;
onLoggedIn: () => void;
onLoggedIn: (userId: GUID, returnUrl?: string) => void;
emailOrPhone: string;
authType: AuthType;
loginRegisterValue: string;
countryCode: CountryCode;
}
export const EnterPasswordForm = ({
@@ -24,7 +36,12 @@ export const EnterPasswordForm = ({
onLoginWithOTP,
onLoggedIn,
emailOrPhone,
authType,
loginRegisterValue,
countryCode,
}: EnterPasswordFormProps) => {
const DEFAULT_RETURN_URL = 'https://account.business-harmony.com/';
const [searchParams] = useSearchParams();
const { t } = useTranslation('authentication');
const [passValue, setPassValue] = useState<string>('');
const [inputTouched, setInputTouched] = useState<boolean>(false);
@@ -34,29 +51,53 @@ export const EnterPasswordForm = ({
const [loginStatus, setLoginStatus] = useState<'success' | 'failed'>();
const [loginAlertOpen, setLoginAlertOpen] = useState<boolean>(false);
const [loginFailedMessage, setLoginFailedMessage] = useState<string>('');
const [sendOtpLoading, setSendOtpLoading] = useState<boolean>(false);
const handleBlur = () => {
setInputTouched(true);
};
const handleSubmit = () => {
const handleSubmit = async () => {
if (!passValue) {
inputRef.current?.focus();
} else {
setLoginLoading(true);
// Change setTimeout to api call
setTimeout(() => {
setLoginAlertOpen(true);
// setLoginStatus('success');
const apiRequest: PasswordLoginRequest = {
phoneNumber:
authType === 'phone' ? countryCode + loginRegisterValue : undefined,
email: authType === 'email' ? loginRegisterValue : undefined,
password: passValue,
returnUrl: searchParams.get('returnUrl') ?? DEFAULT_RETURN_URL,
};
const result = await loginWithPassword(apiRequest);
const jsonRes = await result.json();
if (jsonRes.success) {
setLoginStatus('success');
onLoggedIn(jsonRes.userId, jsonRes.returnUrl);
} else {
setLoginStatus('failed');
setLoginFailedMessage('رمز عبور اشتباه میباشد');
onLoggedIn();
setLoginLoading(false);
}, 1000);
setLoginFailedMessage(jsonRes.message);
}
setLoginAlertOpen(true);
setLoginLoading(false);
}
};
const handleLoginWithOtp = async () => {
setSendOtpLoading(true);
if (authType === 'phone') {
await sendSmsOtp({ phoneNumber: countryCode + loginRegisterValue });
} else {
await sendEmailOtp({ email: loginRegisterValue });
}
setSendOtpLoading(false);
onLoginWithOTP();
};
return (
<AuthenticationCard>
<Toast
@@ -127,9 +168,10 @@ export const EnterPasswordForm = ({
/>
<Button
onClick={onLoginWithOTP}
onClick={handleLoginWithOtp}
sx={{ width: 'auto', mb: 2 }}
variant="text"
loading={sendOtpLoading}
endIcon={<ArrowLeft />}
>
{t('enterPassword.loginWithOneTimeCode')}
@@ -139,7 +181,9 @@ export const EnterPasswordForm = ({
<Button loading={loginLoading} onClick={handleSubmit}>
{t('verify.confirmAndLogin')}
</Button>
<Button variant="text">{t('enterPassword.iForgotMyPassword')}</Button>
<Link to="/forget-password">
<Button variant="text">{t('enterPassword.iForgotMyPassword')}</Button>
</Link>
</Stack>
</AuthenticationCard>
);

View File

@@ -21,7 +21,11 @@ interface OtpVerifyFormProps {
authType: AuthType;
authMode: AuthMode;
onEditValue: () => void;
onOTPVerified: (registeredWithoutPhoneNumber: boolean, userID: GUID) => void;
onOTPVerified: (
registeredWithoutPhoneNumber: boolean,
userID: GUID,
returnUrl?: string,
) => void;
}
export function OtpVerifyForm({
@@ -32,6 +36,7 @@ export function OtpVerifyForm({
onEditValue,
onOTPVerified,
}: OtpVerifyFormProps) {
const DEFAULT_RETURN_URL = 'https://account.business-harmony.com/';
const [searchParams] = useSearchParams();
const [otpCode, setOtpCode] = useState<string>('');
const [otpDigitInvalid, setOtpDigitInvalid] = useState<boolean>(false);
@@ -97,14 +102,18 @@ export function OtpVerifyForm({
otpCode: otpCode,
phoneNumber: authType === 'phone' ? countryCode + value : undefined,
email: authType === 'email' ? value : undefined,
returnUrl: searchParams.get('returnUrl') ?? '/',
returnUrl: searchParams.get('returnUrl') ?? DEFAULT_RETURN_URL,
};
const result = await loginOrSignUpWithOtp(loginRequest);
const jsonRes = await result.json();
if (jsonRes.success) {
setVerifyStatus('success');
onOTPVerified(jsonRes.registeredWithOutPhoneNumber, jsonRes.userId);
onOTPVerified(
jsonRes.registeredWithOutPhoneNumber,
jsonRes.userId,
jsonRes.returnUrl,
);
} else {
setVerifyStatus('failed');
setErrorMessage(jsonRes.message);

View File

@@ -241,7 +241,7 @@ export const ChangePassword = ({
color="primary"
onClick={() => setShowConfirmPassword(!showConfirmPassword)}
>
{showPassword ? <Eye /> : <EyeSlash />}
{showConfirmPassword ? <Eye /> : <EyeSlash />}
</IconButton>
) : (
''

View File

@@ -137,7 +137,9 @@ export function ForgetPasswordOtp({
endIcon={<Edit2 />}
onClick={onEditInfo}
>
{forgettedPasswordInfo}
{infoType === 'phone'
? countryCode + forgettedPasswordInfo
: forgettedPasswordInfo}
</Button>
</Box>

View File

@@ -28,6 +28,13 @@ export interface LoginRequest {
returnUrl: string;
}
export interface PasswordLoginRequest {
phoneNumber?: string;
email?: string;
password: string;
returnUrl: string;
}
export interface LoginResponse extends ApiResponse {
returnUrl: string;
userId: GUID;