diff --git a/public/locales/en/profileSetting.json b/public/locales/en/profileSetting.json
index bb4b664..e8dc332 100644
--- a/public/locales/en/profileSetting.json
+++ b/public/locales/en/profileSetting.json
@@ -40,6 +40,7 @@
"notDetermined": "Not determined",
"successfulChangePhone": "Phone number changed successfully",
"phoneNumberIsInvalid": "Phone number is invalid",
- "thisFieldIsRequired": "This field is requried"
+ "thisFieldIsRequired": "This field is required",
+ "changePicture": "Change picture"
}
}
diff --git a/public/locales/fa/profileSetting.json b/public/locales/fa/profileSetting.json
index c48cd73..1a591e7 100644
--- a/public/locales/fa/profileSetting.json
+++ b/public/locales/fa/profileSetting.json
@@ -40,6 +40,7 @@
"notDetermined": "تعیین نشده",
"successfulChangePhone": "شماره تماس با موفقیت تغییر کرد",
"phoneNumberIsInvalid": "شماره وارد شده نامعتبر میباشد",
- "thisFieldIsRequired": "این فیلد الزامی است"
+ "thisFieldIsRequired": "این فیلد الزامی است",
+ "changePicture": "تغییر تصویر"
}
}
diff --git a/src/App.tsx b/src/App.tsx
index 4fd2f1b..21371ee 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -3,6 +3,7 @@ import './App.css';
import { LanguageManager } from './components/LanguageManager';
import { Settings } from './features/profile/routes/SettingPage';
+
function App() {
return (
<>
diff --git a/src/components/CardContainer.tsx b/src/components/CardContainer.tsx
index 798b25a..ad90026 100644
--- a/src/components/CardContainer.tsx
+++ b/src/components/CardContainer.tsx
@@ -1,19 +1,21 @@
import React from 'react';
import { Box, Typography } from '@mui/material';
+interface CardContainerProps {
+ title: string;
+ subtitle: string;
+ action?: React.ReactNode;
+ children?: React.ReactNode;
+ highlighted?: boolean;
+}
+
export function CardContainer({
title,
subtitle,
action,
children,
highlighted,
-}: {
- title: string;
- subtitle: string;
- action?: React.ReactNode;
- children?: React.ReactNode;
- highlighted?: boolean;
-}) {
+}: CardContainerProps) {
return (
-
-
-
-
-
-
{t('active.deletDevicesButton')}
@@ -116,7 +109,11 @@ export function ActiveDevices() {
order: { xs: 2, sm: 2 },
}}
>
-
+
{device.deviceModel}
@@ -138,22 +135,22 @@ export function ActiveDevices() {
sx={{
flexBasis: { xs: '100%', sm: 'auto' },
mb: { xs: 1, sm: 0 },
- textAlign: { xs: 'left', sm: 'center' },
minWidth: { sm: '138px' },
order: { xs: 4, sm: 4 },
+ alignItems: 'center',
+ justifyContent: 'center',
}}
>
{device.current && (
+ {uploadedImageUrl && onRemoveImage && (
+
+
+
+ )}
+
+
+
+ );
+}
diff --git a/src/features/profile/components/userInformation/personlInformation/InfoRowDisplay.tsx b/src/features/profile/components/userInformation/personlInformation/InfoRowDisplay.tsx
deleted file mode 100644
index d838f37..0000000
--- a/src/features/profile/components/userInformation/personlInformation/InfoRowDisplay.tsx
+++ /dev/null
@@ -1,90 +0,0 @@
-import { Box, Typography, Avatar } from '@mui/material';
-import { useTranslation } from 'react-i18next';
-import { CountryFlag } from '@/components/CountryFlag';
-import { DisplayField } from './DisplayField';
-
-export function InfoRowDisplay({
- data,
- uploadedImageUrl,
- initials,
-}: {
- data: any;
- uploadedImageUrl: string | null;
- initials: string;
-}) {
- const { t } = useTranslation('profileSetting');
- const displayValue = (value: string) =>
- value?.trim() || t('settingForm.notDetermined');
-
- return (
-
-
-
-
- {t('settingForm.name')} و {t('settingForm.familyName')}
-
-
-
- {initials}
-
-
- {`${displayValue(data.firstName)} ${displayValue(data.lastName)}`}
-
-
-
-
-
-
- {t('settingForm.country')}
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/src/features/profile/components/userInformation/personlInformation/ProfileImage.tsx b/src/features/profile/components/userInformation/personlInformation/ProfileImage.tsx
deleted file mode 100644
index 3d848c8..0000000
--- a/src/features/profile/components/userInformation/personlInformation/ProfileImage.tsx
+++ /dev/null
@@ -1,62 +0,0 @@
-import { Box, Avatar, Typography, Button } from '@mui/material';
-import { useTranslation } from 'react-i18next';
-import { Camera } from 'iconsax-react';
-
-export function ProfileImage({
- initials,
- uploadedImageUrl,
- onImageChange,
-}: {
- initials: string;
- uploadedImageUrl: string | null;
- onImageChange: (e: React.ChangeEvent) => void;
-}) {
- const { t } = useTranslation('profileSetting');
-
- return (
-
-
- {initials}
-
-
-
- {t('settingForm.profilePicture')}
-
-
- {t('settingForm.allowedFormat')}
-
-
- }
- >
- {t('settingForm.uploadPicture')}
-
-
-
-
-
- );
-}
diff --git a/src/features/profile/components/userInformation/phoneNumber/PhoneActionButtons.tsx b/src/features/profile/components/userInformation/phoneNumber/PhoneActionButtons.tsx
index 8329ebb..a20e577 100644
--- a/src/features/profile/components/userInformation/phoneNumber/PhoneActionButtons.tsx
+++ b/src/features/profile/components/userInformation/phoneNumber/PhoneActionButtons.tsx
@@ -1,14 +1,16 @@
import { Box, Button } from '@mui/material';
+interface PhoneActionButtonsProps {
+ isEditing: boolean;
+ toggleEdit: () => void;
+ t: (key: string) => string;
+}
+
export default function PhoneActionButtons({
isEditing,
toggleEdit,
t,
-}: {
- isEditing: boolean;
- toggleEdit: () => void;
- t: (key: string) => string;
-}) {
+}: PhoneActionButtonsProps) {
return (
{isEditing && (
@@ -19,7 +21,7 @@ export default function PhoneActionButtons({
sx={{
color: 'primary.main',
textTransform: 'none',
- width: '43px',
+ width: { xs: '100%', sm: 'auto' },
}}
>
{t('settingForm.rejectButton')}
@@ -30,13 +32,9 @@ export default function PhoneActionButtons({
size="large"
variant="outlined"
sx={{
- textTransform: 'none',
- border: '1px solid',
- borderColor: 'primary.main',
- borderRadius: '4px',
- bgcolor: isEditing ? 'primary.main' : 'background.paper',
+ borderRadius: 1,
+ bgcolor: isEditing ? 'primary.main' : 'background.default',
color: isEditing ? 'primary.contrastText' : 'primary.main',
- width: { xs: '100%', sm: isEditing ? '85px' : '161px' },
whiteSpace: 'nowrap',
}}
>
diff --git a/src/features/profile/components/userInformation/phoneNumber/PhoneDisplay.tsx b/src/features/profile/components/userInformation/phoneNumber/PhoneDisplay.tsx
index a922ae4..72f5733 100644
--- a/src/features/profile/components/userInformation/phoneNumber/PhoneDisplay.tsx
+++ b/src/features/profile/components/userInformation/phoneNumber/PhoneDisplay.tsx
@@ -1,31 +1,55 @@
import { Box, Typography } from '@mui/material';
+import { Icon } from '@rkheftan/harmony-ui';
+import { Mobile } from 'iconsax-react';
-export default function PhoneDisplay({
- phones,
-}: {
+interface PhoneDisplayProps {
phones: { phone: string; time: string }[];
-}) {
+}
+
+export default function PhoneDisplay({ phones }: PhoneDisplayProps) {
return (
-
+ <>
{phones.map((item, index) => (
-
- {item.phone}
-
-
- {item.time}
-
+
+
+
+
+
+
+ {item.phone}
+
+
+ {item.time}
+
+
))}
-
+ >
);
}
diff --git a/src/features/profile/components/userInformation/phoneNumber/PhoneEditForm.tsx b/src/features/profile/components/userInformation/phoneNumber/PhoneEditForm.tsx
index 2dd7695..4875e71 100644
--- a/src/features/profile/components/userInformation/phoneNumber/PhoneEditForm.tsx
+++ b/src/features/profile/components/userInformation/phoneNumber/PhoneEditForm.tsx
@@ -1,8 +1,41 @@
-import { Box, Typography, TextField, IconButton, Button } from '@mui/material';
-import { Edit, Refresh, TickCircle } from 'iconsax-react';
+import {
+ Box,
+ Typography,
+ TextField,
+ Button,
+ IconButton,
+ InputAdornment,
+ CircularProgress,
+} from '@mui/material';
+import { Edit2, TickCircle } from 'iconsax-react';
import { CountDownTimer } from '@/components/CountDownTimer';
import { Toast } from '@/components/Toast';
import { CountryCodeSelector } from '../../CountryCodeSelector';
+import { Icon } from '@rkheftan/harmony-ui';
+
+interface PhoneEditFormProps {
+ phoneNumber: string;
+ setPhoneNumber: (v: string) => void;
+ countryCode: string;
+ setCountryCode: (v: string) => void;
+ verificationCode: string;
+ setVerificationCode: (v: string) => void;
+ isVerified: boolean;
+ isVerifying: boolean;
+ buttonState: 'default' | 'counting';
+ setButtonState: (v: 'default' | 'counting') => void;
+ handleSendCode: () => void;
+ handleVerifyClick: () => void;
+ error?: string;
+ inputError: boolean;
+ handleBlur: () => void;
+ textFieldRef: React.RefObject;
+ inputRef: React.RefObject;
+ phones: { phone: string; time: string; withCode: string }[];
+ showToast: boolean;
+ setShowToast: (v: boolean) => void;
+ t: (key: string) => string;
+}
export default function PhoneEditForm({
phoneNumber,
@@ -26,41 +59,34 @@ export default function PhoneEditForm({
showToast,
setShowToast,
t,
-}: {
- phoneNumber: string;
- setPhoneNumber: (v: string) => void;
- countryCode: string;
- setCountryCode: (v: string) => void;
- verificationCode: string;
- setVerificationCode: (v: string) => void;
- isVerified: boolean;
- isVerifying: boolean;
- buttonState: 'default' | 'counting';
- setButtonState: (v: 'default' | 'counting') => void;
- handleSendCode: () => void;
- handleVerifyClick: () => void;
- error?: string;
- inputError: boolean;
- handleBlur: () => void;
- textFieldRef: React.RefObject;
- inputRef: React.RefObject;
- phones: { phone: string; time: string; withCode: string }[];
- showToast: boolean;
- setShowToast: (v: boolean) => void;
- t: (key: string) => string;
-}) {
+}: PhoneEditFormProps) {
+ const isValidPhoneNumber = (phone: string) => {
+ const digitsOnly = phone.replace(/\D/g, '');
+ return digitsOnly.length >= 8 && digitsOnly.length <= 15;
+ };
+
return (
-
-
- {t('settingForm.editPhoneNumber')}
-
- {t('settingForm.phoneNumberText')}({phones.map((p) => p.withCode)})
- {t('settingForm.verb')}
-
+ <>
+
+
+
+ {t('settingForm.editPhoneNumber')}
+
+
+ {t('settingForm.phoneNumberText')}({phones.map((p) => p.withCode)})
+ {t('settingForm.verb')}
+
+
setPhoneNumber(e.target.value)}
- sx={{ flex: '1 1 240px', minWidth: 0 }}
+ sx={{ flex: '1 1 220px', minWidth: 0 }}
placeholder="09123456789"
- inputProps={{ dir: 'rtl', style: { textAlign: 'right' } }}
InputProps={{
endAdornment:
buttonState === 'counting' ? (
- {
- setButtonState('default');
- setPhoneNumber('');
- }}
- sx={{ mr: 1 }}
- >
-
-
- ) : null,
- }}
- slotProps={{
- htmlInput: {
- dir: 'auto',
- sx: { lineHeight: 1.5, paddingX: 0 },
- },
- input: {
- endAdornment: (
+
+ {
+ setButtonState('default');
+ setPhoneNumber('');
+ setVerificationCode('');
+ }}
+ edge="end"
+ >
+
+
+
+ ) : (
),
- },
}}
/>
{isVerified ? (
-
-
-
- {t('settingForm.successButton')}
-
+
+
+ {t('settingForm.successButton')}
) : (
{
+ if (isValidPhoneNumber(phoneNumber)) {
+ handleSendCode();
+ }
+ }}
+ disabled={
+ buttonState === 'counting' ||
+ phoneNumber.length === 0 ||
+ !isValidPhoneNumber(phoneNumber)
+ }
sx={{
- textTransform: 'none',
- minWidth: { xs: '100%', sm: 170 },
+ minWidth: { xs: '100%', sm: 220 },
color: 'primary.main',
- height: 56,
}}
>
{buttonState === 'counting' ? (
@@ -149,6 +183,7 @@ export default function PhoneEditForm({
gap: 2,
mt: 2,
alignItems: 'center',
+ mx: 3,
}}
>
{isVerifying ? (
-
-
-
+
) : (
t('settingForm.checkCode')
)}
@@ -203,6 +223,6 @@ export default function PhoneEditForm({
>
{t('settingForm.successfulChangePhone')}
-
+ >
);
}
diff --git a/src/features/profile/components/userInformation/socialMedia/SocialMediaDialog.tsx b/src/features/profile/components/userInformation/socialMedia/SocialMediaDialog.tsx
index 0f6054e..df721f1 100644
--- a/src/features/profile/components/userInformation/socialMedia/SocialMediaDialog.tsx
+++ b/src/features/profile/components/userInformation/socialMedia/SocialMediaDialog.tsx
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { type ReactElement, type ElementType } from 'react';
import {
Box,
Button,
@@ -11,15 +11,28 @@ import {
} from '@mui/material';
import Slide from '@mui/material/Slide';
import type { TransitionProps } from '@mui/material/transitions';
-import { CloseSquare, Google, Apple } from 'iconsax-react';
+import { CloseCircle } from 'iconsax-react';
+import { Icon } from '@rkheftan/harmony-ui';
const MobileSlide = React.forwardRef(function MobileSlide(
- props: TransitionProps & { children: React.ReactElement },
+ props: TransitionProps & { children: ReactElement },
ref: React.Ref,
) {
return ;
});
+interface SocialMediaDialogProps {
+ open: boolean;
+ onClose: () => void;
+ t: (key: string) => string;
+ emailInput: string;
+ setEmailInput: (val: string) => void;
+ emailError: boolean;
+ setEmailError: (val: boolean) => void;
+ fullScreen: boolean;
+ computedMaxWidth: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
+}
+
export default function SocialMediaDialog({
open,
onClose,
@@ -30,17 +43,7 @@ export default function SocialMediaDialog({
setEmailError,
fullScreen,
computedMaxWidth,
-}: {
- open: boolean;
- onClose: () => void;
- t: (key: string) => string;
- emailInput: string;
- setEmailInput: (val: string) => void;
- emailError: boolean;
- setEmailError: (val: boolean) => void;
- fullScreen: boolean;
- computedMaxWidth: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
-}) {
+}: SocialMediaDialogProps) {
const handleEmailChange = (e: React.ChangeEvent) => {
const value = e.target.value;
setEmailInput(value);
@@ -59,30 +62,25 @@ export default function SocialMediaDialog({
TransitionComponent={fullScreen ? MobileSlide : undefined}
PaperProps={{
sx: {
- m: { xs: 0, sm: 2 },
borderRadius: { xs: 0, sm: 2 },
- width: { xs: '100%', sm: 'auto' },
- height: { xs: '100%', sm: 'auto' },
+ width: { xs: '100%', sm: '30%' },
+ height: { xs: '100%', sm: '43%' },
},
}}
>
-
+
{t('settingForm.addEmailButton')}
@@ -90,11 +88,9 @@ export default function SocialMediaDialog({
@@ -128,59 +124,6 @@ export default function SocialMediaDialog({
>
{t('settingForm.verificationCodeButton')}
-
-
-
-
- {t('settingForm.or')}
-
-
-
-
-
-
-
- {t('settingForm.google')}
-
-
-
- {t('settingForm.apple')}
-
-
);
diff --git a/src/features/profile/components/userInformation/socialMedia/SocialMediaList.tsx b/src/features/profile/components/userInformation/socialMedia/SocialMediaList.tsx
index 75c83de..443c5fc 100644
--- a/src/features/profile/components/userInformation/socialMedia/SocialMediaList.tsx
+++ b/src/features/profile/components/userInformation/socialMedia/SocialMediaList.tsx
@@ -1,16 +1,17 @@
import { Box, Typography, IconButton } from '@mui/material';
-import { Google, Apple, Sms, Trash } from 'iconsax-react';
+import { Google, Sms, Trash } from 'iconsax-react';
+import { Icon } from '@rkheftan/harmony-ui';
-export default function SocialMediaList({
- emailList,
-}: {
+interface SocialMediaListProps {
t: (key: string) => string;
emailList: readonly {
email: string;
provider: 'email' | 'google' | 'apple';
time: string;
}[];
-}) {
+}
+
+export default function SocialMediaList({ emailList }: SocialMediaListProps) {
return (
{emailList.map((item, index) => (
@@ -33,15 +34,34 @@ export default function SocialMediaList({
minWidth: 0,
}}
>
- {item.provider === 'google' && (
-
- )}
- {item.provider === 'apple' && (
-
- )}
- {item.provider === 'email' && (
-
- )}
+
+ {item.provider === 'google' && (
+
+ )}
+ {item.provider === 'email' && (
+
+ )}
+
@@ -53,8 +73,8 @@ export default function SocialMediaList({
-
-
+
+
))}
diff --git a/src/features/profile/components/userInformation/socialMedia/SocialMediaMenu.tsx b/src/features/profile/components/userInformation/socialMedia/SocialMediaMenu.tsx
index 006550b..ed0f652 100644
--- a/src/features/profile/components/userInformation/socialMedia/SocialMediaMenu.tsx
+++ b/src/features/profile/components/userInformation/socialMedia/SocialMediaMenu.tsx
@@ -8,27 +8,35 @@ import {
ListItemText,
} from '@mui/material';
import { Message, Google, Apple, ArrowDown3 } from 'iconsax-react';
+import { Icon } from '@rkheftan/harmony-ui';
+
+interface SocialMediaMenuProps {
+ t: (key: string) => string;
+ onOpenDialog: () => void;
+}
export default function SocialMediaMenu({
t,
onOpenDialog,
-}: {
- t: (key: string) => string;
- onOpenDialog: () => void;
-}) {
+}: SocialMediaMenuProps) {
const [anchor, setAnchor] = useState(null);
const openMenu = Boolean(anchor);
+ const [open, setOpen] = useState(false);
- const handleClickMenu = (e: React.MouseEvent) =>
+ const handleClickMenu = (e: React.MouseEvent) => {
+ setOpen(true);
setAnchor(e.currentTarget);
- const handleCloseMenu = () => setAnchor(null);
+ };
+ const handleCloseMenu = () => {
+ setOpen(false);
+ setAnchor(null);
+ };
return (
@@ -38,40 +46,28 @@ export default function SocialMediaMenu({
flexDirection: { xs: 'column', sm: 'row' },
alignItems: { xs: 'stretch', sm: 'flex-start' },
gap: 1,
- width: '100%',
}}
>
-
- {t('settingForm.addEmailOrSocialButton')}
-
-
+ {t('settingForm.addEmailOrSocialButton')}
+
@@ -81,7 +77,7 @@ export default function SocialMediaMenu({
onClose={handleCloseMenu}
PaperProps={{
sx: {
- minWidth: 240,
+ minWidth: 187,
maxWidth: '90vw',
},
}}
@@ -95,19 +91,19 @@ export default function SocialMediaMenu({
}}
>
-
+
{t('settingForm.email')}
diff --git a/src/features/profile/types.ts b/src/features/profile/types.ts
new file mode 100644
index 0000000..66caa4f
--- /dev/null
+++ b/src/features/profile/types.ts
@@ -0,0 +1,13 @@
+export enum Gender {
+ Male = 'male',
+ Female = 'female',
+ None = '',
+}
+
+export interface InfoRowData {
+ firstName: string;
+ lastName: string;
+ nationalCode: string;
+ country: string;
+ gender: Gender;
+}
diff --git a/src/providers/CustomThemeProvider.tsx b/src/providers/CustomThemeProvider.tsx
index 29df78d..0b19f41 100644
--- a/src/providers/CustomThemeProvider.tsx
+++ b/src/providers/CustomThemeProvider.tsx
@@ -27,6 +27,15 @@ export const CustomThemeProvider: React.FC<{ children: React.ReactNode }> = ({
},
spacing: 8,
typography: typography,
+ components: {
+ MuiButton: {
+ defaultProps: {
+ sx: {
+ textTransform: 'none',
+ },
+ },
+ },
+ },
});
}, [i18n]);