chore: change styles and add Api for user profile
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useState, useRef } from 'react';
|
||||
import { useState, useRef, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import parsePhoneNumberFromString from 'libphonenumber-js';
|
||||
import { PageWrapper } from '../PageWrapper';
|
||||
@@ -6,9 +6,32 @@ import { CardContainer } from '@/components/CardContainer';
|
||||
import PhoneDisplay from './phoneNumber/PhoneDisplay';
|
||||
import PhoneEditForm from './phoneNumber/PhoneEditForm';
|
||||
import PhoneActionButtons from './phoneNumber/PhoneActionButtons';
|
||||
import apiClient from '@/lib/apiClient';
|
||||
import { CircularProgress, Box, Typography } from '@mui/material';
|
||||
|
||||
interface Phone {
|
||||
phone: string;
|
||||
time: string;
|
||||
withCode: string;
|
||||
}
|
||||
|
||||
interface GetProfileApiResponse {
|
||||
success: boolean;
|
||||
message?: string;
|
||||
phoneNumber?: string;
|
||||
}
|
||||
|
||||
interface ApiResponse {
|
||||
success: boolean;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
interface ConfirmApiResponse extends ApiResponse {
|
||||
confirm?: boolean;
|
||||
}
|
||||
|
||||
export function PhoneNumber() {
|
||||
const { t } = useTranslation('profileSetting');
|
||||
const { t } = useTranslation('setting');
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const [phoneNumber, setPhoneNumber] = useState('');
|
||||
const [verificationCode, setVerificationCode] = useState('');
|
||||
@@ -18,15 +41,60 @@ export function PhoneNumber() {
|
||||
);
|
||||
const [isVerifying, setIsVerifying] = useState(false);
|
||||
const [isVerified, setIsVerified] = useState(false);
|
||||
const [phones, setPhone] = useState([
|
||||
{ phone: '09123456789', time: '۱ ماه پیش', withCode: '+989123456789' },
|
||||
]);
|
||||
const [phones, setPhone] = useState<Phone[]>([]);
|
||||
const [countryCode, setCountryCode] = useState('+98');
|
||||
const textFieldRef = useRef<HTMLDivElement>(null);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const [error, setError] = useState<string>();
|
||||
const [touched, setTouched] = useState<boolean>(false);
|
||||
const inputError: boolean = touched && !!error;
|
||||
const token = localStorage.getItem('authToken');
|
||||
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [fetchError, setFetchError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchPhoneNumber = async () => {
|
||||
setIsLoading(true);
|
||||
setFetchError(null);
|
||||
if (!token) {
|
||||
setIsLoading(false);
|
||||
setFetchError(t('settingForm.notLoggedIn'));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const res = await apiClient.post<GetProfileApiResponse>(
|
||||
'/Profile/GetProfile',
|
||||
{},
|
||||
{ headers: { Authorization: `Bearer ${token}` } },
|
||||
);
|
||||
|
||||
if (res.data.success && res.data.phoneNumber) {
|
||||
setPhone([
|
||||
{
|
||||
phone: res.data.phoneNumber,
|
||||
time: '',
|
||||
withCode: res.data.phoneNumber,
|
||||
},
|
||||
]);
|
||||
} else if (!res.data.success) {
|
||||
throw new Error(
|
||||
res.data.message || t('settingForm.failFetchPhoneNumber'),
|
||||
);
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
let message = t('settingForm.errorFetchPhoneNumber');
|
||||
if (err instanceof Error) {
|
||||
message = err.message;
|
||||
}
|
||||
setFetchError(message);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchPhoneNumber();
|
||||
}, [token, t]);
|
||||
|
||||
const isPhoneValid = (code: string, phone: string) => {
|
||||
const phoneNum = parsePhoneNumberFromString(code + phone);
|
||||
@@ -37,6 +105,7 @@ export function PhoneNumber() {
|
||||
setTouched(true);
|
||||
if (!phoneNumber) {
|
||||
setError(t('settingForm.thisFieldIsRequired'));
|
||||
return;
|
||||
}
|
||||
if (!isPhoneValid(countryCode, phoneNumber)) {
|
||||
setError(t('settingForm.phoneNumberIsInvalid'));
|
||||
@@ -54,34 +123,100 @@ export function PhoneNumber() {
|
||||
setIsVerified(false);
|
||||
setButtonState('default');
|
||||
setShowToast(false);
|
||||
setError(undefined);
|
||||
setTouched(false);
|
||||
}
|
||||
return enteringEditMode;
|
||||
});
|
||||
};
|
||||
|
||||
const handleSendCode = () => {
|
||||
const handleSendCode = async () => {
|
||||
if (!phoneNumber) return;
|
||||
setButtonState('counting');
|
||||
setIsVerified(false);
|
||||
if (!isPhoneValid(countryCode, phoneNumber)) {
|
||||
setError(t('settingForm.phoneNumberIsInvalid'));
|
||||
return;
|
||||
}
|
||||
setError(undefined);
|
||||
|
||||
try {
|
||||
const res = await apiClient.post<ApiResponse>(
|
||||
'/Profile/SendVerfiyPhoneNumberCode',
|
||||
{
|
||||
phoneNumber: countryCode + phoneNumber.replace(/^0/, ''),
|
||||
},
|
||||
{ headers: { Authorization: `Bearer ${token}` } },
|
||||
);
|
||||
if (res.data.success) {
|
||||
setButtonState('counting');
|
||||
setIsVerified(false);
|
||||
} else {
|
||||
setError(res.data.message || t('settingForm.sendCodeFailed'));
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
setError(t('settingForm.sendCodeFailed'));
|
||||
}
|
||||
};
|
||||
|
||||
const handleVerifyCode = () => {
|
||||
const handleVerifyCode = async () => {
|
||||
if (!verificationCode) {
|
||||
setError(t('settingForm.verificationCodeRequired'));
|
||||
return;
|
||||
}
|
||||
setIsVerifying(true);
|
||||
setTimeout(() => {
|
||||
setError(undefined);
|
||||
|
||||
try {
|
||||
const res = await apiClient.post<ConfirmApiResponse>(
|
||||
'/Profile/ConfirmPhoneNumberChangeCode',
|
||||
{
|
||||
phoneNumber: countryCode + phoneNumber.replace(/^0/, ''),
|
||||
verifyCode: verificationCode,
|
||||
},
|
||||
{ headers: { Authorization: `Bearer ${token}` } },
|
||||
);
|
||||
|
||||
if (res.data.success && res.data.confirm) {
|
||||
setIsVerified(true);
|
||||
setShowToast(true);
|
||||
await handleChangePhoneNumber();
|
||||
} else {
|
||||
setError(res.data.message || t('settingForm.verifyCodeFailed'));
|
||||
setIsVerified(false);
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
setError(t('settingForm.verifyCodeFailed'));
|
||||
setIsVerified(false);
|
||||
} finally {
|
||||
setIsVerifying(false);
|
||||
setIsVerified(true);
|
||||
setShowToast(true);
|
||||
const newPhone = '+98' + phoneNumber.slice(1);
|
||||
setPhone([
|
||||
{ phone: phoneNumber, time: 'چند ثانیه پیش', withCode: newPhone },
|
||||
]);
|
||||
}, 1500);
|
||||
}
|
||||
};
|
||||
|
||||
const handleVerifyClick = () => {
|
||||
if (!verificationCode || isVerifying) return;
|
||||
handleVerifyCode();
|
||||
setTimeout(() => setShowToast(true), 1600);
|
||||
const handleChangePhoneNumber = async () => {
|
||||
try {
|
||||
const fullPhoneNumber = countryCode + phoneNumber.replace(/^0/, '');
|
||||
const res = await apiClient.post<ApiResponse>(
|
||||
'/Profile/ChangePhoneNumber',
|
||||
{
|
||||
phoneNumber: fullPhoneNumber,
|
||||
},
|
||||
{ headers: { Authorization: `Bearer ${token}` } },
|
||||
);
|
||||
|
||||
if (res.data.success) {
|
||||
setPhone([
|
||||
{
|
||||
phone: phoneNumber,
|
||||
time: t('settingForm.justNow'),
|
||||
withCode: fullPhoneNumber,
|
||||
},
|
||||
]);
|
||||
setIsEditing(false);
|
||||
} else {
|
||||
setError(res.data.message || t('settingForm.changePhoneFailed'));
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
setError(t('settingForm.changePhoneFailed'));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -98,7 +233,23 @@ export function PhoneNumber() {
|
||||
/>
|
||||
}
|
||||
>
|
||||
{isEditing ? (
|
||||
{isLoading ? (
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
p: 4,
|
||||
minHeight: '150px',
|
||||
}}
|
||||
>
|
||||
<CircularProgress />
|
||||
</Box>
|
||||
) : fetchError ? (
|
||||
<Box sx={{ textAlign: 'center', p: 4 }}>
|
||||
<Typography color="error">{fetchError}</Typography>
|
||||
</Box>
|
||||
) : isEditing ? (
|
||||
<PhoneEditForm
|
||||
phoneNumber={phoneNumber}
|
||||
setPhoneNumber={setPhoneNumber}
|
||||
@@ -111,7 +262,7 @@ export function PhoneNumber() {
|
||||
buttonState={buttonState}
|
||||
setButtonState={setButtonState}
|
||||
handleSendCode={handleSendCode}
|
||||
handleVerifyClick={handleVerifyClick}
|
||||
handleVerifyClick={handleVerifyCode}
|
||||
error={error}
|
||||
inputError={inputError}
|
||||
handleBlur={handleBlur}
|
||||
|
||||
Reference in New Issue
Block a user