From 7e5ed6e2fc8dd354793c881fd32cbc1be9ff29bd Mon Sep 17 00:00:00 2001 From: Koosha Lahouti Date: Sun, 17 Aug 2025 14:31:18 +0330 Subject: [PATCH] fix: merge style bugs --- .../authorization/api/userCompletion.ts | 24 +--- .../routes/UserCompletionPage.tsx | 64 +++------- .../types/completionFormApiTypes.ts | 1 - src/features/profile/api/settingsApi.ts | 2 +- .../components/security/PasswordDialog.tsx | 1 - .../components/security/PasswordSecurity.tsx | 26 +++- .../components/security/RecentLogins.tsx | 10 ++ .../userInformation/PersonalInformation.tsx | 116 ++++++++++-------- .../userInformation/PhoneNumber.tsx | 62 +++++++++- .../userInformation/SocialMedia.tsx | 43 ++++++- .../personalInformation/ProfileImage.tsx | 3 + .../phoneNumber/PhoneEditForm.tsx | 19 ++- .../profile/routes/ActiveDevicesPage.tsx | 17 ++- src/features/profile/routes/SettingPage.tsx | 4 +- 14 files changed, 254 insertions(+), 138 deletions(-) diff --git a/src/features/authorization/api/userCompletion.ts b/src/features/authorization/api/userCompletion.ts index d9fcf26..efb03fc 100644 --- a/src/features/authorization/api/userCompletion.ts +++ b/src/features/authorization/api/userCompletion.ts @@ -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 => { - 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 => { - 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 => { - const { data } = await apiClient.post( - '/User/CompleteUserInformation', - payload, - ); - return data; +) => { + return apiClient.post('/User/CompleteUserInformation', payload); }; diff --git a/src/features/authorization/routes/UserCompletionPage.tsx b/src/features/authorization/routes/UserCompletionPage.tsx index 8e86c90..6286474 100644 --- a/src/features/authorization/routes/UserCompletionPage.tsx +++ b/src/features/authorization/routes/UserCompletionPage.tsx @@ -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} /> diff --git a/src/features/authorization/types/completionFormApiTypes.ts b/src/features/authorization/types/completionFormApiTypes.ts index 303deca..5bd651d 100644 --- a/src/features/authorization/types/completionFormApiTypes.ts +++ b/src/features/authorization/types/completionFormApiTypes.ts @@ -14,7 +14,6 @@ export interface ConfirmEmailOtpPayload { } export interface CompleteUserInfoPayload { - userId: string; firstName: string; lastName: string; gender: 0 | 1 | 2; diff --git a/src/features/profile/api/settingsApi.ts b/src/features/profile/api/settingsApi.ts index 4cb45a7..bec087f 100644 --- a/src/features/profile/api/settingsApi.ts +++ b/src/features/profile/api/settingsApi.ts @@ -15,7 +15,7 @@ import { import { type InfoRowData } from '../types/settingsType'; export async function fetchProfile() { - return apiClient.post('/Profile/GetProfile'); + return apiClient.post('/Profile/GetProfile', {}); } export async function saveProfile(payload: { diff --git a/src/features/profile/components/security/PasswordDialog.tsx b/src/features/profile/components/security/PasswordDialog.tsx index 9077c94..2c2c729 100644 --- a/src/features/profile/components/security/PasswordDialog.tsx +++ b/src/features/profile/components/security/PasswordDialog.tsx @@ -65,7 +65,6 @@ export function PasswordDialog({ px: { xs: 2, sm: 3 }, }} > - {/* ✅ 4. API ERROR DISPLAY ADDED */} {apiError && ( {apiError} diff --git a/src/features/profile/components/security/PasswordSecurity.tsx b/src/features/profile/components/security/PasswordSecurity.tsx index 9d5093b..4f5134a 100644 --- a/src/features/profile/components/security/PasswordSecurity.tsx +++ b/src/features/profile/components/security/PasswordSecurity.tsx @@ -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={ - - - - ) : ( + {isEditing ? ( + <> - )} - + + + ) : ( + + )} {getErrorMessage(saveProfileError) && ( { + 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/, ''), }); diff --git a/src/features/profile/components/userInformation/SocialMedia.tsx b/src/features/profile/components/userInformation/SocialMedia.tsx index 8c150f4..1c3ba2b 100644 --- a/src/features/profile/components/userInformation/SocialMedia.tsx +++ b/src/features/profile/components/userInformation/SocialMedia.tsx @@ -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([]); const [formError, setFormError] = useState(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; } diff --git a/src/features/profile/components/userInformation/personalInformation/ProfileImage.tsx b/src/features/profile/components/userInformation/personalInformation/ProfileImage.tsx index 4baa6ad..be56d4b 100644 --- a/src/features/profile/components/userInformation/personalInformation/ProfileImage.tsx +++ b/src/features/profile/components/userInformation/personalInformation/ProfileImage.tsx @@ -68,6 +68,9 @@ export function ProfileImage({ sx={{ borderRadius: 2, textTransform: 'none', + flexShrink: 0, + flexGrow: 0, + width: 'auto', }} startIcon={ = 8 && digitsOnly.length <= 15; }; + const [isSending, setIsSending] = useState(false); + return ( <> @@ -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({ ) : (