diff --git a/package-lock.json b/package-lock.json
index fe1023c..24da072 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -17,8 +17,10 @@
"i18next-http-backend": "^3.0.2",
"iconsax-react": "^0.0.8",
"react": "^19.1.0",
+ "react-country-flag": "^3.1.0",
"react-dom": "^19.1.0",
"react-i18next": "^15.6.0",
+ "react-virtuoso": "^4.13.0",
"stylis": "^4.3.6",
"stylis-plugin-rtl": "^2.1.1"
},
@@ -1067,9 +1069,9 @@
}
},
"node_modules/@eslint/plugin-kit": {
- "version": "0.3.3",
- "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.3.tgz",
- "integrity": "sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==",
+ "version": "0.3.4",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz",
+ "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
@@ -3722,6 +3724,18 @@
"node": ">=0.10.0"
}
},
+ "node_modules/react-country-flag": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/react-country-flag/-/react-country-flag-3.1.0.tgz",
+ "integrity": "sha512-JWQFw1efdv9sTC+TGQvTKXQg1NKbDU2mBiAiRWcKM9F1sK+/zjhP2yGmm8YDddWyZdXVkR8Md47rPMJmo4YO5g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "peerDependencies": {
+ "react": ">=16"
+ }
+ },
"node_modules/react-dom": {
"version": "19.1.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
@@ -3792,6 +3806,16 @@
"react-dom": ">=16.6.0"
}
},
+ "node_modules/react-virtuoso": {
+ "version": "4.13.0",
+ "resolved": "https://registry.npmjs.org/react-virtuoso/-/react-virtuoso-4.13.0.tgz",
+ "integrity": "sha512-XHv2Fglpx80yFPdjZkV9d1baACKghg/ucpDFEXwaix7z0AfVQj+mF6lM+YQR6UC/TwzXG2rJKydRMb3+7iV3PA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": ">=16 || >=17 || >= 18 || >= 19",
+ "react-dom": ">=16 || >=17 || >= 18 || >=19"
+ }
+ },
"node_modules/resolve": {
"version": "1.22.10",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
diff --git a/package.json b/package.json
index 91571b6..b2ffcf4 100644
--- a/package.json
+++ b/package.json
@@ -20,8 +20,10 @@
"i18next-http-backend": "^3.0.2",
"iconsax-react": "^0.0.8",
"react": "^19.1.0",
+ "react-country-flag": "^3.1.0",
"react-dom": "^19.1.0",
"react-i18next": "^15.6.0",
+ "react-virtuoso": "^4.13.0",
"stylis": "^4.3.6",
"stylis-plugin-rtl": "^2.1.1"
},
diff --git a/public/locales/en/setting.json b/public/locales/en/setting.json
index 7024be4..c5c78f4 100644
--- a/public/locales/en/setting.json
+++ b/public/locales/en/setting.json
@@ -9,6 +9,10 @@
"light": "Light",
"dark": "Dark",
"language": "زبان/language",
- "calendar": "Calendar and date format"
+ "calendar": "Calendar and date format",
+ "solar": "Solar",
+ "lunar": "Lunar",
+ "christian": "Christian",
+ "iran": "Iran"
}
}
diff --git a/public/locales/fa/setting.json b/public/locales/fa/setting.json
index 948eb7a..1b07a7a 100644
--- a/public/locales/fa/setting.json
+++ b/public/locales/fa/setting.json
@@ -9,6 +9,10 @@
"light": "روشن",
"dark": "تاریک",
"language": "زبان/language",
- "calendar": "فرمت تقویم و تاریخ"
+ "calendar": "فرمت تقویم و تاریخ",
+ "solar": "شمسی",
+ "lunar": "قمری",
+ "christian": "میلادی",
+ "iran": "ایران"
}
}
diff --git a/src/features/profile/components/activeDevices/ActiveDevices.tsx b/src/features/profile/components/activeDevices/ActiveDevices.tsx
index 350bb2e..f4b4bb6 100644
--- a/src/features/profile/components/activeDevices/ActiveDevices.tsx
+++ b/src/features/profile/components/activeDevices/ActiveDevices.tsx
@@ -45,6 +45,7 @@ export function ActiveDevices() {
maxWidth: '834px',
mx: 'auto',
px: { xs: 2, sm: 3 },
+ py: 4,
}}
>
-
-
+
+
-
+
{t('active.activeDevices')}
{t('active.activeDevicesCaption')}
@@ -88,87 +82,111 @@ export function ActiveDevices() {
borderColor: 'error.main',
color: 'error.main',
border: '1px solid',
+ textTransform: 'none',
}}
>
{t('active.deletDevicesButton')}
-
- {devices.map((device) => (
+ {devices.map((device) => (
+
+
+ {device.timeAndDate}
+
+
-
- {device.timeAndDate}
+
+
+ {device.deviceModel}
+
-
-
-
- {device.deviceModel}
-
-
+
+ {device.ip}
+
-
- {device.ip}
-
-
-
- {device.current ? (
-
- ) : null}
-
-
-
+
+ {device.current && (
}
sx={{
- width: '80%',
borderRadius: '15px',
- border: '1px solid',
- borderColor: 'error.main',
+ border: '2px solid',
+ borderColor: 'success.main',
height: '30px',
whiteSpace: 'nowrap',
- color: 'error.main',
+ color: 'success.main',
+ width: '75%',
+ textTransform: 'none',
}}
- disabled={device.current}
>
- {t('active.deleteDevice')}
+ {t('active.currentDevice')}
-
+ )}
- ))}
-
+
+
+ }
+ disabled={device.current}
+ sx={{
+ color: 'error.main',
+ width: '80%',
+ minWidth: 0,
+ borderRadius: '15px',
+ borderColor: 'error.main',
+ p: '4px 8px',
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ whiteSpace: 'nowrap',
+ textTransform: 'none',
+ '& .MuiButton-startIcon': {
+ marginRight: '4px',
+ marginLeft: 0,
+ },
+ }}
+ >
+ {t('active.deleteDevice')}
+
+
+
+ ))}
);
diff --git a/src/features/profile/components/security/UserSecurity.tsx b/src/features/profile/components/security/UserSecurity.tsx
index ae794a4..578e0bf 100644
--- a/src/features/profile/components/security/UserSecurity.tsx
+++ b/src/features/profile/components/security/UserSecurity.tsx
@@ -8,7 +8,9 @@ import {
TextField,
DialogActions,
IconButton,
+ useMediaQuery,
} from '@mui/material';
+import { useTheme } from '@mui/material/styles';
import { useTranslation } from 'react-i18next';
import { useState, useEffect } from 'react';
import { CloseCircle, Refresh } from 'iconsax-react';
@@ -18,6 +20,8 @@ import Logo from '@/components/Logo';
import { CardContainer } from '@/components/CardContainer';
export function UserSecurity() {
+ const theme = useTheme();
+ const fullScreen = useMediaQuery(theme.breakpoints.down('sm'));
const { t } = useTranslation('security');
const [open, setOpen] = useState(false);
const [password, setPassword] = useState('');
@@ -63,14 +67,14 @@ export function UserSecurity() {
}, [password, validPassword]);
return (
-
+
-
+
@@ -119,30 +110,22 @@ export function UserSecurity() {
}
>
-
+
{changePassword ? (
-
+
رمز عبور فعال است
آخرین تغییر چند ثانیه پیش
) : (
-
-
- {t('securityForm.notDeterminedPassword')}
-
-
+ {t('securityForm.notDeterminedPassword')}
+
)}
@@ -150,21 +133,13 @@ export function UserSecurity() {
open={open}
onClose={handleClose}
fullWidth
+ fullScreen={fullScreen}
maxWidth="xs"
scroll="body"
- PaperProps={{
- sx: { mx: 1 },
- }}
+ PaperProps={{ sx: { mx: 1 } }}
>
-
+
@@ -182,48 +157,18 @@ export function UserSecurity() {
px: { xs: 2, sm: 3 },
}}
>
-
- setPassword(e.target.value)}
- sx={{
- '& .MuiOutlinedInput-root': {
- borderRadius: 2,
- },
- }}
- />
-
+ setPassword(e.target.value)}
+ sx={{ '& .MuiOutlinedInput-root': { borderRadius: 2 } }}
+ />
{password && showValidation && (
-
-
+
+
)}
-
- setConfirmPassword(e.target.value)}
- error={confirmPassword.length > 0 && !matchPassword}
- helperText={
- confirmPassword.length > 0 && !matchPassword
- ? t('securityForm.notCompatibility')
- : ' '
- }
- sx={{
- '& .MuiOutlinedInput-root': {
- borderRadius: 2,
- },
- }}
- />
-
+ setConfirmPassword(e.target.value)}
+ error={confirmPassword.length > 0 && !matchPassword}
+ helperText={
+ confirmPassword.length > 0 && !matchPassword
+ ? t('securityForm.notCompatibility')
+ : ' '
+ }
+ sx={{ '& .MuiOutlinedInput-root': { borderRadius: 2 } }}
+ />
-
+
)}
}
/>
-
-
-
- {isEditing ? (
-
- {t('settings.theme')}
-
-
- ) : (
- <>
-
- {t('settings.theme')}
-
-
- {mode === 'light'
- ? t('settings.light')
- : t('settings.dark')}
-
- >
- )}
-
-
- {isEditing ? (
- option.fa}
- value={selectedLanguage || null}
- onChange={(_, newValue) => {
- if (newValue) setSelectedLang(newValue.code);
- }}
- renderOption={(props, option) => (
-
-
-
- )}
- renderInput={(params) => (
-
- )}
- />
- ) : (
- <>
-
- {t('settings.language')}
-
-
- >
- )}
-
+
+
+ {isEditing ? (
+
+ {t('settings.theme')}
+
+
+ ) : (
+
+
+ {t('settings.theme')}
+
+
+ {mode === 'light' ? t('settings.light') : t('settings.dark')}
+
+
+ )}
-
+
{isEditing ? (
option.label}
+ value={
+ languageOptions.find((opt) => opt.code === draftLanguage) ||
+ null
+ }
+ onChange={handleDraftLanguageChange}
+ renderInput={(params) => (
+
+ )}
+ size="small"
+ sx={{ width: 337, height: '56px' }}
+ />
+ ) : (
+
+
+ {t('settings.language')}
+
+
+ {
+ languageOptions.find((opt) => opt.code === savedLanguage)
+ ?.label
+ }
+
+
+ )}
+
+
+
+
+
+ {isEditing ? (
+ {
- if (newValue) setSelectedCalendar(newValue);
- }}
+ onChange={(_, newVal) => newVal && setSelectedCalendar(newVal)}
renderInput={(params) => (
)}
- sx={{ width: '337px' }}
+ size="small"
+ sx={{ width: 337, height: 56 }}
/>
) : (
- <>
+
{t('settings.calendar')}
{selectedCalendar}
- >
+
)}
diff --git a/src/features/profile/components/setting/data/Languages.tsx b/src/features/profile/components/setting/data/Languages.tsx
deleted file mode 100644
index 1c95f00..0000000
--- a/src/features/profile/components/setting/data/Languages.tsx
+++ /dev/null
@@ -1,44 +0,0 @@
-import { Box, Typography } from '@mui/material';
-
-export const Languages = [
- { code: 'en', en: 'English', fa: 'انگلیسی' },
- { code: 'fa', en: 'Persian (Farsi)', fa: 'فارسی' },
- { code: 'ar', en: 'Arabic', fa: 'عربی' },
- { code: 'fr', en: 'French', fa: 'فرانسوی' },
- { code: 'de', en: 'German', fa: 'آلمانی' },
- { code: 'es', en: 'Spanish', fa: 'اسپانیایی' },
- { code: 'it', en: 'Italian', fa: 'ایتالیایی' },
- { code: 'ru', en: 'Russian', fa: 'روسی' },
- { code: 'zh', en: 'Chinese', fa: 'چینی' },
- { code: 'ja', en: 'Japanese', fa: 'ژاپنی' },
- { code: 'ko', en: 'Korean', fa: 'کرهای' },
- { code: 'hi', en: 'Hindi', fa: 'هندی' },
- { code: 'tr', en: 'Turkish', fa: 'ترکی استانبولی' },
- { code: 'pt', en: 'Portuguese', fa: 'پرتغالی' },
- { code: 'ur', en: 'Urdu', fa: 'اردو' },
- { code: 'nl', en: 'Dutch', fa: 'هلندی' },
- { code: 'sv', en: 'Swedish', fa: 'سوئدی' },
- { code: 'pl', en: 'Polish', fa: 'لهستانی' },
- { code: 'th', en: 'Thai', fa: 'تایلندی' },
- { code: 'id', en: 'Indonesian', fa: 'اندونزیایی' },
-];
-
-export function CountryFlag({ country }: { country: string }) {
- const lang = Languages.find((lang) => lang.code === country);
- const countryCode = lang?.code || 'un';
-
- const flagUrl = `https://flagcdn.com/w40/${countryCode}.png`;
-
- return (
-
-
- {lang ? lang.fa : 'نامشخص'}
-
- );
-}