chore: change the styles and add countries to the user profile
This commit is contained in:
@@ -1,40 +0,0 @@
|
||||
// components/Countries.ts
|
||||
|
||||
export interface Country {
|
||||
name: string;
|
||||
fa: string;
|
||||
flag: string;
|
||||
}
|
||||
|
||||
export const countries: Country[] = [
|
||||
{ name: 'Afghanistan', fa: 'افغانستان', flag: 'af' },
|
||||
{ name: 'Albania', fa: 'آلبانی', flag: 'al' },
|
||||
{ name: 'Algeria', fa: 'الجزایر', flag: 'dz' },
|
||||
{ name: 'Argentina', fa: 'آرژانتین', flag: 'ar' },
|
||||
{ name: 'Armenia', fa: 'ارمنستان', flag: 'am' },
|
||||
{ name: 'Australia', fa: 'استرالیا', flag: 'au' },
|
||||
{ name: 'Austria', fa: 'اتریش', flag: 'at' },
|
||||
{ name: 'Bahrain', fa: 'بحرین', flag: 'bh' },
|
||||
{ name: 'Canada', fa: 'کانادا', flag: 'ca' },
|
||||
{ name: 'China', fa: 'چین', flag: 'cn' },
|
||||
{ name: 'France', fa: 'فرانسه', flag: 'fr' },
|
||||
{ name: 'Germany', fa: 'آلمان', flag: 'de' },
|
||||
{ name: 'India', fa: 'هند', flag: 'in' },
|
||||
{ name: 'Iran', fa: 'ایران', flag: 'ir' },
|
||||
{ name: 'Iraq', fa: 'عراق', flag: 'iq' },
|
||||
{ name: 'Italy', fa: 'ایتالیا', flag: 'it' },
|
||||
{ name: 'Japan', fa: 'ژاپن', flag: 'jp' },
|
||||
{ name: 'Netherlands', fa: 'هلند', flag: 'nl' },
|
||||
{ name: 'Pakistan', fa: 'پاکستان', flag: 'pk' },
|
||||
{ name: 'Qatar', fa: 'قطر', flag: 'qa' },
|
||||
{ name: 'Russia', fa: 'روسیه', flag: 'ru' },
|
||||
{ name: 'Saudi Arabia', fa: 'عربستان سعودی', flag: 'sa' },
|
||||
{ name: 'Spain', fa: 'اسپانیا', flag: 'es' },
|
||||
{ name: 'Sweden', fa: 'سوئد', flag: 'se' },
|
||||
{ name: 'Switzerland', fa: 'سوئیس', flag: 'ch' },
|
||||
{ name: 'Turkey', fa: 'ترکیه', flag: 'tr' },
|
||||
{ name: 'United Arab Emirates', fa: 'امارات متحده عربی', flag: 'ae' },
|
||||
{ name: 'United Kingdom', fa: 'بریتانیا', flag: 'gb' },
|
||||
{ name: 'United States', fa: 'ایالات متحده آمریکا', flag: 'us' },
|
||||
{ name: 'Yemen', fa: 'یمن', flag: 'ye' },
|
||||
];
|
||||
@@ -1,46 +1,34 @@
|
||||
import { Box, Typography } from '@mui/material';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { countries } from '@/features/profile/data/countries';
|
||||
|
||||
export 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',
|
||||
};
|
||||
interface CountryFlagProps {
|
||||
code: string;
|
||||
}
|
||||
|
||||
export function CountryFlag({ country }: { country: string }) {
|
||||
const countryCode = countryCodeMap[country] || 'un';
|
||||
const flagUrl = `https://flagcdn.com/w40/${countryCode}.png`;
|
||||
export function CountryFlag({ code }: CountryFlagProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const countryObj = code ? countries.find((c) => c.code === code) : null;
|
||||
|
||||
if (!countryObj) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const displayName = t(countryObj.label);
|
||||
const flagUrl = `https://flagcdn.com/w40/${countryObj.code.toLowerCase()}.png`;
|
||||
|
||||
return (
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<img
|
||||
loading="lazy"
|
||||
src={flagUrl}
|
||||
alt={country}
|
||||
alt={displayName}
|
||||
width="24"
|
||||
height="16"
|
||||
style={{ borderRadius: '2px', border: '1px solid #ccc' }}
|
||||
/>
|
||||
<Typography variant="body2">{country}</Typography>
|
||||
<Typography variant="body2">{displayName}</Typography>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,12 +2,17 @@ import { ToggleButtonGroup, ToggleButton, Box } from '@mui/material';
|
||||
import { useColorScheme } from '@mui/material/styles';
|
||||
import { Sun1, Moon } from 'iconsax-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Icon } from '@rkheftan/harmony-ui';
|
||||
import { type MouseEvent } from 'react';
|
||||
|
||||
export const ThemeToggleButton = () => {
|
||||
const { mode, setMode } = useColorScheme();
|
||||
const { t } = useTranslation('setting');
|
||||
|
||||
const handleChange = (_: any, newMode: 'light' | 'dark' | null) => {
|
||||
const handleChange = (
|
||||
_event: MouseEvent<HTMLElement>,
|
||||
newMode: 'light' | 'dark' | null,
|
||||
) => {
|
||||
if (newMode !== null) {
|
||||
setMode(newMode);
|
||||
localStorage.setItem('theme', newMode);
|
||||
@@ -41,7 +46,7 @@ export const ThemeToggleButton = () => {
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Sun1 size={18} color="#2979FF" variant="Bold" />
|
||||
<Icon Component={Sun1} color="primary.main" variant="Bold" />
|
||||
{t('settings.light')}
|
||||
</ToggleButton>
|
||||
|
||||
@@ -59,7 +64,7 @@ export const ThemeToggleButton = () => {
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Moon size={18} color="#82B1FF" />
|
||||
<Icon Component={Moon} size="medium" color="primary.light" />
|
||||
{t('settings.dark')}
|
||||
</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
|
||||
@@ -11,6 +11,8 @@ import { CardContainer } from '@/components/CardContainer';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ThemeToggleButton } from '@/components/ThemToggle';
|
||||
import { PageWrapper } from '../PageWrapper';
|
||||
import { Icon } from '@rkheftan/harmony-ui';
|
||||
import { Sun1, Moon, Calendar1 } from 'iconsax-react';
|
||||
|
||||
export function Setting() {
|
||||
const { t, i18n } = useTranslation(['setting']);
|
||||
@@ -21,18 +23,18 @@ export function Setting() {
|
||||
);
|
||||
const [draftLanguage, setDraftLanguage] = useState<string>(savedLanguage);
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const [selectedCalendar, setSelectedCalendar] = useState<string>(
|
||||
t('settings.solar'),
|
||||
);
|
||||
const [selectedCalendar, setSelectedCalendar] = useState<
|
||||
'christian' | 'solar' | 'lunar'
|
||||
>('solar');
|
||||
|
||||
const languageOptions = [
|
||||
{ code: 'en', label: 'English' },
|
||||
{ code: 'fa', label: 'فارسی' },
|
||||
];
|
||||
const calendarOptions = [
|
||||
t('settings.christian'),
|
||||
t('settings.solar'),
|
||||
t('settings.lunar'),
|
||||
const calendarOptions: ('christian' | 'solar' | 'lunar')[] = [
|
||||
'christian',
|
||||
'solar',
|
||||
'lunar',
|
||||
];
|
||||
|
||||
const handleDraftLanguageChange = (
|
||||
@@ -56,6 +58,10 @@ export function Setting() {
|
||||
const handleEditToggle = () =>
|
||||
isEditing ? handleSave() : setIsEditing(true);
|
||||
|
||||
// useEffect(() => {
|
||||
// setSelectedCalendar(t('settings.solar'));
|
||||
// }, [i18n.language, t]);
|
||||
|
||||
return (
|
||||
<PageWrapper>
|
||||
<Box
|
||||
@@ -141,11 +147,26 @@ export function Setting() {
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
{t('settings.theme')}
|
||||
</Typography>
|
||||
<Typography variant="body1">
|
||||
{mode === 'light'
|
||||
? t('settings.light')
|
||||
: t('settings.dark')}
|
||||
</Typography>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 1,
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
Component={mode === 'light' ? Sun1 : Moon}
|
||||
size="medium"
|
||||
variant="Bold"
|
||||
color={mode === 'light' ? 'black' : 'primary.main'}
|
||||
/>
|
||||
<Typography variant="body1">
|
||||
{mode === 'light'
|
||||
? t('settings.light')
|
||||
: t('settings.dark')}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
@@ -184,10 +205,11 @@ export function Setting() {
|
||||
{isEditing ? (
|
||||
<Autocomplete
|
||||
options={calendarOptions}
|
||||
getOptionLabel={(key) => t(`settings.${key}`)}
|
||||
value={selectedCalendar}
|
||||
onChange={(_, v) => v && setSelectedCalendar(v)}
|
||||
renderInput={(p) => (
|
||||
<TextField {...p} label={t('settings.calendar')} />
|
||||
renderInput={(params) => (
|
||||
<TextField {...params} label={t('settings.calendar')} />
|
||||
)}
|
||||
size="medium"
|
||||
fullWidth
|
||||
@@ -197,7 +219,17 @@ export function Setting() {
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
{t('settings.calendar')}
|
||||
</Typography>
|
||||
<Typography variant="body1">{selectedCalendar}</Typography>
|
||||
<Box sx={{ display: 'flex', gap: 1 }}>
|
||||
<Icon
|
||||
Component={Calendar1}
|
||||
size="medium"
|
||||
color={mode === 'light' ? 'black' : 'primary.main'}
|
||||
variant="Bold"
|
||||
/>
|
||||
<Typography variant="body1">
|
||||
{t(`settings.${selectedCalendar}`)}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Box, Typography, Avatar } from '@mui/material';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { CountryFlag } from '@/components/CountryFlag';
|
||||
import { DisplayField } from './DisplayField';
|
||||
import { CountryFlag } from '@/components/CountryFlag';
|
||||
import { Gender } from '@/features/profile/types';
|
||||
|
||||
interface InfoRowData {
|
||||
@@ -26,6 +26,7 @@ export function InfoRowDisplay({
|
||||
const { t } = useTranslation('profileSetting');
|
||||
const displayValue = (value: string) =>
|
||||
value?.trim() || t('settingForm.notDetermined');
|
||||
|
||||
const getGenderLabel = (gender: Gender | '') => {
|
||||
switch (gender) {
|
||||
case Gender.Male:
|
||||
@@ -36,6 +37,7 @@ export function InfoRowDisplay({
|
||||
return t('settingForm.notDetermined');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Box sx={{ width: '100%' }}>
|
||||
@@ -70,7 +72,9 @@ export function InfoRowDisplay({
|
||||
{initials}
|
||||
</Avatar>
|
||||
<Typography variant="body1" color="text.primary">
|
||||
{`${displayValue(data.firstName)} ${displayValue(data.lastName)}`}
|
||||
{`${displayValue(data.firstName)} ${displayValue(
|
||||
data.lastName,
|
||||
)}`}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
@@ -79,7 +83,15 @@ export function InfoRowDisplay({
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
{t('settingForm.country')}
|
||||
</Typography>
|
||||
<CountryFlag country={data.country} />
|
||||
<Box sx={{ mt: 0.5 }}>
|
||||
{data.country ? (
|
||||
<CountryFlag code={data.country} />
|
||||
) : (
|
||||
<Typography variant="body1" color="text.primary">
|
||||
{t('settingForm.notDetermined')}
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
@@ -7,7 +7,8 @@ import {
|
||||
Autocomplete,
|
||||
} from '@mui/material';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { CountryFlag, countryCodeMap } from '@/components/CountryFlag';
|
||||
import { countries } from '@/features/profile/data/countries';
|
||||
import { CountryFlag } from '@/components/CountryFlag';
|
||||
import { Gender } from '@/features/profile/types';
|
||||
import { type InfoRowData } from '@/features/profile/types';
|
||||
|
||||
@@ -24,44 +25,50 @@ export function InfoRowEdit({
|
||||
gender,
|
||||
setGender,
|
||||
}: InfoRowEditProps) {
|
||||
const { t } = useTranslation('profileSetting');
|
||||
const { t } = useTranslation(['countries', 'profileSetting']);
|
||||
|
||||
const labels = [
|
||||
{
|
||||
name: 'firstName' as keyof InfoRowData,
|
||||
label: t('settingForm.name'),
|
||||
value: data.firstName,
|
||||
},
|
||||
{
|
||||
name: 'lastName' as keyof InfoRowData,
|
||||
label: t('settingForm.familyName'),
|
||||
value: data.lastName,
|
||||
},
|
||||
{
|
||||
name: 'nationalCode' as keyof InfoRowData,
|
||||
label: t('settingForm.nationalCode'),
|
||||
value: data.nationalCode,
|
||||
},
|
||||
];
|
||||
const countryOptions = countries.map((c) => ({
|
||||
code: c.code,
|
||||
label: t(c.label, { ns: 'countries' }),
|
||||
}));
|
||||
|
||||
const currentCountry =
|
||||
countryOptions.find((c) => c.code === data.country) || null;
|
||||
|
||||
return (
|
||||
<Box sx={{ display: 'flex', gap: 2, flexWrap: 'wrap' }}>
|
||||
{labels.map((field, idx) => (
|
||||
{[
|
||||
{
|
||||
name: 'firstName' as keyof InfoRowData,
|
||||
label: t('settingForm.name', { ns: 'profileSetting' }),
|
||||
value: data.firstName,
|
||||
},
|
||||
{
|
||||
name: 'lastName' as keyof InfoRowData,
|
||||
label: t('settingForm.familyName', { ns: 'profileSetting' }),
|
||||
value: data.lastName,
|
||||
},
|
||||
{
|
||||
name: 'nationalCode' as keyof InfoRowData,
|
||||
label: t('settingForm.nationalCode', { ns: 'profileSetting' }),
|
||||
value: data.nationalCode,
|
||||
},
|
||||
].map(({ name, label, value }) => (
|
||||
<Box
|
||||
key={idx}
|
||||
key={name}
|
||||
sx={{ width: { xs: '100%', sm: '48%', md: 'calc(50% - 8px)' } }}
|
||||
>
|
||||
<TextField
|
||||
fullWidth
|
||||
name={field.name}
|
||||
value={field.value}
|
||||
name={name}
|
||||
value={value}
|
||||
onChange={(e) =>
|
||||
setData((prev) => ({
|
||||
...prev,
|
||||
[field.name]: e.target.value,
|
||||
[name]: e.target.value,
|
||||
}))
|
||||
}
|
||||
label={field.label}
|
||||
label={label}
|
||||
/>
|
||||
</Box>
|
||||
))}
|
||||
@@ -75,36 +82,50 @@ export function InfoRowEdit({
|
||||
renderValue={(selected) =>
|
||||
selected ? (
|
||||
selected === Gender.Male ? (
|
||||
t('settingForm.man')
|
||||
t('settingForm.man', { ns: 'profileSetting' })
|
||||
) : (
|
||||
t('settingForm.woman')
|
||||
t('settingForm.woman', { ns: 'profileSetting' })
|
||||
)
|
||||
) : (
|
||||
<span>{t('settingForm.genderPlaceholder')}</span>
|
||||
<span>
|
||||
{t('settingForm.genderPlaceholder', { ns: 'profileSetting' })}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
>
|
||||
<MenuItem value={Gender.Male}>{t('settingForm.man')}</MenuItem>
|
||||
<MenuItem value={Gender.Female}>{t('settingForm.woman')}</MenuItem>
|
||||
<MenuItem value={Gender.Male}>
|
||||
{t('settingForm.man', { ns: 'profileSetting' })}
|
||||
</MenuItem>
|
||||
<MenuItem value={Gender.Female}>
|
||||
{t('settingForm.woman', { ns: 'profileSetting' })}
|
||||
</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Box>
|
||||
|
||||
<Autocomplete
|
||||
sx={{ width: { xs: '100%', sm: '48%', md: 'calc(50% - 8px)' } }}
|
||||
options={Object.keys(countryCodeMap)}
|
||||
value={data.country}
|
||||
options={countryOptions}
|
||||
getOptionLabel={(option) => option.label}
|
||||
value={currentCountry}
|
||||
onChange={(_, newValue) =>
|
||||
setData((prev) => ({ ...prev, country: newValue || '' }))
|
||||
setData((prev) => ({
|
||||
...prev,
|
||||
country: newValue?.code || '',
|
||||
}))
|
||||
}
|
||||
renderOption={(props, option) => (
|
||||
<Box component="li" {...props}>
|
||||
<CountryFlag country={option} />
|
||||
<Box component="li" {...props} key={option.code}>
|
||||
<CountryFlag code={option.code} />
|
||||
</Box>
|
||||
)}
|
||||
renderInput={(params) => (
|
||||
<TextField {...params} label={t('settingForm.country')} />
|
||||
<TextField
|
||||
{...params}
|
||||
label={t('settingForm.country', { ns: 'profileSetting' })}
|
||||
/>
|
||||
)}
|
||||
clearOnEscape
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user