fix: merge style bugs
This commit is contained in:
@@ -2,31 +2,19 @@ import {
|
||||
type SendEmailOtpPayload,
|
||||
type ConfirmEmailOtpPayload,
|
||||
type CompleteUserInfoPayload,
|
||||
type CompleteUserInfoResponse,
|
||||
type GenericApiResponse,
|
||||
} from '../types/completionFormApiTypes';
|
||||
import apiClient from '@/lib/apiClient';
|
||||
|
||||
export const sendEmailOtpApi = async (
|
||||
payload: SendEmailOtpPayload,
|
||||
): Promise<GenericApiResponse & { codeSentAnyway?: boolean }> => {
|
||||
const { data } = await apiClient.post('/User/SendEmailOtp', payload);
|
||||
return data;
|
||||
export const sendEmailOtpApi = async (payload: SendEmailOtpPayload) => {
|
||||
return apiClient.post('/User/SendEmailOtp', payload);
|
||||
};
|
||||
|
||||
export const confirmEmailOtpApi = async (
|
||||
payload: ConfirmEmailOtpPayload,
|
||||
): Promise<GenericApiResponse> => {
|
||||
const { data } = await apiClient.post('/User/ConfirmEmailOtp', payload);
|
||||
return data;
|
||||
export const confirmEmailOtpApi = async (payload: ConfirmEmailOtpPayload) => {
|
||||
return apiClient.post('/User/ConfirmEmailOtp', payload);
|
||||
};
|
||||
|
||||
export const completeUserInformationApi = async (
|
||||
payload: CompleteUserInfoPayload,
|
||||
): Promise<CompleteUserInfoResponse> => {
|
||||
const { data } = await apiClient.post(
|
||||
'/User/CompleteUserInformation',
|
||||
payload,
|
||||
);
|
||||
return data;
|
||||
) => {
|
||||
return apiClient.post('/User/CompleteUserInformation', payload);
|
||||
};
|
||||
|
||||
@@ -16,12 +16,6 @@ import {
|
||||
confirmEmailOtpApi,
|
||||
completeUserInformationApi,
|
||||
} from '../api/userCompletion';
|
||||
import {
|
||||
type SendEmailOtpPayload,
|
||||
type ConfirmEmailOtpPayload,
|
||||
type CompleteUserInfoPayload,
|
||||
} from '../types/completionFormApiTypes';
|
||||
import { type ApiResponse } from '@/types/apiResponse';
|
||||
import { containsSymbol } from '@/utils/regexes/containsSymbol';
|
||||
import { least8Chars } from '@/utils/regexes/least8Chars';
|
||||
import { containsNumber } from '@/utils/regexes/containsNumber';
|
||||
@@ -63,46 +57,20 @@ export function UserCompletionPage() {
|
||||
const matchPassword = password === confirmPassword;
|
||||
const correctEmail = isEmail(email);
|
||||
|
||||
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 { execute: sendCode, data: sendCodeData } = useApi(sendEmailOtpApi);
|
||||
|
||||
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 };
|
||||
});
|
||||
loading: isVerifyingCode,
|
||||
} = useApi(confirmEmailOtpApi);
|
||||
|
||||
const {
|
||||
execute: submitForm,
|
||||
data: submitData,
|
||||
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 };
|
||||
});
|
||||
} = useApi(completeUserInformationApi);
|
||||
|
||||
const getErrorMessage = (error: unknown): string | null => {
|
||||
if (!error) return null;
|
||||
@@ -112,9 +80,10 @@ export function UserCompletionPage() {
|
||||
|
||||
useEffect(() => {
|
||||
if (sendCodeData) {
|
||||
if (sendCodeData.success) {
|
||||
if (sendCodeData.data.success) {
|
||||
showToast({
|
||||
message: sendCodeData.message || t('completion.successfullCodeSent'),
|
||||
message:
|
||||
sendCodeData.data.message || t('completion.successfullCodeSent'),
|
||||
severity: 'success',
|
||||
});
|
||||
setCodeSent(true);
|
||||
@@ -122,7 +91,7 @@ export function UserCompletionPage() {
|
||||
setCountdown(120);
|
||||
} else {
|
||||
showToast({
|
||||
message: sendCodeData.message || t('completion.problem'),
|
||||
message: sendCodeData.data.message || t('completion.problem'),
|
||||
severity: 'error',
|
||||
});
|
||||
}
|
||||
@@ -131,15 +100,15 @@ export function UserCompletionPage() {
|
||||
|
||||
useEffect(() => {
|
||||
if (verifyCodeData) {
|
||||
if (verifyCodeData.success) {
|
||||
if (verifyCodeData.data.success) {
|
||||
setEmailVerified(true);
|
||||
showToast({
|
||||
message: verifyCodeData.message || t('completion.codeVerified'),
|
||||
message: verifyCodeData.data.message || t('completion.codeVerified'),
|
||||
severity: 'success',
|
||||
});
|
||||
} else {
|
||||
showToast({
|
||||
message: verifyCodeData.message || t('completion.invalidCode'),
|
||||
message: verifyCodeData.data.message || t('completion.invalidCode'),
|
||||
severity: 'error',
|
||||
});
|
||||
setEmailVerified(false);
|
||||
@@ -151,13 +120,13 @@ export function UserCompletionPage() {
|
||||
if (submitData) {
|
||||
showToast({
|
||||
message:
|
||||
submitData.message ||
|
||||
submitData.data.message ||
|
||||
t(
|
||||
submitData.success
|
||||
submitData.data.success
|
||||
? 'completion.submitSuccess'
|
||||
: 'completion.submitError',
|
||||
),
|
||||
severity: submitData.success ? 'success' : 'error',
|
||||
severity: submitData.data.success ? 'success' : 'error',
|
||||
});
|
||||
} else if (submitError) {
|
||||
showToast({
|
||||
@@ -205,7 +174,6 @@ export function UserCompletionPage() {
|
||||
|
||||
const handleSubmit = () => {
|
||||
submitForm({
|
||||
userId: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
|
||||
firstName,
|
||||
lastName,
|
||||
gender: sex,
|
||||
@@ -325,7 +293,7 @@ export function UserCompletionPage() {
|
||||
onSubmit={handleSubmit}
|
||||
loading={isSubmitting}
|
||||
error={getErrorMessage(submitError)}
|
||||
success={!!submitData?.success}
|
||||
success={!!submitData?.data.success}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
@@ -14,7 +14,6 @@ export interface ConfirmEmailOtpPayload {
|
||||
}
|
||||
|
||||
export interface CompleteUserInfoPayload {
|
||||
userId: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
gender: 0 | 1 | 2;
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
import { type InfoRowData } from '../types/settingsType';
|
||||
|
||||
export async function fetchProfile() {
|
||||
return apiClient.post<GetProfileApiResponse>('/Profile/GetProfile');
|
||||
return apiClient.post<GetProfileApiResponse>('/Profile/GetProfile', {});
|
||||
}
|
||||
|
||||
export async function saveProfile(payload: {
|
||||
|
||||
@@ -65,7 +65,6 @@ export function PasswordDialog({
|
||||
px: { xs: 2, sm: 3 },
|
||||
}}
|
||||
>
|
||||
{/* ✅ 4. API ERROR DISPLAY ADDED */}
|
||||
{apiError && (
|
||||
<Typography color="error" variant="body2" textAlign="center">
|
||||
{apiError}
|
||||
|
||||
@@ -21,6 +21,7 @@ import { containsNumber } from '@/utils/regexes/containsNumber';
|
||||
import { least8Chars } from '@/utils/regexes/least8Chars';
|
||||
import { hasUpperAndLowerLetter } from '@/utils/regexes/hasUpperAndLowerLetter';
|
||||
import { containsSymbol } from '@/utils/regexes/containsSymbol';
|
||||
import { useToast } from '@rkheftan/harmony-ui';
|
||||
|
||||
export function PasswordSecurity() {
|
||||
const theme = useTheme();
|
||||
@@ -42,6 +43,7 @@ export function PasswordSecurity() {
|
||||
hasNumber && hasMinLength && hasUpperAndLower && hasSpecialChar;
|
||||
|
||||
const matchPassword = password === confirmPassword;
|
||||
const showToast = useToast();
|
||||
|
||||
const {
|
||||
data: profileData,
|
||||
@@ -79,6 +81,12 @@ export function PasswordSecurity() {
|
||||
|
||||
useEffect(() => {
|
||||
if (addData?.success || changeData?.success) {
|
||||
showToast({
|
||||
message: changePasswordUI
|
||||
? t('securityForm.passwordChanged')
|
||||
: t('securityForm.passwordAdded'),
|
||||
severity: 'success',
|
||||
});
|
||||
if (!changePasswordUI) {
|
||||
setChangePasswordUI(true);
|
||||
}
|
||||
@@ -87,7 +95,16 @@ export function PasswordSecurity() {
|
||||
setConfirmPassword('');
|
||||
setCurrentPassword('');
|
||||
}
|
||||
}, [addData, changeData, changePasswordUI]);
|
||||
}, [addData, changeData, changePasswordUI, showToast, t]);
|
||||
|
||||
useEffect(() => {
|
||||
if (addError || changeError) {
|
||||
showToast({
|
||||
message: getErrorMessage(addError || changeError) || t('error.general'),
|
||||
severity: 'error',
|
||||
});
|
||||
}
|
||||
}, [addError, changeError, t, showToast]);
|
||||
|
||||
const handleOpen = () => setOpen(true);
|
||||
const handleClose = () => setOpen(false);
|
||||
@@ -120,7 +137,12 @@ export function PasswordSecurity() {
|
||||
title={t('securityForm.password')}
|
||||
subtitle={t('securityForm.determinePassword')}
|
||||
action={
|
||||
<Button variant="contained" onClick={handleOpen}>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={handleOpen}
|
||||
size="medium"
|
||||
sx={{ flexShrink: 0, flexGrow: 0, width: 'auto' }}
|
||||
>
|
||||
{changePasswordUI
|
||||
? t('securityForm.changePassword')
|
||||
: t('securityForm.addPassword')}
|
||||
|
||||
@@ -13,12 +13,14 @@ import { fetchProfile } from '../../api/settingsApi';
|
||||
import type { LoginLog } from '../../types/settingsApiType';
|
||||
import { useApi } from '@/hooks/useApi';
|
||||
import { formatDate } from '@/utils/formatSessionDate';
|
||||
import { useToast } from '@rkheftan/harmony-ui';
|
||||
|
||||
export function RecentLogins() {
|
||||
const { t, i18n } = useTranslation('setting');
|
||||
const [logs, setLogs] = useState<LoginLog[]>([]);
|
||||
const theme = useTheme();
|
||||
const isXsup = useMediaQuery(theme.breakpoints.up('xs'));
|
||||
const showToast = useToast();
|
||||
|
||||
const {
|
||||
data: profileData,
|
||||
@@ -32,6 +34,14 @@ export function RecentLogins() {
|
||||
}
|
||||
}, [profileData]);
|
||||
|
||||
useEffect(() => {
|
||||
if (fetchError) {
|
||||
showToast({
|
||||
message: getErrorMessage(fetchError) || t('active.errorFetch'),
|
||||
severity: 'error',
|
||||
});
|
||||
}
|
||||
}, [fetchError, t, showToast]);
|
||||
const getErrorMessage = (error: unknown): string | null => {
|
||||
if (!error) return null;
|
||||
if (error instanceof Error) return error.message;
|
||||
|
||||
@@ -9,6 +9,7 @@ import { PageWrapper } from '../PageWrapper';
|
||||
import { useApi } from '@/hooks/useApi';
|
||||
import { Gender, type InfoRowData } from '../../types/settingsType';
|
||||
import { fetchProfile, saveProfile } from '../../api/settingsApi';
|
||||
import { useToast } from '@rkheftan/harmony-ui';
|
||||
|
||||
export function PersonalInformation() {
|
||||
const { t } = useTranslation('setting');
|
||||
@@ -22,6 +23,7 @@ export function PersonalInformation() {
|
||||
country: '',
|
||||
});
|
||||
const [originalData, setOriginalData] = useState<InfoRowData | null>(null);
|
||||
const showToast = useToast();
|
||||
|
||||
const {
|
||||
data: profileData,
|
||||
@@ -55,10 +57,14 @@ export function PersonalInformation() {
|
||||
|
||||
useEffect(() => {
|
||||
if (saveData?.success) {
|
||||
showToast({
|
||||
message: t('settingForm.successSaveProfile'),
|
||||
severity: 'success',
|
||||
});
|
||||
setIsEditing(false);
|
||||
setOriginalData(data);
|
||||
}
|
||||
}, [saveData, data]);
|
||||
}, [saveData, data, showToast, t]);
|
||||
|
||||
const initials = `${data?.firstName?.trim()[0] || ''}${data?.lastName?.trim()[0] || ''}`;
|
||||
|
||||
@@ -84,6 +90,25 @@ export function PersonalInformation() {
|
||||
if (error instanceof Error) return error.message;
|
||||
return String(error);
|
||||
};
|
||||
useEffect(() => {
|
||||
if (saveProfileError) {
|
||||
showToast({
|
||||
message:
|
||||
getErrorMessage(saveProfileError) || t('settingForm.errorSave'),
|
||||
severity: 'error',
|
||||
});
|
||||
}
|
||||
}, [saveProfileError, showToast, t]);
|
||||
|
||||
useEffect(() => {
|
||||
if (fetchProfileError) {
|
||||
showToast({
|
||||
message:
|
||||
getErrorMessage(fetchProfileError) || t('settingForm.errorFetch'),
|
||||
severity: 'error',
|
||||
});
|
||||
}
|
||||
}, [fetchProfileError, showToast, t]);
|
||||
|
||||
return (
|
||||
<PageWrapper>
|
||||
@@ -95,62 +120,51 @@ export function PersonalInformation() {
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'flex-end',
|
||||
width: '100%',
|
||||
gap: 1,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
gap: 1,
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'flex-end',
|
||||
}}
|
||||
>
|
||||
{isEditing ? (
|
||||
<>
|
||||
<Button
|
||||
variant="text"
|
||||
onClick={handleCancelClick}
|
||||
size="large"
|
||||
sx={{
|
||||
color: 'primary.main',
|
||||
textTransform: 'none',
|
||||
width: { xs: '100%', sm: 'auto' },
|
||||
}}
|
||||
>
|
||||
{t('settingForm.rejectButton')}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleSaveClick}
|
||||
size="large"
|
||||
variant="contained"
|
||||
sx={{
|
||||
textTransform: 'none',
|
||||
width: { xs: '100%', sm: 'auto' },
|
||||
}}
|
||||
disabled={isSavingProfile}
|
||||
>
|
||||
{isSavingProfile ? (
|
||||
<CircularProgress size={24} color="inherit" />
|
||||
) : (
|
||||
t('settingForm.saveButton')
|
||||
)}
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
{isEditing ? (
|
||||
<>
|
||||
<Button
|
||||
onClick={handleEditClick}
|
||||
variant="text"
|
||||
onClick={handleCancelClick}
|
||||
size="large"
|
||||
variant="outlined"
|
||||
sx={{ borderRadius: 1 }}
|
||||
disabled={isLoadingProfile}
|
||||
sx={{
|
||||
color: 'primary.main',
|
||||
textTransform: 'none',
|
||||
width: { xs: '100%', sm: 'auto' },
|
||||
}}
|
||||
>
|
||||
{t('settingForm.editButton')}
|
||||
{t('settingForm.rejectButton')}
|
||||
</Button>
|
||||
)}
|
||||
</Box>
|
||||
<Button
|
||||
onClick={handleSaveClick}
|
||||
size="large"
|
||||
variant="contained"
|
||||
sx={{
|
||||
textTransform: 'none',
|
||||
width: { xs: '100%', sm: 'auto' },
|
||||
}}
|
||||
disabled={isSavingProfile}
|
||||
>
|
||||
{isSavingProfile ? (
|
||||
<CircularProgress size={24} color="inherit" />
|
||||
) : (
|
||||
t('settingForm.saveButton')
|
||||
)}
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
<Button
|
||||
onClick={handleEditClick}
|
||||
size="large"
|
||||
variant="outlined"
|
||||
sx={{ borderRadius: 1 }}
|
||||
disabled={isLoadingProfile}
|
||||
>
|
||||
{t('settingForm.editButton')}
|
||||
</Button>
|
||||
)}
|
||||
{getErrorMessage(saveProfileError) && (
|
||||
<Typography
|
||||
color="error"
|
||||
|
||||
@@ -16,9 +16,11 @@ import {
|
||||
changePhoneNumber,
|
||||
} from '../../api/settingsApi';
|
||||
import { type Phone } from '../../types/settingsType';
|
||||
import { useToast } from '@rkheftan/harmony-ui';
|
||||
|
||||
export function PhoneNumber() {
|
||||
const { t, i18n } = useTranslation('setting');
|
||||
const toast = useToast();
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const [phoneNumber, setPhoneNumber] = useState('');
|
||||
const [verificationCode, setVerificationCode] = useState('');
|
||||
@@ -74,12 +76,36 @@ export function PhoneNumber() {
|
||||
}
|
||||
}, [profileData]);
|
||||
|
||||
useEffect(() => {
|
||||
if (fetchError) {
|
||||
toast({
|
||||
message:
|
||||
getErrorMessage(fetchError) || t('settingForm.errorFetchPhoneNumber'),
|
||||
severity: 'error',
|
||||
});
|
||||
}
|
||||
}, [fetchError, toast, t]);
|
||||
|
||||
useEffect(() => {
|
||||
if (sendCodeData?.success) {
|
||||
setButtonState('counting');
|
||||
setIsVerified(false);
|
||||
toast({
|
||||
message: t('settingForm.codeSentSuccessfully'),
|
||||
severity: 'success',
|
||||
});
|
||||
}
|
||||
}, [sendCodeData]);
|
||||
}, [sendCodeData, t, toast]);
|
||||
|
||||
useEffect(() => {
|
||||
if (sendCodeError) {
|
||||
toast({
|
||||
message:
|
||||
getErrorMessage(sendCodeError) || t('settingForm.errorSendCode'),
|
||||
severity: 'error',
|
||||
});
|
||||
}
|
||||
}, [sendCodeError, toast, t]);
|
||||
|
||||
useEffect(() => {
|
||||
if (confirmData?.success && confirmData.confirm) {
|
||||
@@ -87,8 +113,22 @@ export function PhoneNumber() {
|
||||
setShowToast(true);
|
||||
const fullPhoneNumber = countryCode + phoneNumber.replace(/^0/, '');
|
||||
executeChangePhone({ phoneNumber: fullPhoneNumber });
|
||||
toast({
|
||||
message: t('settingForm.phoneVerified'),
|
||||
severity: 'success',
|
||||
});
|
||||
}
|
||||
}, [confirmData, countryCode, phoneNumber, executeChangePhone]);
|
||||
}, [confirmData, countryCode, phoneNumber, executeChangePhone, t, toast]);
|
||||
|
||||
useEffect(() => {
|
||||
if (confirmError) {
|
||||
toast({
|
||||
message:
|
||||
getErrorMessage(confirmError) || t('settingForm.errorConfirmCode'),
|
||||
severity: 'error',
|
||||
});
|
||||
}
|
||||
}, [confirmError, toast, t]);
|
||||
|
||||
useEffect(() => {
|
||||
if (changePhoneData?.success) {
|
||||
@@ -101,8 +141,23 @@ export function PhoneNumber() {
|
||||
},
|
||||
]);
|
||||
setIsEditing(false);
|
||||
toast({
|
||||
message: t('settingForm.phoneChangedSuccessfully'),
|
||||
severity: 'success',
|
||||
});
|
||||
}
|
||||
}, [changePhoneData, countryCode, phoneNumber, t]);
|
||||
}, [changePhoneData, countryCode, phoneNumber, t, toast]);
|
||||
|
||||
useEffect(() => {
|
||||
if (changePhoneError) {
|
||||
toast({
|
||||
message:
|
||||
getErrorMessage(changePhoneError) ||
|
||||
t('settingForm.errorChangePhone'),
|
||||
severity: 'error',
|
||||
});
|
||||
}
|
||||
}, [changePhoneError, toast, t]);
|
||||
|
||||
const apiError = useMemo(
|
||||
() => sendCodeError || confirmError || changePhoneError,
|
||||
@@ -151,7 +206,6 @@ export function PhoneNumber() {
|
||||
const handleSendCode = () => {
|
||||
handleBlur();
|
||||
if (formError || !isPhoneValid(countryCode, phoneNumber)) return;
|
||||
|
||||
executeSendCode({
|
||||
phoneNumber: countryCode + phoneNumber.replace(/^0/, ''),
|
||||
});
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
changeEmail,
|
||||
} from '../../api/settingsApi';
|
||||
import { type EmailAccount } from '../../types/settingsType';
|
||||
import { useToast } from '@rkheftan/harmony-ui';
|
||||
|
||||
export function SocialMedia() {
|
||||
const { t } = useTranslation('setting');
|
||||
@@ -28,6 +29,7 @@ export function SocialMedia() {
|
||||
);
|
||||
const [emailList, setEmailList] = useState<EmailAccount[]>([]);
|
||||
const [formError, setFormError] = useState<string | null>(null);
|
||||
const showToast = useToast();
|
||||
|
||||
const {
|
||||
data: profileData,
|
||||
@@ -82,15 +84,23 @@ export function SocialMedia() {
|
||||
|
||||
useEffect(() => {
|
||||
if (sendCodeData?.success) {
|
||||
showToast({
|
||||
message: t('settingForm.verificationCodeSent'),
|
||||
severity: 'success',
|
||||
});
|
||||
setDialogStep('enterCode');
|
||||
}
|
||||
}, [sendCodeData]);
|
||||
}, [sendCodeData, t, showToast]);
|
||||
|
||||
useEffect(() => {
|
||||
if (confirmData?.success && confirmData.confirm) {
|
||||
showToast({
|
||||
message: t('settingForm.verificationSuccessful'),
|
||||
severity: 'success',
|
||||
});
|
||||
executeChangeEmail({ email: emailInput });
|
||||
}
|
||||
}, [confirmData, emailInput, executeChangeEmail]);
|
||||
}, [confirmData, emailInput, executeChangeEmail, t, showToast]);
|
||||
|
||||
useEffect(() => {
|
||||
if (changeEmailData?.success) {
|
||||
@@ -102,9 +112,34 @@ export function SocialMedia() {
|
||||
time: t('settingForm.justNow'),
|
||||
},
|
||||
]);
|
||||
showToast({
|
||||
message: t('settingForm.emailChangedSuccessfully'),
|
||||
severity: 'success',
|
||||
});
|
||||
resetDialog();
|
||||
}
|
||||
}, [changeEmailData, emailInput, t]);
|
||||
}, [changeEmailData, emailInput, t, showToast]);
|
||||
|
||||
useEffect(() => {
|
||||
if (sendCodeError) {
|
||||
showToast({
|
||||
message: getErrorMessage(sendCodeError) || t('settingForm.error'),
|
||||
severity: 'error',
|
||||
});
|
||||
}
|
||||
if (confirmError) {
|
||||
showToast({
|
||||
message: getErrorMessage(confirmError) || t('settingForm.error'),
|
||||
severity: 'error',
|
||||
});
|
||||
}
|
||||
if (changeEmailError) {
|
||||
showToast({
|
||||
message: getErrorMessage(changeEmailError) || t('settingForm.error'),
|
||||
severity: 'error',
|
||||
});
|
||||
}
|
||||
}, [sendCodeError, confirmError, changeEmailError, t, showToast]);
|
||||
|
||||
const resetDialog = () => {
|
||||
setOpenDialog(false);
|
||||
@@ -115,7 +150,7 @@ export function SocialMedia() {
|
||||
};
|
||||
|
||||
const handleSendCode = () => {
|
||||
if (!/^\S+@\S+\.\S+$/.test(emailInput)) {
|
||||
if (!/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(emailInput)) {
|
||||
setFormError(t('settingForm.emailIsInvalid'));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -68,6 +68,9 @@ export function ProfileImage({
|
||||
sx={{
|
||||
borderRadius: 2,
|
||||
textTransform: 'none',
|
||||
flexShrink: 0,
|
||||
flexGrow: 0,
|
||||
width: 'auto',
|
||||
}}
|
||||
startIcon={
|
||||
<Icon
|
||||
|
||||
@@ -12,6 +12,7 @@ import { CountDownTimer } from '@/components/CountDownTimer';
|
||||
import { CountryCodeSelector } from '../../CountryCodeSelector';
|
||||
import { Icon } from '@rkheftan/harmony-ui';
|
||||
import { type PhoneEditFormProps } from '@/features/profile/types/settingsType';
|
||||
import { useState } from 'react';
|
||||
|
||||
export default function PhoneEditForm({
|
||||
phoneNumber,
|
||||
@@ -39,6 +40,8 @@ export default function PhoneEditForm({
|
||||
return digitsOnly.length >= 8 && digitsOnly.length <= 15;
|
||||
};
|
||||
|
||||
const [isSending, setIsSending] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box sx={{ width: '100%' }}>
|
||||
@@ -73,7 +76,7 @@ export default function PhoneEditForm({
|
||||
error={inputError}
|
||||
helperText={inputError ? error : ''}
|
||||
onChange={(e) => setPhoneNumber(e.target.value)}
|
||||
sx={{ flex: '1 1 220px', minWidth: 0 }}
|
||||
sx={{ flex: '1 1 220px' }}
|
||||
placeholder="09123456789"
|
||||
InputProps={{
|
||||
endAdornment:
|
||||
@@ -122,12 +125,18 @@ export default function PhoneEditForm({
|
||||
) : (
|
||||
<Button
|
||||
variant="text"
|
||||
onClick={() => {
|
||||
onClick={async () => {
|
||||
if (isValidPhoneNumber(phoneNumber)) {
|
||||
handleSendCode();
|
||||
setIsSending(true);
|
||||
try {
|
||||
await handleSendCode();
|
||||
} finally {
|
||||
setIsSending(false);
|
||||
}
|
||||
}
|
||||
}}
|
||||
disabled={
|
||||
isSending ||
|
||||
buttonState === 'counting' ||
|
||||
phoneNumber.length === 0 ||
|
||||
!isValidPhoneNumber(phoneNumber)
|
||||
@@ -138,7 +147,9 @@ export default function PhoneEditForm({
|
||||
textTransform: 'none',
|
||||
}}
|
||||
>
|
||||
{buttonState === 'counting' ? (
|
||||
{isSending ? (
|
||||
<CircularProgress size={20} />
|
||||
) : buttonState === 'counting' ? (
|
||||
<CountDownTimer
|
||||
initialSeconds={60}
|
||||
onComplete={() => setButtonState('default')}
|
||||
|
||||
@@ -17,6 +17,7 @@ import { type ApiSession } from '../types/settingsApiType';
|
||||
import { useApi } from '@/hooks/useApi';
|
||||
import { formatDate } from '@/utils/formatSessionDate';
|
||||
import { type Device } from '../types/settingsType';
|
||||
import { useToast } from '@rkheftan/harmony-ui';
|
||||
|
||||
export function ActiveDevicesPage() {
|
||||
const { t, i18n } = useTranslation('setting');
|
||||
@@ -24,6 +25,7 @@ export function ActiveDevicesPage() {
|
||||
const [loadingDeleteIds, setLoadingDeleteIds] = useState<string[]>([]);
|
||||
const theme = useTheme();
|
||||
const isXsup = useMediaQuery(theme.breakpoints.up('xs'));
|
||||
const showToast = useToast();
|
||||
|
||||
const {
|
||||
data: profileData,
|
||||
@@ -65,10 +67,17 @@ export function ActiveDevicesPage() {
|
||||
if (data.success) {
|
||||
setDevices((prevDevices) => prevDevices.filter((d) => d.id !== id));
|
||||
} else {
|
||||
console.error('Delete failed:', data.message);
|
||||
showToast({
|
||||
message: data.message || t('active.deleteFailed'),
|
||||
severity: 'error',
|
||||
});
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
console.error('Delete error:', error);
|
||||
// console.error('Delete error:', error);
|
||||
showToast({
|
||||
message: t('active.deleteFailed'),
|
||||
severity: 'error',
|
||||
});
|
||||
} finally {
|
||||
setLoadingDeleteIds((prev) =>
|
||||
prev.filter((loadingId) => loadingId !== id),
|
||||
@@ -104,6 +113,9 @@ export function ActiveDevicesPage() {
|
||||
borderColor: 'error.main',
|
||||
color: 'error.main',
|
||||
textTransform: 'none',
|
||||
flexShrink: 0,
|
||||
flexGrow: 0,
|
||||
width: 'auto',
|
||||
}}
|
||||
disabled={isLoading || isTerminating}
|
||||
>
|
||||
@@ -215,6 +227,7 @@ export function ActiveDevicesPage() {
|
||||
whiteSpace: 'nowrap',
|
||||
color: 'success.main',
|
||||
textTransform: 'none',
|
||||
maxWidth: '125px',
|
||||
'&.Mui-disabled': {
|
||||
color: 'success.main',
|
||||
borderColor: 'success.main',
|
||||
|
||||
@@ -56,7 +56,7 @@ export function SettingPage() {
|
||||
data: profileData,
|
||||
loading: isFetching,
|
||||
error: fetchError,
|
||||
} = useApi(fetchProfile, { immediate: true });
|
||||
} = useApi(fetchProfile);
|
||||
|
||||
const {
|
||||
data: saveData,
|
||||
@@ -142,7 +142,7 @@ export function SettingPage() {
|
||||
subtitle={t('settings.description')}
|
||||
highlighted={isEditing}
|
||||
action={
|
||||
<Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap' }}>
|
||||
<Box sx={{ display: 'flex', gap: 1 }}>
|
||||
{isEditing && (
|
||||
<Button
|
||||
variant="text"
|
||||
|
||||
Reference in New Issue
Block a user