fix: Add localization route and settings menu entry
- Add LocalizationSettings to RouterContext routes - Add localization card to SettingsOverview menu - Update LocalizationSettings to use SettingsLayout - Add Language icon import Now accessible at /settings/localization
This commit is contained in:
@@ -31,6 +31,7 @@ import PaymentSuccessView from '../views/Payments/PaymentSuccessView'
|
||||
import PrivacyPolicyView from '../views/PrivacyPolicy/PrivacyPolicyView'
|
||||
import ProjectView from '../views/Projects/ProjectView'
|
||||
import APITokenSettings from '../views/Settings/APITokenSettings'
|
||||
import LocalizationSettings from '../views/Settings/LocalizationSettings'
|
||||
import MFASettings from '../views/Settings/MFASettings'
|
||||
import NotificationSetting from '../views/Settings/NotificationSetting'
|
||||
import ProfileSettings from '../views/Settings/ProfileSettings'
|
||||
@@ -115,6 +116,10 @@ const Router = createBrowserRouter([
|
||||
path: 'theme',
|
||||
element: <ThemeSettings />,
|
||||
},
|
||||
{
|
||||
path: 'localization',
|
||||
element: <LocalizationSettings />,
|
||||
},
|
||||
{
|
||||
path: 'advanced',
|
||||
element: <AdvancedSettings />,
|
||||
|
||||
@@ -8,17 +8,16 @@ import {
|
||||
Box,
|
||||
Button,
|
||||
Card,
|
||||
Chip,
|
||||
Divider,
|
||||
FormControl,
|
||||
FormHelperText,
|
||||
FormLabel,
|
||||
Option,
|
||||
Select,
|
||||
Typography,
|
||||
} from '@mui/joy'
|
||||
import moment from 'moment'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import SettingsLayout from './SettingsLayout'
|
||||
|
||||
const LocalizationSettings = () => {
|
||||
const { t } = useTranslation('settings')
|
||||
@@ -46,141 +45,147 @@ const LocalizationSettings = () => {
|
||||
]
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Card variant='outlined' sx={{ p: 3, mb: 2 }}>
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 1 }}>
|
||||
<LanguageOutlined />
|
||||
<Typography level='title-md'>
|
||||
{t('localization.language')}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Typography level='body-sm' sx={{ mb: 2 }}>
|
||||
{t('localization.languageDescription')}
|
||||
</Typography>
|
||||
<FormControl>
|
||||
<Select
|
||||
value={language}
|
||||
onChange={(_, value) => setLanguage(value)}
|
||||
sx={{ minWidth: 250 }}
|
||||
>
|
||||
{availableLanguages.map(lang => (
|
||||
<Option key={lang.code} value={lang.code}>
|
||||
<Box sx={{ display: 'flex', gap: 1, alignItems: 'center' }}>
|
||||
<Typography>{lang.nativeName}</Typography>
|
||||
<Typography level='body-sm' sx={{ color: 'text.secondary' }}>
|
||||
({lang.name})
|
||||
</Typography>
|
||||
</Box>
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
{isRTL && (
|
||||
<FormHelperText>
|
||||
This language uses right-to-left (RTL) text direction
|
||||
</FormHelperText>
|
||||
)}
|
||||
</FormControl>
|
||||
</Box>
|
||||
<SettingsLayout title="Localization">
|
||||
<div className='grid gap-4'>
|
||||
<Typography level='body-md'>
|
||||
{t('localization.description')}
|
||||
</Typography>
|
||||
|
||||
<Divider sx={{ my: 3 }} />
|
||||
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<Typography level='title-md' sx={{ mb: 1 }}>
|
||||
{t('localization.dateFormat')}
|
||||
</Typography>
|
||||
<Typography level='body-sm' sx={{ mb: 2 }}>
|
||||
{t('localization.dateFormatDescription')}
|
||||
</Typography>
|
||||
<FormControl>
|
||||
<Select
|
||||
value={dateFormat}
|
||||
onChange={(_, value) => setDateFormat(value)}
|
||||
sx={{ minWidth: 250 }}
|
||||
>
|
||||
{dateFormatOptions.map(option => (
|
||||
<Option key={option.value} value={option.value}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', width: '100%', gap: 2 }}>
|
||||
<Typography>{option.label}</Typography>
|
||||
<Typography level='body-sm' sx={{ color: 'text.secondary' }}>
|
||||
{sampleDate.format(option.value)}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
<FormHelperText>
|
||||
Preview: {sampleDate.format(dateFormat)}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</Box>
|
||||
|
||||
<Divider sx={{ my: 3 }} />
|
||||
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<Typography level='title-md' sx={{ mb: 1 }}>
|
||||
{t('localization.timeFormat')}
|
||||
</Typography>
|
||||
<Typography level='body-sm' sx={{ mb: 2 }}>
|
||||
{t('localization.timeFormatDescription')}
|
||||
</Typography>
|
||||
<FormControl>
|
||||
<Select
|
||||
value={timeFormat}
|
||||
onChange={(_, value) => setTimeFormat(value)}
|
||||
sx={{ minWidth: 250 }}
|
||||
>
|
||||
<Option value={TIME_FORMATS.HOUR_12}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', width: '100%', gap: 2 }}>
|
||||
<Typography>{t('localization.12hour')}</Typography>
|
||||
<Typography level='body-sm' sx={{ color: 'text.secondary' }}>
|
||||
{sampleDate.format(TIME_FORMATS.HOUR_12)}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Option>
|
||||
<Option value={TIME_FORMATS.HOUR_24}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', width: '100%', gap: 2 }}>
|
||||
<Typography>{t('localization.24hour')}</Typography>
|
||||
<Typography level='body-sm' sx={{ color: 'text.secondary' }}>
|
||||
{sampleDate.format(TIME_FORMATS.HOUR_24)}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Option>
|
||||
</Select>
|
||||
<FormHelperText>
|
||||
Preview: {sampleDate.format(timeFormat)}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</Box>
|
||||
|
||||
<Divider sx={{ my: 3 }} />
|
||||
|
||||
<Box>
|
||||
<Typography level='title-md' sx={{ mb: 1 }}>
|
||||
{t('localization.firstDayOfWeek')}
|
||||
</Typography>
|
||||
<Typography level='body-sm' sx={{ mb: 2 }}>
|
||||
{t('localization.firstDayOfWeekDescription')}
|
||||
</Typography>
|
||||
<FormControl>
|
||||
<Box sx={{ display: 'flex', gap: 2 }}>
|
||||
<Button
|
||||
variant={firstDayOfWeek === 0 ? 'solid' : 'outlined'}
|
||||
onClick={() => setFirstDayOfWeek(0)}
|
||||
>
|
||||
{t('localization.sunday')}
|
||||
</Button>
|
||||
<Button
|
||||
variant={firstDayOfWeek === 1 ? 'solid' : 'outlined'}
|
||||
onClick={() => setFirstDayOfWeek(1)}
|
||||
>
|
||||
{t('localization.monday')}
|
||||
</Button>
|
||||
<Card variant='outlined' sx={{ p: 3, mb: 2 }}>
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 1 }}>
|
||||
<LanguageOutlined />
|
||||
<Typography level='title-md'>
|
||||
{t('localization.language')}
|
||||
</Typography>
|
||||
</Box>
|
||||
</FormControl>
|
||||
</Box>
|
||||
</Card>
|
||||
</Box>
|
||||
<Typography level='body-sm' sx={{ mb: 2 }}>
|
||||
{t('localization.languageDescription')}
|
||||
</Typography>
|
||||
<FormControl>
|
||||
<Select
|
||||
value={language}
|
||||
onChange={(_, value) => setLanguage(value)}
|
||||
sx={{ minWidth: 250 }}
|
||||
>
|
||||
{availableLanguages.map(lang => (
|
||||
<Option key={lang.code} value={lang.code}>
|
||||
<Box sx={{ display: 'flex', gap: 1, alignItems: 'center' }}>
|
||||
<Typography>{lang.nativeName}</Typography>
|
||||
<Typography level='body-sm' sx={{ color: 'text.secondary' }}>
|
||||
({lang.name})
|
||||
</Typography>
|
||||
</Box>
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
{isRTL && (
|
||||
<FormHelperText>
|
||||
This language uses right-to-left (RTL) text direction
|
||||
</FormHelperText>
|
||||
)}
|
||||
</FormControl>
|
||||
</Box>
|
||||
|
||||
<Divider sx={{ my: 3 }} />
|
||||
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<Typography level='title-md' sx={{ mb: 1 }}>
|
||||
{t('localization.dateFormat')}
|
||||
</Typography>
|
||||
<Typography level='body-sm' sx={{ mb: 2 }}>
|
||||
{t('localization.dateFormatDescription')}
|
||||
</Typography>
|
||||
<FormControl>
|
||||
<Select
|
||||
value={dateFormat}
|
||||
onChange={(_, value) => setDateFormat(value)}
|
||||
sx={{ minWidth: 250 }}
|
||||
>
|
||||
{dateFormatOptions.map(option => (
|
||||
<Option key={option.value} value={option.value}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', width: '100%', gap: 2 }}>
|
||||
<Typography>{option.label}</Typography>
|
||||
<Typography level='body-sm' sx={{ color: 'text.secondary' }}>
|
||||
{sampleDate.format(option.value)}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
<FormHelperText>
|
||||
Preview: {sampleDate.format(dateFormat)}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</Box>
|
||||
|
||||
<Divider sx={{ my: 3 }} />
|
||||
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<Typography level='title-md' sx={{ mb: 1 }}>
|
||||
{t('localization.timeFormat')}
|
||||
</Typography>
|
||||
<Typography level='body-sm' sx={{ mb: 2 }}>
|
||||
{t('localization.timeFormatDescription')}
|
||||
</Typography>
|
||||
<FormControl>
|
||||
<Select
|
||||
value={timeFormat}
|
||||
onChange={(_, value) => setTimeFormat(value)}
|
||||
sx={{ minWidth: 250 }}
|
||||
>
|
||||
<Option value={TIME_FORMATS.HOUR_12}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', width: '100%', gap: 2 }}>
|
||||
<Typography>{t('localization.12hour')}</Typography>
|
||||
<Typography level='body-sm' sx={{ color: 'text.secondary' }}>
|
||||
{sampleDate.format(TIME_FORMATS.HOUR_12)}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Option>
|
||||
<Option value={TIME_FORMATS.HOUR_24}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', width: '100%', gap: 2 }}>
|
||||
<Typography>{t('localization.24hour')}</Typography>
|
||||
<Typography level='body-sm' sx={{ color: 'text.secondary' }}>
|
||||
{sampleDate.format(TIME_FORMATS.HOUR_24)}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Option>
|
||||
</Select>
|
||||
<FormHelperText>
|
||||
Preview: {sampleDate.format(timeFormat)}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</Box>
|
||||
|
||||
<Divider sx={{ my: 3 }} />
|
||||
|
||||
<Box>
|
||||
<Typography level='title-md' sx={{ mb: 1 }}>
|
||||
{t('localization.firstDayOfWeek')}
|
||||
</Typography>
|
||||
<Typography level='body-sm' sx={{ mb: 2 }}>
|
||||
{t('localization.firstDayOfWeekDescription')}
|
||||
</Typography>
|
||||
<FormControl>
|
||||
<Box sx={{ display: 'flex', gap: 2 }}>
|
||||
<Button
|
||||
variant={firstDayOfWeek === 0 ? 'solid' : 'outlined'}
|
||||
onClick={() => setFirstDayOfWeek(0)}
|
||||
>
|
||||
{t('localization.sunday')}
|
||||
</Button>
|
||||
<Button
|
||||
variant={firstDayOfWeek === 1 ? 'solid' : 'outlined'}
|
||||
onClick={() => setFirstDayOfWeek(1)}
|
||||
>
|
||||
{t('localization.monday')}
|
||||
</Button>
|
||||
</Box>
|
||||
</FormControl>
|
||||
</Box>
|
||||
</Card>
|
||||
</div>
|
||||
</SettingsLayout>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
Circle,
|
||||
Code,
|
||||
FamilyRestroom,
|
||||
Language,
|
||||
Notifications,
|
||||
Palette,
|
||||
Person,
|
||||
@@ -109,6 +110,13 @@ const SettingsOverview = () => {
|
||||
'Choose your preferred theme and configure dark/light mode settings.',
|
||||
icon: <Palette />,
|
||||
},
|
||||
{
|
||||
id: 'localization',
|
||||
title: 'Localization',
|
||||
description:
|
||||
'Customize language, date format, time format, and regional preferences.',
|
||||
icon: <Language />,
|
||||
},
|
||||
{
|
||||
id: 'advanced',
|
||||
title: 'Advanced Settings',
|
||||
|
||||
Reference in New Issue
Block a user