chore: fix styles and change the apis

This commit is contained in:
Koosha Lahouti
2025-08-15 17:49:53 +03:30
parent f82fed54d5
commit fa2d2a3c73
15 changed files with 1218 additions and 1190 deletions

View File

@@ -1,50 +1,53 @@
import { useEffect, useState } from 'react';
import { Box, Typography, Button } from '@mui/material';
import { Box, Typography } from '@mui/material';
import { useTranslation } from 'react-i18next';
import Logo from '@/components/Logo';
import { PersonalInfoFields } from './PersonalInfoFields';
import { PasswordSection } from './PasswordSection';
import { EmailSection } from './EmailSection';
import { SubmitSection } from './SubmitSection';
import apiClient from '@/lib/apiClient';
import { useToast } from '@rkheftan/harmony-ui';
import { AxiosError } from 'axios';
import { regex } from '../../../utils/regex';
import { toLocaleDigits } from '../../../utils/persianDigit';
import axios, { isAxiosError } from 'axios';
import i18n from '@/config/i18n';
import { Gender } from './types'; // ✅ Added
interface TokenApiResponse {
access_token: string;
}
import { Gender } from '../types/settingForm';
import { useApi } from '@/hooks/useApi';
import {
sendEmailOtpApi,
confirmEmailOtpApi,
completeUserInformationApi,
} from '../api/userCompletion';
import {
type SendEmailOtpPayload,
type ConfirmEmailOtpPayload,
type CompleteUserInfoPayload,
} from '../types/completionFormApiTypes';
import { type ApiResponse } from '@/types/apiResponse';
export function UserCompletionForm() {
const { t } = useTranslation('completionForm');
const showToast = useToast();
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const [nationalId, setNationalId] = useState('');
const [birthDate, setBirthDate] = useState<Date | null>(null);
// ✅ Corrected section: use Gender enum
const [sex, setSex] = useState<Gender>(Gender.Female);
const [country, setCountry] = useState('');
const [showPasswordSection, setShowPasswordSection] = useState(false);
const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [showEmail, setShowEmail] = useState(false);
const [email, setEmail] = useState('');
const [codeSent, setCodeSent] = useState(false);
const [verificationCode, setVerificationCode] = useState('');
const [codeSent, setCodeSent] = useState(false);
const [buttonState, setButtonState] = useState<'default' | 'counting'>(
'default',
);
const [countdown, setCountdown] = useState(0);
const [emailVerified, setEmailVerified] = useState(false);
const [isVerifyingCode, setIsVerifyingCode] = useState(false);
const [showPasswordValidations, setShowPasswordValidations] = useState(false);
const {
hasNumber,
@@ -55,25 +58,113 @@ export function UserCompletionForm() {
correctEmail,
} = regex(password, email);
const matchPassword = password === confirmPassword;
const [showPasswordValidations, setShowPasswordValidations] = useState(false);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [success, setSuccess] = useState(false);
const { execute: sendCode, data: sendCodeData } = useApi(
async (payload: SendEmailOtpPayload) => {
const result = await sendEmailOtpApi(payload);
const conformingResult: ApiResponse = {
...result,
errorCode: result.errorCode || 0,
validations: [],
};
return { data: conformingResult };
},
);
const showToast = useToast();
const {
execute: verifyCode,
loading: isVerifyingCode,
data: verifyCodeData,
} = useApi(async (payload: ConfirmEmailOtpPayload) => {
const result = await confirmEmailOtpApi(payload);
const conformingResult: ApiResponse = {
...result,
errorCode: result.errorCode || 0,
validations: [],
};
return { data: conformingResult };
});
const {
execute: submitForm,
loading: isSubmitting,
error: submitError,
data: submitData,
} = useApi(async (payload: CompleteUserInfoPayload) => {
const result = await completeUserInformationApi(payload);
const conformingResult: ApiResponse = {
...result,
errorCode: result.errorCode || 0,
validations: [],
};
return { data: conformingResult };
});
const getErrorMessage = (error: unknown): string | null => {
if (!error) return null;
if (error instanceof Error) return error.message;
return String(error);
};
useEffect(() => {
if (password) {
if (!validPassword) {
setShowPasswordValidations(true);
if (sendCodeData) {
if (sendCodeData.success) {
showToast({
message: sendCodeData.message || t('completion.successfullCodeSent'),
severity: 'success',
});
setCodeSent(true);
setButtonState('counting');
setCountdown(120);
} else {
const timer = setTimeout(() => setShowPasswordValidations(false), 1000);
return () => clearTimeout(timer);
showToast({
message: sendCodeData.message || t('completion.problem'),
severity: 'error',
});
}
} else {
setShowPasswordValidations(false);
}
}, [sendCodeData, showToast, t]);
useEffect(() => {
if (verifyCodeData) {
if (verifyCodeData.success) {
setEmailVerified(true);
showToast({
message: verifyCodeData.message || t('completion.codeVerified'),
severity: 'success',
});
} else {
showToast({
message: verifyCodeData.message || t('completion.invalidCode'),
severity: 'error',
});
setEmailVerified(false);
}
}
}, [verifyCodeData, showToast, t]);
useEffect(() => {
if (submitData) {
showToast({
message:
submitData.message ||
t(
submitData.success
? 'completion.submitSuccess'
: 'completion.submitError',
),
severity: submitData.success ? 'success' : 'error',
});
} else if (submitError) {
showToast({
message: getErrorMessage(submitError) || t('completion.problem'),
severity: 'error',
});
}
}, [submitData, submitError, showToast, t]);
useEffect(() => {
setShowPasswordValidations(password ? !validPassword : false);
}, [password, validPassword]);
useEffect(() => {
@@ -93,6 +184,37 @@ export function UserCompletionForm() {
return () => clearInterval(timer);
}, [buttonState, countdown]);
const handleSendCode = () => {
sendCode({ email });
};
const handleVerifyCode = () => {
if (!verificationCode.trim()) {
showToast({
message: 'Please enter the verification code',
severity: 'warning',
});
return;
}
verifyCode({ email, otpCode: verificationCode });
};
const handleSubmit = () => {
submitForm({
userId: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
firstName,
lastName,
gender: sex,
nationalId,
savePassword: showPasswordSection,
password: showPasswordSection ? password : undefined,
saveEmail: showEmail,
email: showEmail ? email : undefined,
birthDate,
country,
});
};
const getButtonLabel = () => {
if (buttonState === 'counting') {
const m = String(Math.floor(countdown / 60)).padStart(2, '0');
@@ -102,142 +224,6 @@ export function UserCompletionForm() {
return t('completion.vericationCodeButton');
};
const [tokenError, setTokenError] = useState<string | null>(null);
const apiUrl = 'https://accounts.business-harmony.com';
const tokenEndpoint = `${apiUrl}/connect/token`;
const getToken = async () => {
setTokenError(null);
try {
const body = new URLSearchParams();
body.set('grant_type', 'password');
body.set('username', 'zareian.1381@gmail.com');
body.set('password', '123@Qweasd');
body.set('client_id', 'harmony_identity');
body.set('scope', 'openid harmony_identity profile offline_access');
const response = await axios.post<TokenApiResponse>(
tokenEndpoint,
body.toString(),
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
},
);
if (response.data?.access_token) {
localStorage.setItem('authToken', response.data.access_token);
} else {
throw new Error('No access token in response');
}
} catch (error: unknown) {
let message = 'Failed to get token';
if (isAxiosError(error) && error.response) {
message = `Request failed with status ${error.response.status}`;
} else if (error instanceof Error) {
message = error.message;
}
setTokenError(message);
}
};
const storedToken = localStorage.getItem('authToken');
const handleSendCode = async () => {
setError(null);
setLoading(true);
setSuccess(false);
try {
const response = await apiClient.post(
'/User/SendEmailOtp',
{ email },
{
headers: {
Authorization: `Bearer ${storedToken}`,
},
},
);
if (response.data?.success) {
showToast({
message: response.data.message || t('completion.successfullCodeSent'),
severity: 'success',
});
setCodeSent(true);
setButtonState('counting');
setCountdown(120);
} else if (response.data?.codeSentAnyway) {
showToast({
message: t('completion.codeSentBut') + (response.data.message || ''),
severity: 'warning',
});
setCodeSent(true);
setButtonState('counting');
setCountdown(120);
} else {
showToast({
message: response.data.message || t('completion.problem'),
severity: 'error',
});
}
} catch (error: unknown) {
const err = error as AxiosError<{ message?: string }>;
showToast({
message: err.response?.data?.message || t('completion.problem'),
severity: 'error',
});
} finally {
setLoading(false);
}
};
const handleVerifyCode = async () => {
if (!verificationCode.trim()) {
setError('Please enter the verification code');
return;
}
setIsVerifyingCode(true);
setError(null);
try {
const res = await apiClient.post(
'/User/ConfirmEmailOtp',
{
email,
otpCode: verificationCode,
},
{
headers: {
Authorization: `Bearer ${storedToken}`,
},
},
);
if (res.data?.success) {
setEmailVerified(true);
setSuccess(true);
showToast({
message: res.data.message || t('completion.codeVerified'),
severity: 'success',
});
} else {
showToast({
message: res.data?.message || t('completion.invalidCode'),
severity: 'error',
});
setEmailVerified(false);
}
} catch (error: unknown) {
const err = error as AxiosError<{ message?: string }>;
showToast({
message: err.response?.data?.message || '',
severity: 'error',
});
setEmailVerified(false);
} finally {
setIsVerifyingCode(false);
}
};
const handleEditEmail = () => {
setButtonState('default');
setCodeSent(false);
@@ -245,60 +231,6 @@ export function UserCompletionForm() {
setVerificationCode('');
};
const handleSubmit = async () => {
setLoading(true);
setError(null);
setSuccess(false);
try {
const { data } = await apiClient.post<{
success: boolean;
errorCode: number;
message: string;
validations: { property: string; message: string }[];
}>(
'/User/CompleteUserInformation',
{
userId: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
firstName,
lastName,
gender: sex === Gender.Female ? 2 : 1, // ✅ Corrected
nationalId,
savePassword: showPasswordSection,
password: showPasswordSection ? password : undefined,
saveEmail: showEmail,
email: showEmail ? email : undefined,
birthDate,
country,
},
{
headers: {
Authorization: `Bearer ${storedToken}`,
},
},
);
if (data.success) {
showToast({
message: data.message || t('completion.submitSuccess'),
severity: 'success',
});
} else {
showToast({
message: data.message || t('completion.submitError'),
severity: 'error',
});
}
} catch (error: unknown) {
const err = error as AxiosError<{ message?: string }>;
showToast({
message:
err.response?.data?.message || err.message || t('completion.problem'),
severity: 'error',
});
} finally {
setLoading(false);
}
};
return (
<Box
sx={{
@@ -345,8 +277,8 @@ export function UserCompletionForm() {
setNationalId={setNationalId}
birthDate={birthDate}
setBirthDate={setBirthDate}
sex={sex} // ✅ Corrected
setSex={setSex} // ✅ Corrected
sex={sex}
setSex={setSex}
country={country}
setCountry={setCountry}
/>
@@ -384,21 +316,13 @@ export function UserCompletionForm() {
loading={isVerifyingCode}
handleEditEmail={handleEditEmail}
/>
<SubmitSection
onSubmit={handleSubmit}
loading={loading}
error={error}
success={success}
loading={isSubmitting}
error={getErrorMessage(submitError)}
success={!!submitData?.success}
/>
<Button
variant="contained"
color="secondary"
onClick={getToken}
size="large"
sx={{ textTransform: 'none' }}
>
Get Token
</Button>
</Box>
</Box>
);