feat: complete sign up form added

This commit is contained in:
2025-07-27 22:26:48 +03:30
parent 5d2e05184f
commit 45371337b7
5 changed files with 157 additions and 6 deletions

View File

@@ -17,7 +17,11 @@
"theVerificationCodeIsIncorrect": "The verification code is incorrect.",
"youHaveSuccessfullyLoggedIn": "You have successfully logged in",
"youHaveSuccessfullySignedIn": "You have successfully signed in"
},
"completeSignUp": {
"completeSignUp": "Complete Sign Up",
"emailHasBeenSuccessfullyVerifiedPleaseEnterYourContactNumberToContinue": "Email {{ email }} has been successfully verified. Please enter your contact number to continue.",
"phoneNumber": "Phone number"
}
}
}

View File

@@ -20,5 +20,10 @@
"theVerificationCodeIsIncorrect": "کد تایید اشتباه می باشد",
"youHaveSuccessfullyLoggedIn": "با موفقیت وارد شدید",
"youHaveSuccessfullySignedIn": "ثبت نام با موفقیت انجام شد"
},
"completeSignUp": {
"completeSignUp": "تکمیل ثبت نام",
"emailHasBeenSuccessfullyVerifiedPleaseEnterYourContactNumberToContinue": "ایمیل {{ email }} با موفقیت تایید شد. برای ادامه لطفا شماره تماس خود را وارد کنید",
"phoneNumber": "شماره تماس"
}
}

View File

@@ -3,14 +3,21 @@ import { LoginRegisterForm } from './LoginRegiserForm';
import type { AuthMode, AuthType } from '../types/auth-types';
import { OtpVerifyForm } from './OtpVerifyForm';
import { isNumeric } from '@/utils/regexes/isNumeric';
import { CompleteSignUp } from './CompleteSignUp';
export const AuthenticationContainer = (): JSX.Element => {
const [authMode, setAuthMode] = useState<AuthMode>('login');
const [authMode, setAuthMode] = useState<AuthMode>('register');
const [authType, setAuthType] = useState<AuthType>('phone');
const [currentStep, setCurrentStep] = useState<
'emailOrPassword' | 'verify' | 'enterPassword' | 'addPhoneNumber'
| 'emailOrPassword'
| 'verify'
| 'enterPassword'
| 'addPhoneNumber'
| 'addedPhoneNumberVerify'
>('emailOrPassword');
const [loginRegisterValue, setLoginRegisterValue] = useState<string>('');
const [addedPhoneNumberValue, setAddedPhoneNumberValue] =
useState<string>('');
const handleLoginRegister = (value: string) => {
setLoginRegisterValue(value);
@@ -19,8 +26,6 @@ export const AuthenticationContainer = (): JSX.Element => {
};
const handleOTPVerfied = (otpCode: string) => {
console.log(otpCode);
if (authMode === 'register' && authType === 'email') {
setAuthType('phone');
setCurrentStep('addPhoneNumber');
@@ -31,6 +36,18 @@ export const AuthenticationContainer = (): JSX.Element => {
setCurrentStep('emailOrPassword');
};
const handleCompleteSignUp = (countryCode: string, value: string) => {
setCurrentStep('addedPhoneNumberVerify');
};
const handleCompleteSignUpOTPVerified = (otpCode: string) => {
console.log(otpCode);
};
const handleCompleteSignUpEditValue = () => {
setCurrentStep('addPhoneNumber');
};
return (
<>
{currentStep === 'emailOrPassword' && (
@@ -52,6 +69,25 @@ export const AuthenticationContainer = (): JSX.Element => {
value={loginRegisterValue}
/>
)}
{currentStep === 'addPhoneNumber' && (
<CompleteSignUp
value={addedPhoneNumberValue}
setValue={setAddedPhoneNumberValue}
email={loginRegisterValue}
onCompleteSignUp={handleCompleteSignUp}
/>
)}
{currentStep === 'addedPhoneNumberVerify' && (
<OtpVerifyForm
onOTPVerified={handleCompleteSignUpOTPVerified}
onEditValue={handleCompleteSignUpEditValue}
authMode="register"
authType="phone"
value={addedPhoneNumberValue}
/>
)}
</>
);
};

View File

@@ -0,0 +1,106 @@
import { Box, Button, TextField, Typography } from '@mui/material';
import parsePhoneNumberFromString from 'libphonenumber-js';
import React, { useRef, useState, type Dispatch } from 'react';
import { useTranslation } from 'react-i18next';
import { CountryCodeSelector } from './CountryCodeSelector';
export interface CompleteSignUpProps {
email: string;
value: string;
setValue: Dispatch<string>;
onCompleteSignUp: (countryCode: string, value: string) => void;
}
export const CompleteSignUp = ({
email,
value,
setValue,
onCompleteSignUp,
}: CompleteSignUpProps) => {
const { t } = useTranslation('authentication');
const [countryCode, setCountryCode] = useState('+98');
const [error, setError] = useState<string>();
const textFieldRef = useRef<HTMLDivElement>(null);
const inputRef = useRef<HTMLInputElement>(null);
const [touched, setTouched] = useState<boolean>(false);
const inputError: boolean = touched && !!error;
const isPhoneValid = (code: string, phone: string) => {
const phoneNumber = parsePhoneNumberFromString(code + phone);
return phoneNumber && phoneNumber.isValid();
};
const handleBlur = () => {
setTouched(true);
if (!value) {
setError(t('loginForm.thisFieldIsRequired'));
}
if (!isPhoneValid(countryCode, value)) {
setError(t('loginForm.phoneNumberIsInvalid'));
} else {
setError(undefined);
}
};
const handleCompleteSignUp = () => {
if (!value) {
setError(t('loginForm.thisFieldIsRequired'));
inputRef.current?.focus();
}
if (!isPhoneValid(countryCode, value)) {
setError(t('loginForm.phoneNumberIsInvalid'));
inputRef.current?.focus();
} else {
setError(undefined);
onCompleteSignUp(countryCode, value);
}
};
return (
<Box sx={{ width: '100%' }}>
<Typography variant="h5" sx={{ mb: 0.5 }}>
{t('completeSignUp.completeSignUp')}
</Typography>
<Typography variant="body2" color="textSecondary" sx={{ mt: 1, mb: 2 }}>
{t(
'completeSignUp.emailHasBeenSuccessfullyVerifiedPleaseEnterYourContactNumberToContinue',
{ email },
)}
</Typography>
<TextField
ref={textFieldRef}
inputRef={inputRef}
label={t('completeSignUp.phoneNumber')}
value={value}
onChange={(e) => setValue(e.target.value)}
onBlur={handleBlur}
error={inputError}
helperText={inputError ? error : ''}
autoFocus
slotProps={{
htmlInput: { dir: 'auto', sx: { lineHeight: 1.5, paddingX: 0 } },
input: {
endAdornment: (
<CountryCodeSelector
value={countryCode}
onChange={setCountryCode}
show={true}
menuAnchor={textFieldRef.current}
onCloseFocusRef={inputRef}
/>
),
},
}}
sx={{ my: 4 }}
/>
<Button onClick={handleCompleteSignUp}>
{t('verify.confirmAndContinue')}
</Button>
</Box>
);
};

View File

@@ -46,7 +46,7 @@ export const CustomThemeProvider: React.FC<{ children: React.ReactNode }> = ({
}, [i18n]);
return (
<ThemeProvider theme={theme} defaultMode="system">
<ThemeProvider theme={theme} defaultMode="light">
{children}
</ThemeProvider>
);