fix: translation and styles

This commit is contained in:
2025-07-20 17:19:22 +03:30
parent ff7b5ce4ac
commit 8bef6bb9ee
6 changed files with 193 additions and 72 deletions

View File

@@ -0,0 +1,36 @@
{
"settingForm": {
"titlePersonalInfo": "اطلاعات شخصی من",
"descriptionPersonalInfo": "این اطلاعات شما صرفا برای احراز هویت شما است و نزد هارمونی باقی می‌ماند",
"titlePhoneNumber": "شماره تماس من",
"descriptionPhoneNumber": "این اطلاعات شما صرفا برای احراز هویت شما است و نزد هارمونی باقی می‌ماند",
"titleSocial": "ایمیل و شبکه های اجتماعی من",
"descriptionSocial": "این اطلاعات شما صرفاً برای احراز هویت شما است و نزد هارمونی باقی می‌ماند",
"rejectButton": "لغو",
"saveButton": "ذخیره",
"editButton": "ویرایش",
"editPhoneNumber": "تغییر شماره تماس",
"addEmailOrSocialButton": "افزودن ایمیل / سوشال",
"addEmailButton": "افزودن ایمیل",
"name": "نام",
"familyName": "نام خانوادگی",
"country": "کشور",
"gender": "جنسیت",
"nationalCode": "کد ملی",
"man": "مرد",
"woman": "زن",
"genderPlaceholder": "مرد",
"newPhoneNumber": "شماره تماس جدید",
"verificationCodeButton": "ارسال کد تایید",
"verificationCode": "کد تایید",
"checkCode": "بررسی کد",
"successButton": "تایید شد",
"email": "ایمیل",
"apple": "اپل",
"google": "گوگل",
"newEmail": "ایمیل جدید",
"dialogHeader": "با فعال‌سازی ایمیل می‌توانید در دفعات بعدی ورود برای ورود از این ایمیل استفاده کنید",
"or": "یا",
"emailError": "لطفا یک ایمیل معتبر وارد کنید"
}
}

View File

@@ -13,6 +13,7 @@ import { LanguageManager } from './components/LanguageManager';
<<<<<<< HEAD <<<<<<< HEAD
<<<<<<< HEAD <<<<<<< HEAD
<<<<<<< HEAD <<<<<<< HEAD
<<<<<<< HEAD
======= =======
>>>>>>> f1620b6 (fix: issue in user profile) >>>>>>> f1620b6 (fix: issue in user profile)
@@ -27,6 +28,8 @@ import { UserProfileForm } from './features/profile/components/UserProfileForm';
======= =======
import { UserForm } from './features/profile/components/UserForm'; import { UserForm } from './features/profile/components/UserForm';
>>>>>>> 60c6dc1 (fix: styles) >>>>>>> 60c6dc1 (fix: styles)
=======
>>>>>>> afc7ff8 (fix: translation and styles)
function App() { function App() {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -34,7 +37,6 @@ function App() {
<> <>
<CssBaseline /> <CssBaseline />
<LanguageManager /> <LanguageManager />
<UserForm />
<div style={{ padding: '16px' }}> <div style={{ padding: '16px' }}>
<Typography variant="h3">{t('helloWorld')}</Typography> <Typography variant="h3">{t('helloWorld')}</Typography>
<Box <Box

View File

@@ -0,0 +1,46 @@
import { Box, Typography } from '@mui/material';
const countryCodeMap: { [key: string]: string } = {
ایران: 'ir',
قطر: 'qa',
آلمان: 'de',
آمریکا: 'us',
فرانسه: 'fr',
ایتالیا: 'it',
اسپانیا: 'es',
انگلیس: 'gb',
کانادا: 'ca',
استرالیا: 'au',
چین: 'cn',
ژاپن: 'jp',
هند: 'in',
روسیه: 'ru',
برزیل: 'br',
آرژانتین: 'ar',
ترکیه: 'tr',
سوئیس: 'ch',
سوئد: 'se',
نروژ: 'no',
عربستان: 'sa',
امارات: 'ae',
عراق: 'iq',
پاکستان: 'pk',
};
export function CountryFlag({ country }: { country: string }) {
const countryCode = countryCodeMap[country] || 'un';
const flagUrl = `https://flagcdn.com/w40/${countryCode}.png`;
return (
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<img
src={flagUrl}
alt={country}
width="24"
height="16"
style={{ borderRadius: '2px', border: '1px solid #ccc' }}
/>
<Typography variant="body2">{country}</Typography>
</Box>
);
}

View File

@@ -10,21 +10,27 @@ import {
} from '@mui/material'; } from '@mui/material';
import { useState, type ChangeEvent } from 'react'; import { useState, type ChangeEvent } from 'react';
import { CardContainer } from '@/components/CardContainer'; import { CardContainer } from '@/components/CardContainer';
import { CountryFlag } from '@/components/CountryFlag';
import { useTranslation } from 'react-i18next';
import { Profile } from 'iconsax-react';
export function PersonalInformation() { export function PersonalInformation() {
const { t } = useTranslation('profileSetting');
const [isEditing, setIsEditing] = useState(false); const [isEditing, setIsEditing] = useState(false);
const [gender, setGender] = useState(''); const [gender, setGender] = useState('');
const [data, setData] = useState({ const initialData = {
firstName: 'محمد حسین', firstName: 'محمد حسین',
lastName: 'برزه‌گر', lastName: 'برزه‌گر',
country: 'قطر',
gender: 'مرد', gender: 'مرد',
nationalCode: '', nationalCode: '',
}); };
const [data, setData] = useState(initialData);
const handleChange = (e: ChangeEvent<HTMLInputElement>) => { const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setData((prev) => ({ setData((prev) => ({
...prev, ...prev,
[e.target.name]: e.target.value, [name]: value,
})); }));
}; };
@@ -62,8 +68,8 @@ export function PersonalInformation() {
}} }}
> >
<CardContainer <CardContainer
title="اطلاعات شخصی من" title={t('settingForm.titlePersonalInfo')}
subtitle="این اطلاعات شما صرفا برای احراز هویت شما است و نزد هارمونی باقی می‌ماند" subtitle={t('settingForm.descriptionPersonalInfo')}
highlighted={isEditing} highlighted={isEditing}
action={ action={
<Box sx={{ display: 'flex', gap: 1 }}> <Box sx={{ display: 'flex', gap: 1 }}>
@@ -77,7 +83,7 @@ export function PersonalInformation() {
width: '43px', width: '43px',
}} }}
> >
لغو {t('settingForm.rejectButton')}
</Button> </Button>
)} )}
<Button <Button
@@ -91,11 +97,12 @@ export function PersonalInformation() {
color: isEditing ? 'white' : '#2979FF', color: isEditing ? 'white' : '#2979FF',
px: '22px', px: '22px',
py: '8px', py: '8px',
// minWidth: '93px',
width: isEditing ? '85px' : '93px', width: isEditing ? '85px' : '93px',
}} }}
> >
{isEditing ? 'ذخیره' : 'ویرایش'} {isEditing
? t('settingForm.saveButton')
: t('settingForm.editButton')}
</Button> </Button>
</Box> </Box>
} }
@@ -115,24 +122,30 @@ export function PersonalInformation() {
name="firstName" name="firstName"
value={data.firstName} value={data.firstName}
onChange={handleChange} onChange={handleChange}
label="نام" label={t('settingForm.name')}
/> />
) : ( ) : (
<Box <Box
sx={{ sx={{
height: '48px', height: 'auto',
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
justifyContent: 'center', justifyContent: 'center',
alignItems: 'flex-start',
gap: 0.5,
px: 4, px: 4,
}} }}
> >
<Typography variant="caption" color="text.secondary"> <Typography variant="caption" color="text.secondary">
نام {`${t('settingForm.name')} و ${t('settingForm.familyName')}`}
</Typography>
<Typography variant="body1" color="text.primary">
{displayValue(data.firstName)}
</Typography> </Typography>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<Profile size="20" color="gray" />
<Typography variant="body1" color="text.primary">
{displayValue(data.firstName + ' ' + data.lastName)}
</Typography>
</Box>
</Box> </Box>
)} )}
</Box> </Box>
@@ -144,7 +157,7 @@ export function PersonalInformation() {
name="lastName" name="lastName"
value={data.lastName} value={data.lastName}
onChange={handleChange} onChange={handleChange}
label="نام خانوادگی" label={t('settingForm.familyName')}
/> />
) : ( ) : (
<Box <Box
@@ -156,11 +169,9 @@ export function PersonalInformation() {
}} }}
> >
<Typography variant="caption" color="text.secondary"> <Typography variant="caption" color="text.secondary">
نام خانوادگی {t('settingForm.country')}
</Typography>
<Typography variant="body1" color="text.primary">
{displayValue(data.lastName)}
</Typography> </Typography>
<CountryFlag country={data.country} />
</Box> </Box>
)} )}
</Box> </Box>
@@ -172,9 +183,21 @@ export function PersonalInformation() {
value={gender} value={gender}
onChange={handleChangeGender} onChange={handleChangeGender}
displayEmpty displayEmpty
renderValue={(selected) => {
if (!selected) {
return (
<span style={{ color: '#aaa' }}>
{t('settingForm.genderPlaceholder')}
</span>
);
}
return selected === 'male'
? t('settingForm.man')
: t('settingForm.woman');
}}
> >
<MenuItem value="female">زن</MenuItem> <MenuItem value="male">{t('settingForm.man')}</MenuItem>
<MenuItem value="male">مرد</MenuItem> <MenuItem value="female">{t('settingForm.woman')}</MenuItem>
</Select> </Select>
</FormControl> </FormControl>
) : ( ) : (
@@ -188,7 +211,7 @@ export function PersonalInformation() {
}} }}
> >
<Typography variant="caption" color="text.secondary"> <Typography variant="caption" color="text.secondary">
جنسیت {t('settingForm.gender')}
</Typography> </Typography>
<Typography variant="body1" color="text.primary"> <Typography variant="body1" color="text.primary">
{displayValue(data.gender)} {displayValue(data.gender)}
@@ -204,7 +227,7 @@ export function PersonalInformation() {
name="nationalCode" name="nationalCode"
value={data.nationalCode} value={data.nationalCode}
onChange={handleChange} onChange={handleChange}
label="کد ملی" label={t('settingForm.nationalCode')}
/> />
) : ( ) : (
<Box <Box
@@ -216,7 +239,7 @@ export function PersonalInformation() {
}} }}
> >
<Typography variant="caption" color="text.secondary"> <Typography variant="caption" color="text.secondary">
کد ملی {t('settingForm.nationalCode')}{' '}
</Typography> </Typography>
<Typography variant="body1" color="text.primary"> <Typography variant="body1" color="text.primary">
{displayValue(data.nationalCode)} {displayValue(data.nationalCode)}

View File

@@ -7,11 +7,13 @@ import {
GlobalStyles, GlobalStyles,
} from '@mui/material'; } from '@mui/material';
import { Edit, Refresh, TickCircle } from 'iconsax-react'; import { Edit, Refresh, TickCircle } from 'iconsax-react';
import { useState, useEffect, type ChangeEvent } from 'react'; import { useState, type ChangeEvent } from 'react';
import { CardContainer } from '@/components/CardContainer'; import { CardContainer } from '@/components/CardContainer';
import { CountDownTimer } from '@/components/CountDownTimer'; import { CountDownTimer } from '@/components/CountDownTimer';
import { useTranslation } from 'react-i18next';
export function PhoneNumber() { export function PhoneNumber() {
const { t } = useTranslation('profileSetting');
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('');
@@ -21,6 +23,8 @@ export function PhoneNumber() {
const [isVerifying, setIsVerifying] = useState(false); const [isVerifying, setIsVerifying] = useState(false);
const [isVerified, setIsVerified] = useState(false); const [isVerified, setIsVerified] = useState(false);
const phones = [{ phone: '09123456789', time: '۱ ماه پیش' }];
const handleSendCode = () => { const handleSendCode = () => {
setButtonState('counting'); setButtonState('counting');
setIsVerified(false); setIsVerified(false);
@@ -71,8 +75,8 @@ export function PhoneNumber() {
}} }}
> >
<CardContainer <CardContainer
title="شماره تماس من" title={t('settingForm.titlePhoneNumber')}
subtitle="این اطلاعات شما صرفا برای احراز هویت شما است و نزد هارمونی باقی می‌ماند" subtitle={t('settingForm.descriptionPhoneNumber')}
highlighted={isEditing} highlighted={isEditing}
action={ action={
<Box sx={{ display: 'flex', gap: 1 }}> <Box sx={{ display: 'flex', gap: 1 }}>
@@ -83,7 +87,7 @@ export function PhoneNumber() {
size="large" size="large"
sx={{ color: '#2979FF', width: '43px' }} sx={{ color: '#2979FF', width: '43px' }}
> >
لغو {t('settingForm.rejectButton')}
</Button> </Button>
)} )}
<Button <Button
@@ -99,7 +103,9 @@ export function PhoneNumber() {
width: isEditing ? '85px' : '161px', width: isEditing ? '85px' : '161px',
}} }}
> >
{isEditing ? 'ذخیره' : 'تغییر شماره تماس'} {isEditing
? t('settingForm.saveButton')
: t('settingForm.editPhoneNumber')}
</Button> </Button>
</Box> </Box>
} }
@@ -108,7 +114,7 @@ export function PhoneNumber() {
<Box sx={{ px: 4 }}> <Box sx={{ px: 4 }}>
<Box sx={{ width: '668px', mb: 2 }}> <Box sx={{ width: '668px', mb: 2 }}>
<Typography variant="h6" color="text.primary"> <Typography variant="h6" color="text.primary">
تغییر شماره تماس {t('settingForm.editPhoneNumber')}
</Typography> </Typography>
<Typography variant="body2" color="text.secondary"> <Typography variant="body2" color="text.secondary">
شماره تماس جدید شما جایگزین شماره تماس قبلی(+989123456789) شماره تماس جدید شما جایگزین شماره تماس قبلی(+989123456789)
@@ -127,7 +133,7 @@ export function PhoneNumber() {
<TextField <TextField
name="phoneNumber" name="phoneNumber"
placeholder="09123456789" placeholder="09123456789"
label="شماره تماس جدید" label={t('settingForm.newPhoneNumber')}
type="tel" type="tel"
value={phoneNumber} value={phoneNumber}
onChange={handlePhoneNumberChange} onChange={handlePhoneNumberChange}
@@ -158,7 +164,7 @@ export function PhoneNumber() {
}} }}
> >
<TickCircle size="20" color="#2e7d32" variant="Bold" /> <TickCircle size="20" color="#2e7d32" variant="Bold" />
تایید شد {t('profileSetting.successButton')}
</Box> </Box>
) : ( ) : (
<Button <Button
@@ -174,7 +180,7 @@ export function PhoneNumber() {
onComplete={() => setButtonState('default')} onComplete={() => setButtonState('default')}
/> />
) : ( ) : (
'ارسال کد تایید' t('settingForm.verificationCodeButton')
)} )}
</Button> </Button>
)} )}
@@ -192,8 +198,8 @@ export function PhoneNumber() {
> >
<TextField <TextField
name="verificationCode" name="verificationCode"
placeholder="کد تایید" placeholder={t('settingForm.verificationCode')}
label="کد تایید" label={t('settingForm.verificationCode')}
type="tel" type="tel"
value={verificationCode} value={verificationCode}
onChange={handleVerificationCodeChange} onChange={handleVerificationCodeChange}
@@ -223,28 +229,33 @@ export function PhoneNumber() {
<Refresh size="20" color="white" /> <Refresh size="20" color="white" />
</Box> </Box>
) : ( ) : (
'بررسی کد' t('settingForm.checkCode')
)} )}
</Button> </Button>
</Box> </Box>
)} )}
</Box> </Box>
) : ( ) : (
<Box <Box sx={{ px: 4 }}>
sx={{ {phones.map((item, index) => (
height: '48px', <Box
display: 'flex', key={index}
flexDirection: 'column', sx={{
justifyContent: 'center', height: '48px',
px: 4, display: 'flex',
}} flexDirection: 'column',
> justifyContent: 'center',
<Typography variant="h6" color="text.primary"> mb: 1,
09909366045 }}
</Typography> >
<Typography variant="caption" color="text.secondary"> <Typography variant="h6" color="text.primary">
۱ ماه پیش {item.phone}
</Typography> </Typography>
<Typography variant="caption" color="text.secondary">
{item.time}
</Typography>
</Box>
))}
</Box> </Box>
)} )}
</CardContainer> </CardContainer>

View File

@@ -23,8 +23,10 @@ import {
} from '@mui/material'; } from '@mui/material';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { CardContainer } from '@/components/CardContainer'; import { CardContainer } from '@/components/CardContainer';
import { useTranslation } from 'react-i18next';
export function SocialMedia() { export function SocialMedia() {
const { t } = useTranslation('profileSetting');
const [openDialog, setOpenDialog] = useState(false); const [openDialog, setOpenDialog] = useState(false);
const [emailInput, setEmailInput] = useState(''); const [emailInput, setEmailInput] = useState('');
const [emailError, setEmailError] = useState(false); const [emailError, setEmailError] = useState(false);
@@ -45,8 +47,8 @@ export function SocialMedia() {
}; };
const emailList = [ const emailList = [
{ email: 'emailtemp@email.com', provider: 'email' }, { email: 'emailtemp@email.com', provider: 'email', time: '1 ماه پیش' },
{ email: 'emailtemp@gmail.com', provider: 'google' }, { email: 'emailtemp@gmail.com', provider: 'google', time: '1 ماه پیش' },
]; ];
return ( return (
@@ -59,8 +61,8 @@ export function SocialMedia() {
}} }}
> >
<CardContainer <CardContainer
title="ایمیل و شبکه های اجتماعی من" title={t('settingForm.titleSocial')}
subtitle="این اطلاعات شما صرفاً برای احراز هویت شما است و نزد هارمونی باقی می‌ماند" subtitle={t('settingForm.descriptionSocial')}
action={ action={
<Box sx={{ display: 'flex', justifyContent: 'flex-start' }}> <Box sx={{ display: 'flex', justifyContent: 'flex-start' }}>
<Button <Button
@@ -80,7 +82,7 @@ export function SocialMedia() {
px: 2, px: 2,
}} }}
> >
افزودن ایمیل / سوشال {t('settingForm.addEmailOrSocialButton')}
{openMenu && <ArrowDown3 size="20" color="#2979FF" />} {openMenu && <ArrowDown3 size="20" color="#2979FF" />}
</Button> </Button>
<Menu <Menu
@@ -102,7 +104,7 @@ export function SocialMedia() {
<ListItemIcon> <ListItemIcon>
<Message size={20} color="black" /> <Message size={20} color="black" />
</ListItemIcon> </ListItemIcon>
<ListItemText>ایمیل</ListItemText> <ListItemText>{t('settingForm.email')}</ListItemText>
</MenuItem> </MenuItem>
<MenuItem <MenuItem
onClick={() => { onClick={() => {
@@ -113,7 +115,7 @@ export function SocialMedia() {
<ListItemIcon> <ListItemIcon>
<Google size={20} color="#4285F4" /> <Google size={20} color="#4285F4" />
</ListItemIcon> </ListItemIcon>
<ListItemText>گوگل</ListItemText> <ListItemText>{t('settingForm.google')}</ListItemText>
</MenuItem> </MenuItem>
<MenuItem <MenuItem
onClick={() => { onClick={() => {
@@ -124,7 +126,7 @@ export function SocialMedia() {
<ListItemIcon> <ListItemIcon>
<Apple size={20} color="black" /> <Apple size={20} color="black" />
</ListItemIcon> </ListItemIcon>
<ListItemText>اپل</ListItemText> <ListItemText>{t('settingForm.apple')}</ListItemText>
</MenuItem> </MenuItem>
</Menu> </Menu>
</Box> </Box>
@@ -180,7 +182,7 @@ export function SocialMedia() {
{item.email} {item.email}
</Typography> </Typography>
<Typography variant="caption" color="text.secondary"> <Typography variant="caption" color="text.secondary">
۱ ماه پیش {item.time}
</Typography> </Typography>
</Box> </Box>
</Box> </Box>
@@ -211,7 +213,7 @@ export function SocialMedia() {
<IconButton onClick={handleCloseDialog} size="small"> <IconButton onClick={handleCloseDialog} size="small">
<CloseSquare size="24" color="gray" /> <CloseSquare size="24" color="gray" />
</IconButton> </IconButton>
افزودن ایمیل {t('settingForm.addEmailButton')}
</DialogTitle> </DialogTitle>
<DialogContent> <DialogContent>
<Box <Box
@@ -223,10 +225,11 @@ export function SocialMedia() {
}} }}
> >
<Box> <Box>
<Typography fontWeight="bold">ایمیل جدید</Typography> <Typography fontWeight="bold">
{t('settingForm.newEmail')}
</Typography>
<Typography variant="body2" color="text.secondary"> <Typography variant="body2" color="text.secondary">
با فعالسازی ایمیل میتوانید در دفعات بعدی ورود برای ورود از {t('settingForm.dialogHeader')}
این ایمیل استفاده کنید
</Typography> </Typography>
</Box> </Box>
<TextField <TextField
@@ -235,8 +238,8 @@ export function SocialMedia() {
value={emailInput} value={emailInput}
onChange={handleEmailChange} onChange={handleEmailChange}
error={emailError} error={emailError}
helperText={emailError ? 'لطفا یک ایمیل معتبر وارد کنید' : ''} helperText={emailError ? t('settingForm.emailError') : ''}
label="ایمیل" label={t('settingForm.email')}
placeholder="abc@email.com" placeholder="abc@email.com"
sx={{ sx={{
'& .MuiOutlinedInput-root': { '& .MuiOutlinedInput-root': {
@@ -253,13 +256,13 @@ export function SocialMedia() {
}} }}
disabled={emailError || emailInput === ''} disabled={emailError || emailInput === ''}
> >
ارسال کد تایید {t('settingForm.verificationCodeButton')}
</Button> </Button>
<Box sx={{ display: 'flex', alignItems: 'center', my: 2 }}> <Box sx={{ display: 'flex', alignItems: 'center', my: 2 }}>
<Box sx={{ flex: 1, height: '1px', backgroundColor: '#ccc' }} /> <Box sx={{ flex: 1, height: '1px', backgroundColor: '#ccc' }} />
<Typography sx={{ mx: 1, fontSize: '12px', color: 'gray' }}> <Typography sx={{ mx: 1, fontSize: '12px', color: 'gray' }}>
یا {t('settingForm.or')}
</Typography> </Typography>
<Box sx={{ flex: 1, height: '1px', backgroundColor: '#ccc' }} /> <Box sx={{ flex: 1, height: '1px', backgroundColor: '#ccc' }} />
</Box> </Box>
@@ -278,7 +281,7 @@ export function SocialMedia() {
}} }}
> >
<Google size="20" color="#4285F4" style={{ marginLeft: 8 }} /> <Google size="20" color="#4285F4" style={{ marginLeft: 8 }} />
گوگل {t('settingForm.google')}
</Button> </Button>
<Button <Button
sx={{ sx={{
@@ -293,7 +296,7 @@ export function SocialMedia() {
}} }}
> >
<Apple size="20" color="black" style={{ marginLeft: 8 }} /> <Apple size="20" color="black" style={{ marginLeft: 8 }} />
اپل {t('settingForm.apple')}
</Button> </Button>
</Box> </Box>
</Box> </Box>