198 lines
5.5 KiB
TypeScript
198 lines
5.5 KiB
TypeScript
import { useRef, useState } from 'react';
|
|
import { AuthenticationCard } from '../AuthenticationCard';
|
|
import { ArrowLeft, Edit2, Eye, EyeSlash } from 'iconsax-react';
|
|
import {
|
|
Box,
|
|
Button,
|
|
IconButton,
|
|
Stack,
|
|
TextField,
|
|
Typography,
|
|
} from '@mui/material';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { Link } from 'react-router-dom';
|
|
import type { AuthType } from '../../types/authTypes';
|
|
import type { CountryCode, GUID } from '@/types/commonTypes';
|
|
import {
|
|
loginWithPassword,
|
|
sendEmailOtp,
|
|
sendSmsOtp,
|
|
} from '../../api/authorizationAPI';
|
|
import type { LoginResult, PasswordLoginRequest } from '../../types/userTypes';
|
|
import { Icon, useToast } from '@rkheftan/harmony-ui';
|
|
import { useApi } from '@/hooks/useApi';
|
|
import { useAuth } from '@/hooks/useAuth';
|
|
import { generateTokenWithPassword } from '../../api/identityAPI';
|
|
|
|
export interface EnterPasswordFormProps {
|
|
onEditValue: () => void;
|
|
onLoginWithOTP: () => void;
|
|
onLoggedIn: (loginResult: LoginResult) => void;
|
|
emailOrPhone: string;
|
|
authType: AuthType;
|
|
loginRegisterValue: string;
|
|
countryCode: CountryCode;
|
|
authReturnUrl: string;
|
|
}
|
|
|
|
export const EnterPasswordForm = ({
|
|
onEditValue,
|
|
onLoginWithOTP,
|
|
onLoggedIn,
|
|
emailOrPhone,
|
|
authType,
|
|
loginRegisterValue,
|
|
countryCode,
|
|
authReturnUrl,
|
|
}: EnterPasswordFormProps) => {
|
|
const { t } = useTranslation('authentication');
|
|
const [passValue, setPassValue] = useState<string>('');
|
|
const [inputTouched, setInputTouched] = useState<boolean>(false);
|
|
const [showPassword, setShowPassword] = useState<boolean>(false);
|
|
const inputRef = useRef<HTMLInputElement>(null);
|
|
const toast = useToast();
|
|
const { loading: smsResendLoading, execute: smsResendCall } =
|
|
useApi(sendSmsOtp);
|
|
const { loading: emailResendLoading, execute: emailResendCall } =
|
|
useApi(sendEmailOtp);
|
|
const { loading: loginWithPassLoading, execute: loginWithPassCall } =
|
|
useApi(loginWithPassword);
|
|
const auth = useAuth();
|
|
|
|
const handleBlur = () => {
|
|
setInputTouched(true);
|
|
};
|
|
|
|
const handleSubmit = async () => {
|
|
if (!passValue) {
|
|
inputRef.current?.focus();
|
|
} else {
|
|
const apiRequest: PasswordLoginRequest = {
|
|
phoneNumber:
|
|
authType === 'phone' ? countryCode + loginRegisterValue : undefined,
|
|
email: authType === 'email' ? loginRegisterValue : undefined,
|
|
password: passValue,
|
|
returnUrl: authReturnUrl,
|
|
};
|
|
const res = await loginWithPassCall(apiRequest);
|
|
|
|
if (!res) return;
|
|
|
|
if (res.success) {
|
|
const tokenRes = await generateTokenWithPassword({
|
|
username: apiRequest.email ?? (apiRequest.phoneNumber as string),
|
|
password: apiRequest.password,
|
|
});
|
|
auth.login({
|
|
...tokenRes.data,
|
|
});
|
|
|
|
onLoggedIn(res);
|
|
toast({
|
|
message: t('verify.youHaveSuccessfullyLoggedIn'),
|
|
severity: 'success',
|
|
});
|
|
} else {
|
|
toast({
|
|
message: res.message,
|
|
severity: 'error',
|
|
});
|
|
}
|
|
}
|
|
};
|
|
|
|
const handleLoginWithOtp = async () => {
|
|
if (authType === 'phone') {
|
|
await smsResendCall({ phoneNumber: countryCode + loginRegisterValue });
|
|
} else {
|
|
await emailResendCall({ email: loginRegisterValue });
|
|
}
|
|
|
|
onLoginWithOTP();
|
|
};
|
|
|
|
return (
|
|
<AuthenticationCard>
|
|
<Box
|
|
sx={{
|
|
display: 'flex',
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
justifyContent: 'space-between',
|
|
gap: 4,
|
|
mb: 0.5,
|
|
}}
|
|
>
|
|
<Typography variant="h5">
|
|
{t('enterPassword.loginWithPassword')}
|
|
</Typography>
|
|
|
|
<Button
|
|
variant="outlined"
|
|
size="large"
|
|
sx={{ width: 'auto' }}
|
|
endIcon={<Icon Component={Edit2} />}
|
|
onClick={onEditValue}
|
|
>
|
|
{emailOrPhone}
|
|
</Button>
|
|
</Box>
|
|
|
|
<Typography variant="body2" color="textSecondary" sx={{ mt: 1, mb: 2 }}>
|
|
{t('enterPassword.enterThePasswordYouSetForYourAccount')}
|
|
</Typography>
|
|
|
|
<TextField
|
|
label={t('enterPassword.loginPassword')}
|
|
type={showPassword ? 'text' : 'password'}
|
|
value={passValue}
|
|
inputRef={inputRef}
|
|
onChange={(e) => setPassValue(e.target.value)}
|
|
onBlur={handleBlur}
|
|
error={!passValue && inputTouched}
|
|
helperText={
|
|
!passValue && inputTouched ? t('loginForm.thisFieldIsRequired') : ''
|
|
}
|
|
autoFocus
|
|
slotProps={{
|
|
htmlInput: { sx: { lineHeight: 1.5 } },
|
|
input: {
|
|
endAdornment: (
|
|
<IconButton
|
|
color="primary"
|
|
onClick={() => setShowPassword(!showPassword)}
|
|
>
|
|
{showPassword ? (
|
|
<Icon Component={Eye} />
|
|
) : (
|
|
<Icon Component={EyeSlash} />
|
|
)}
|
|
</IconButton>
|
|
),
|
|
},
|
|
}}
|
|
sx={{ my: 4 }}
|
|
/>
|
|
|
|
<Button
|
|
onClick={handleLoginWithOtp}
|
|
sx={{ width: 'auto', mb: 2 }}
|
|
variant="text"
|
|
loading={emailResendLoading || smsResendLoading}
|
|
endIcon={<Icon Component={ArrowLeft} />}
|
|
>
|
|
{t('enterPassword.loginWithOneTimeCode')}
|
|
</Button>
|
|
|
|
<Stack spacing={1}>
|
|
<Button loading={loginWithPassLoading} onClick={handleSubmit}>
|
|
{t('verify.confirmAndLogin')}
|
|
</Button>
|
|
<Link to="/forget-password">
|
|
<Button variant="text">{t('enterPassword.iForgotMyPassword')}</Button>
|
|
</Link>
|
|
</Stack>
|
|
</AuthenticationCard>
|
|
);
|
|
};
|