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:
Mo Tarbin
2026-03-08 16:22:13 +00:00
parent 6d6185dc5e
commit 7f74574a7f
3 changed files with 153 additions and 135 deletions

View File

@@ -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 />,

View File

@@ -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>
)
}

View File

@@ -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',