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