feat(settings): appearance settings tab

New Appearance tab with color mode (moved out of Display), color-scheme swatches, a custom accent picker with a live WCAG contrast hint, transparency and reduce-motion toggles, density, a global text-size slider with advanced per-tier controls, and per-device dashboard widget toggles. Edits preview live and commit on a short debounce. i18n keys added across all locales, translated for German.
This commit is contained in:
Maurice
2026-06-29 10:59:52 +02:00
committed by Maurice
parent 2cda779bc5
commit a7334a9060
26 changed files with 1377 additions and 105 deletions
@@ -0,0 +1,97 @@
// FE-COMP-APPEARANCE-001+ — color mode moved here from DisplaySettingsTab,
// plus the new scheme / readability / dashboard-widget controls.
import { render, screen, waitFor } from '../../../tests/helpers/render';
import userEvent from '@testing-library/user-event';
import { http, HttpResponse } from 'msw';
import { server } from '../../../tests/helpers/msw/server';
import { useAuthStore } from '../../store/authStore';
import { useSettingsStore } from '../../store/settingsStore';
import { resetAllStores, seedStore } from '../../../tests/helpers/store';
import { buildUser, buildSettings } from '../../../tests/helpers/factories';
import AppearanceSettingsTab from './AppearanceSettingsTab';
beforeEach(() => {
resetAllStores();
server.use(http.put('/api/settings', async () => HttpResponse.json({ success: true })));
seedStore(useAuthStore, { user: buildUser(), isAuthenticated: true });
seedStore(useSettingsStore, { settings: buildSettings({ dark_mode: 'light', language: 'en' }) });
});
describe('AppearanceSettingsTab', () => {
it('FE-COMP-APPEARANCE-001: renders without crashing', () => {
render(<AppearanceSettingsTab />);
expect(document.body).toBeInTheDocument();
});
it('FE-COMP-APPEARANCE-002: shows the color-mode buttons', () => {
render(<AppearanceSettingsTab />);
expect(screen.getByText('Light')).toBeInTheDocument();
expect(screen.getByText('Dark')).toBeInTheDocument();
expect(screen.getByRole('button', { name: /Auto/i })).toBeInTheDocument();
});
it('FE-COMP-APPEARANCE-003: clicking Dark calls updateSetting with dark', async () => {
const user = userEvent.setup();
const updateSetting = vi.fn().mockResolvedValue(undefined);
seedStore(useSettingsStore, { settings: buildSettings({ dark_mode: 'light' }), updateSetting });
render(<AppearanceSettingsTab />);
await user.click(screen.getByText('Dark'));
expect(updateSetting).toHaveBeenCalledWith('dark_mode', 'dark');
});
it('FE-COMP-APPEARANCE-004: clicking Light calls updateSetting with light', async () => {
const user = userEvent.setup();
const updateSetting = vi.fn().mockResolvedValue(undefined);
seedStore(useSettingsStore, { settings: buildSettings({ dark_mode: 'dark' }), updateSetting });
render(<AppearanceSettingsTab />);
await user.click(screen.getByText('Light'));
expect(updateSetting).toHaveBeenCalledWith('dark_mode', 'light');
});
it('FE-COMP-APPEARANCE-005: clicking Auto calls updateSetting with auto', async () => {
const user = userEvent.setup();
const updateSetting = vi.fn().mockResolvedValue(undefined);
seedStore(useSettingsStore, { settings: buildSettings({ dark_mode: 'light' }), updateSetting });
render(<AppearanceSettingsTab />);
await user.click(screen.getByRole('button', { name: /Auto/i }));
expect(updateSetting).toHaveBeenCalledWith('dark_mode', 'auto');
});
it('FE-COMP-APPEARANCE-006: shows readability + dashboard widget sections', () => {
render(<AppearanceSettingsTab />);
expect(screen.getByText('Readability')).toBeInTheDocument();
expect(screen.getByText('Transparency')).toBeInTheDocument();
expect(screen.getByText('Dashboard widgets')).toBeInTheDocument();
});
it('FE-COMP-APPEARANCE-007: shows the preset color schemes', () => {
render(<AppearanceSettingsTab />);
expect(screen.getByText('Indigo')).toBeInTheDocument();
expect(screen.getByText('Teal')).toBeInTheDocument();
expect(screen.getByText('High contrast')).toBeInTheDocument();
});
it('FE-COMP-APPEARANCE-008: choosing a scheme persists the appearance config', async () => {
const user = userEvent.setup();
const updateSetting = vi.fn().mockResolvedValue(undefined);
seedStore(useSettingsStore, { settings: buildSettings({ dark_mode: 'light' }), updateSetting });
render(<AppearanceSettingsTab />);
await user.click(screen.getByText('Indigo'));
await waitFor(
() => expect(updateSetting).toHaveBeenCalledWith('appearance', expect.objectContaining({ schemeId: 'indigo' })),
{ timeout: 1500 },
);
});
it('FE-COMP-APPEARANCE-009: toggling transparency persists transparency:false', async () => {
const user = userEvent.setup();
const updateSetting = vi.fn().mockResolvedValue(undefined);
seedStore(useSettingsStore, { settings: buildSettings({ dark_mode: 'light' }), updateSetting });
render(<AppearanceSettingsTab />);
await user.click(screen.getByRole('button', { name: 'Transparency' }));
await waitFor(
() => expect(updateSetting).toHaveBeenCalledWith('appearance', expect.objectContaining({ transparency: false })),
{ timeout: 1500 },
);
});
});
@@ -0,0 +1,390 @@
import React, { useEffect, useRef, useState } from 'react'
import { Paintbrush, Eye, LayoutDashboard, Sun, Moon, Monitor, RotateCcw } from 'lucide-react'
import { useTranslation } from '../../i18n'
import { useSettingsStore } from '../../store/settingsStore'
import { useToast } from '../shared/Toast'
import Section from './Section'
import ToggleSwitch from './ToggleSwitch'
import { applyAppearance } from '../../theme/applyAppearance'
import { APPEARANCE_SCHEMES, CUSTOM_ACCENT_PRESETS } from '../../theme/schemes'
import {
DEFAULT_APPEARANCE,
normalizeAppearance,
APPEARANCE_SCALE_MIN,
APPEARANCE_SCALE_MAX,
type AppearanceConfig,
} from '@trek/shared'
// ── WCAG contrast helpers (for the custom-accent legibility hint) ────────────
function channelLum(v: number): number {
return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4)
}
function relLuminance(hex: string): number {
const c = hex.replace('#', '')
const full = c.length === 3 ? c.split('').map((x) => x + x).join('') : c
const r = channelLum(parseInt(full.slice(0, 2), 16) / 255)
const g = channelLum(parseInt(full.slice(2, 4), 16) / 255)
const b = channelLum(parseInt(full.slice(4, 6), 16) / 255)
return 0.2126 * r + 0.7152 * g + 0.0722 * b
}
function contrastRatio(a: string, b: string): number {
const la = relLuminance(a)
const lb = relLuminance(b)
const [hi, lo] = la > lb ? [la, lb] : [lb, la]
return (hi + 0.05) / (lo + 0.05)
}
const isHex = (v: string) => /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(v)
type DesktopWidgetKey = keyof AppearanceConfig['dashboard']['desktop']
type MobileWidgetKey = keyof AppearanceConfig['dashboard']['mobile']
const DESKTOP_WIDGETS: { key: DesktopWidgetKey; fallback: string }[] = [
{ key: 'sidebar', fallback: 'Right sidebar' },
{ key: 'currency', fallback: 'Currency' },
{ key: 'timezones', fallback: 'Timezones' },
{ key: 'upcomingReservations', fallback: 'Upcoming reservations' },
{ key: 'atlas', fallback: 'Atlas / countries' },
{ key: 'tripsTotal', fallback: 'Trips total' },
{ key: 'daysTraveled', fallback: 'Days traveled' },
{ key: 'distanceFlown', fallback: 'Distance flown' },
]
const MOBILE_WIDGETS: { key: MobileWidgetKey; fallback: string }[] = [
{ key: 'tripsTotal', fallback: 'Trips total' },
{ key: 'daysTraveled', fallback: 'Days traveled' },
{ key: 'currency', fallback: 'Currency' },
{ key: 'timezones', fallback: 'Timezones' },
{ key: 'upcomingReservations', fallback: 'Upcoming reservations' },
]
// shared segmented-button style (matches DisplaySettingsTab)
function segStyle(active: boolean): React.CSSProperties {
return {
display: 'flex', alignItems: 'center', gap: 6, justifyContent: 'center',
padding: '10px 14px', borderRadius: 10, cursor: 'pointer', flex: '1 1 0', minWidth: 0,
fontFamily: 'inherit', fontSize: 14, fontWeight: 500,
border: active ? '2px solid var(--text-primary)' : '2px solid var(--border-primary)',
background: active ? 'var(--bg-hover)' : 'var(--bg-card)',
color: 'var(--text-primary)', transition: 'all 0.15s',
}
}
export default function AppearanceSettingsTab(): React.ReactElement {
const { settings, updateSetting } = useSettingsStore()
const { t } = useTranslation()
const toast = useToast()
const tr = (key: string, fallback: string) => t(key) || fallback
const [cfg, setCfg] = useState<AppearanceConfig>(() => normalizeAppearance(settings.appearance))
const [advancedType, setAdvancedType] = useState(false)
const persistTimer = useRef<number | undefined>(undefined)
// Re-sync when settings change elsewhere (e.g. server reconcile / another tab).
useEffect(() => {
setCfg(normalizeAppearance(settings.appearance))
}, [settings.appearance])
// Flush any pending persist on unmount.
useEffect(() => () => {
if (persistTimer.current) window.clearTimeout(persistTimer.current)
}, [])
const isDark =
settings.dark_mode === true ||
settings.dark_mode === 'dark' ||
(settings.dark_mode === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches)
// Live preview now (DOM), persist after a short debounce (API).
const update = (patch: Partial<AppearanceConfig>) => {
const next = { ...cfg, ...patch }
setCfg(next)
applyAppearance({ darkMode: settings.dark_mode, appearance: next, isSharedPage: false })
if (persistTimer.current) window.clearTimeout(persistTimer.current)
persistTimer.current = window.setTimeout(() => {
updateSetting('appearance', next).catch((e: unknown) =>
toast.error(e instanceof Error ? e.message : t('common.error'))
)
}, 350)
}
const setMode = async (mode: string) => {
try {
await updateSetting('dark_mode', mode)
} catch (e: unknown) {
toast.error(e instanceof Error ? e.message : t('common.error'))
}
}
const setWidget = (device: 'desktop' | 'mobile', key: string, on: boolean) => {
update({
dashboard: {
...cfg.dashboard,
[device]: { ...cfg.dashboard[device], [key]: on },
},
})
}
const resetAll = () => update({ ...DEFAULT_APPEARANCE })
const accentLight = cfg.accent?.light ?? '#4f46e5'
const accentDark = cfg.accent?.dark ?? '#6366f1'
const customRatio = contrastRatio(isDark ? accentDark : accentLight, isDark ? '#ffffff' : '#ffffff')
return (
<>
{/* ── Theme ───────────────────────────────────────────────── */}
<Section title={tr('settings.appearance.theme', 'Theme')} icon={Paintbrush}>
{/* Color mode */}
<div>
<label className="block text-sm font-medium mb-2 text-content-secondary">
{tr('settings.colorMode', 'Color mode')}
</label>
<div className="flex gap-3" style={{ flexWrap: 'wrap' }}>
{[
{ value: 'light', label: tr('settings.light', 'Light'), icon: Sun },
{ value: 'dark', label: tr('settings.dark', 'Dark'), icon: Moon },
{ value: 'auto', label: tr('settings.auto', 'Auto'), icon: Monitor },
].map((opt) => {
const cur = settings.dark_mode
const active =
cur === opt.value ||
(opt.value === 'light' && cur === false) ||
(opt.value === 'dark' && cur === true)
return (
<button key={opt.value} onClick={() => setMode(opt.value)} style={segStyle(active)}>
<span className="hidden sm:inline-flex"><opt.icon size={16} /></span>
{opt.label}
</button>
)
})}
</div>
</div>
{/* Color scheme swatches */}
<div>
<label className="block text-sm font-medium mb-2 text-content-secondary">
{tr('settings.appearance.scheme', 'Color scheme')}
</label>
<div className="grid grid-cols-2 sm:grid-cols-4 gap-2">
{APPEARANCE_SCHEMES.map((s) => {
const active = cfg.schemeId === s.id
const dot = isDark ? s.swatch.dark : s.swatch.light
return (
<button
key={s.id}
onClick={() => update({ schemeId: s.id })}
style={{
display: 'flex', alignItems: 'center', gap: 8, padding: '10px 12px',
borderRadius: 10, cursor: 'pointer', fontFamily: 'inherit', fontSize: 13, fontWeight: 500,
border: active ? '2px solid var(--text-primary)' : '2px solid var(--border-primary)',
background: active ? 'var(--bg-hover)' : 'var(--bg-card)', color: 'var(--text-primary)',
transition: 'all 0.15s',
}}
>
<span style={{ width: 16, height: 16, borderRadius: '50%', background: dot, flexShrink: 0, boxShadow: 'inset 0 0 0 1px var(--border-faint)' }} />
<span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
{tr(`settings.appearance.scheme.${s.id}`, schemeFallback(s.id))}
</span>
</button>
)
})}
{/* Custom */}
<button
onClick={() => update({ schemeId: 'custom', accent: cfg.accent ?? { light: accentLight, dark: accentDark } })}
style={{
display: 'flex', alignItems: 'center', gap: 8, padding: '10px 12px', borderRadius: 10,
cursor: 'pointer', fontFamily: 'inherit', fontSize: 13, fontWeight: 500,
border: cfg.schemeId === 'custom' ? '2px solid var(--text-primary)' : '2px solid var(--border-primary)',
background: cfg.schemeId === 'custom' ? 'var(--bg-hover)' : 'var(--bg-card)', color: 'var(--text-primary)',
transition: 'all 0.15s',
}}
>
<span style={{ width: 16, height: 16, borderRadius: '50%', flexShrink: 0, background: 'conic-gradient(#ef4444,#f59e0b,#22c55e,#3b82f6,#8b5cf6,#ef4444)' }} />
{tr('settings.appearance.scheme.custom', 'Custom')}
</button>
</div>
</div>
{/* Custom accent picker */}
{cfg.schemeId === 'custom' && (
<div>
<label className="block text-sm font-medium mb-2 text-content-secondary">
{tr('settings.appearance.customAccent', 'Custom accent')}
</label>
<div className="flex flex-wrap gap-2 mb-3">
{CUSTOM_ACCENT_PRESETS.map((c) => (
<button
key={c}
aria-label={c}
onClick={() => update({ accent: { light: c, dark: c } })}
style={{ width: 28, height: 28, borderRadius: '50%', background: c, cursor: 'pointer', border: '2px solid var(--border-primary)' }}
/>
))}
</div>
<div className="flex flex-wrap gap-4 items-center">
<label className="flex items-center gap-2 text-sm text-content-secondary">
{tr('settings.light', 'Light')}
<input type="color" value={isHex(accentLight) ? accentLight : '#4f46e5'}
onChange={(e) => update({ accent: { light: e.target.value, dark: accentDark } })}
style={{ width: 36, height: 28, border: 'none', background: 'none', cursor: 'pointer' }} />
</label>
<label className="flex items-center gap-2 text-sm text-content-secondary">
{tr('settings.dark', 'Dark')}
<input type="color" value={isHex(accentDark) ? accentDark : '#6366f1'}
onChange={(e) => update({ accent: { light: accentLight, dark: e.target.value } })}
style={{ width: 36, height: 28, border: 'none', background: 'none', cursor: 'pointer' }} />
</label>
<span
className="text-xs font-medium px-2 py-1 rounded-md"
style={{ background: customRatio >= 4.5 ? 'var(--success-soft)' : 'var(--warning-soft)', color: customRatio >= 4.5 ? 'var(--success)' : 'var(--warning)' }}
>
{customRatio >= 4.5
? `${tr('settings.appearance.contrastOk', 'Good contrast')} (${customRatio.toFixed(1)}:1)`
: `${tr('settings.appearance.contrastLow', 'Low contrast')} (${customRatio.toFixed(1)}:1)`}
</span>
</div>
</div>
)}
</Section>
{/* ── Readability ─────────────────────────────────────────── */}
<Section title={tr('settings.appearance.readability', 'Readability')} icon={Eye}>
<ToggleRow
label={tr('settings.appearance.transparency', 'Transparency')}
hint={tr('settings.appearance.transparencyHint', 'Glassy translucent surfaces. Turn off for solid, higher-contrast backgrounds.')}
on={cfg.transparency}
onToggle={() => update({ transparency: !cfg.transparency })}
/>
<ToggleRow
label={tr('settings.appearance.reduceMotion', 'Reduce motion')}
hint={tr('settings.appearance.reduceMotionHint', 'Minimize animations and transitions.')}
on={cfg.reduceMotion}
onToggle={() => update({ reduceMotion: !cfg.reduceMotion })}
/>
{/* Density */}
<div>
<label className="block text-sm font-medium mb-2 text-content-secondary">
{tr('settings.appearance.density', 'Density')}
</label>
<div className="flex gap-3">
{[
{ value: 'comfortable', label: tr('settings.appearance.comfortable', 'Comfortable') },
{ value: 'compact', label: tr('settings.appearance.compact', 'Compact') },
].map((opt) => (
<button key={opt.value} onClick={() => update({ density: opt.value as AppearanceConfig['density'] })} style={segStyle(cfg.density === opt.value)}>
{opt.label}
</button>
))}
</div>
</div>
{/* Text size (global) */}
<SliderRow
label={tr('settings.appearance.textSize', 'Text size')}
value={cfg.fontScale}
onChange={(v) => update({ fontScale: v })}
/>
<button
onClick={() => setAdvancedType((v) => !v)}
className="text-xs font-medium text-content-muted hover:text-content"
style={{ background: 'none', border: 'none', cursor: 'pointer', padding: 0 }}
>
{advancedType ? tr('settings.appearance.hideAdvanced', 'Hide advanced') : tr('settings.appearance.advancedTextSizes', 'Advanced text sizes')}
</button>
{advancedType && (
<div className="space-y-3 pl-1">
<SliderRow label={tr('settings.appearance.tier.title', 'Titles')} value={cfg.typeScale.title} onChange={(v) => update({ typeScale: { ...cfg.typeScale, title: v } })} />
<SliderRow label={tr('settings.appearance.tier.subtitle', 'Subtitles')} value={cfg.typeScale.subtitle} onChange={(v) => update({ typeScale: { ...cfg.typeScale, subtitle: v } })} />
<SliderRow label={tr('settings.appearance.tier.body', 'Body')} value={cfg.typeScale.body} onChange={(v) => update({ typeScale: { ...cfg.typeScale, body: v } })} />
<SliderRow label={tr('settings.appearance.tier.caption', 'Captions')} value={cfg.typeScale.caption} onChange={(v) => update({ typeScale: { ...cfg.typeScale, caption: v } })} />
</div>
)}
</Section>
{/* ── Dashboard widgets ───────────────────────────────────── */}
<Section title={tr('settings.appearance.dashboardWidgets', 'Dashboard widgets')} icon={LayoutDashboard}>
<p className="text-xs text-content-faint -mt-1">
{tr('settings.appearance.dashboardWidgetsHint', 'Show or hide dashboard widgets independently on desktop and mobile.')}
</p>
<div>
<div className="text-sm font-medium mb-2 text-content-secondary">{tr('settings.appearance.desktop', 'Desktop')}</div>
{DESKTOP_WIDGETS.map((w) => (
<ToggleRow
key={w.key}
label={tr(`settings.appearance.widget.${w.key}`, w.fallback)}
on={cfg.dashboard.desktop[w.key]}
onToggle={() => setWidget('desktop', w.key, !cfg.dashboard.desktop[w.key])}
/>
))}
</div>
<div>
<div className="text-sm font-medium mb-2 text-content-secondary">{tr('settings.appearance.mobile', 'Mobile')}</div>
{MOBILE_WIDGETS.map((w) => (
<ToggleRow
key={w.key}
label={tr(`settings.appearance.widget.${w.key}`, w.fallback)}
on={cfg.dashboard.mobile[w.key]}
onToggle={() => setWidget('mobile', w.key, !cfg.dashboard.mobile[w.key])}
/>
))}
</div>
</Section>
<div className="flex justify-end mb-6">
<button
onClick={resetAll}
className="flex items-center gap-2 text-sm font-medium text-content-muted hover:text-content"
style={{ background: 'none', border: 'none', cursor: 'pointer', padding: '6px 4px' }}
>
<RotateCcw size={15} />
{tr('settings.appearance.reset', 'Reset to defaults')}
</button>
</div>
</>
)
}
function schemeFallback(id: string): string {
const map: Record<string, string> = {
default: 'Default',
highContrast: 'High contrast',
indigo: 'Indigo',
teal: 'Teal',
rose: 'Rose',
amber: 'Amber',
violet: 'Violet',
}
return map[id] || id
}
function ToggleRow({ label, hint, on, onToggle }: { label: string; hint?: string; on: boolean; onToggle: () => void }) {
return (
<div className="flex items-start justify-between gap-4 py-1.5">
<div>
<div className="text-sm font-medium text-content-secondary">{label}</div>
{hint && <div className="text-xs text-content-faint mt-0.5">{hint}</div>}
</div>
<ToggleSwitch on={on} onToggle={onToggle} label={label} />
</div>
)
}
function SliderRow({ label, value, onChange }: { label: string; value: number; onChange: (v: number) => void }) {
return (
<div>
<div className="flex items-center justify-between mb-1">
<span className="text-sm font-medium text-content-secondary">{label}</span>
<span className="text-xs text-content-muted tabular-nums">{Math.round(value * 100)}%</span>
</div>
<input
type="range"
min={APPEARANCE_SCALE_MIN}
max={APPEARANCE_SCALE_MAX}
step={0.05}
value={value}
onChange={(e) => onChange(Number(e.target.value))}
style={{ width: '100%', accentColor: 'var(--accent)', cursor: 'pointer' }}
/>
</div>
)
}
@@ -1,5 +1,5 @@
// FE-COMP-DISPLAY-001 to FE-COMP-DISPLAY-027
import { render, screen, waitFor } from '../../../tests/helpers/render';
import { render, screen } from '../../../tests/helpers/render';
import userEvent from '@testing-library/user-event';
import { http, HttpResponse } from 'msw';
import { server } from '../../../tests/helpers/msw/server';
@@ -30,21 +30,6 @@ describe('DisplaySettingsTab', () => {
expect(screen.getByText('Display')).toBeInTheDocument();
});
it('FE-COMP-DISPLAY-003: shows Light mode button', () => {
render(<DisplaySettingsTab />);
expect(screen.getByText('Light')).toBeInTheDocument();
});
it('FE-COMP-DISPLAY-004: shows Dark mode button', () => {
render(<DisplaySettingsTab />);
expect(screen.getByText('Dark')).toBeInTheDocument();
});
it('FE-COMP-DISPLAY-005: shows Auto mode button', () => {
render(<DisplaySettingsTab />);
expect(screen.getByRole('button', { name: /Auto/i })).toBeInTheDocument();
});
it('FE-COMP-DISPLAY-006: shows Language section', () => {
render(<DisplaySettingsTab />);
expect(screen.getByText('Language')).toBeInTheDocument();
@@ -55,20 +40,6 @@ describe('DisplaySettingsTab', () => {
expect(screen.getByText('Time Format')).toBeInTheDocument();
});
it('FE-COMP-DISPLAY-008: clicking Dark mode button calls updateSetting', async () => {
const user = userEvent.setup();
const updateSetting = vi.fn().mockResolvedValue(undefined);
seedStore(useSettingsStore, { settings: buildSettings({ dark_mode: 'light' }), updateSetting });
render(<DisplaySettingsTab />);
await user.click(screen.getByText('Dark'));
expect(updateSetting).toHaveBeenCalledWith('dark_mode', 'dark');
});
it('FE-COMP-DISPLAY-009: shows Color Mode label', () => {
render(<DisplaySettingsTab />);
expect(screen.getByText('Color Mode')).toBeInTheDocument();
});
it('FE-COMP-DISPLAY-010: shows 24h time format option', () => {
render(<DisplaySettingsTab />);
// Label is "24h (14:30)"
@@ -81,35 +52,6 @@ describe('DisplaySettingsTab', () => {
expect(screen.getByText(/12h/i)).toBeInTheDocument();
});
it('FE-COMP-DISPLAY-012: clicking Light mode calls updateSetting with light', async () => {
const user = userEvent.setup();
const updateSetting = vi.fn().mockResolvedValue(undefined);
seedStore(useSettingsStore, { settings: buildSettings({ dark_mode: 'dark' }), updateSetting });
render(<DisplaySettingsTab />);
await user.click(screen.getByText('Light'));
expect(updateSetting).toHaveBeenCalledWith('dark_mode', 'light');
});
it('FE-COMP-DISPLAY-013: clicking Auto mode button calls updateSetting with auto', async () => {
const user = userEvent.setup();
const updateSetting = vi.fn().mockResolvedValue(undefined);
seedStore(useSettingsStore, { settings: buildSettings({ dark_mode: 'light' }), updateSetting });
render(<DisplaySettingsTab />);
await user.click(screen.getByRole('button', { name: /Auto/i }));
expect(updateSetting).toHaveBeenCalledWith('dark_mode', 'auto');
});
it('FE-COMP-DISPLAY-014: active color mode button has border with var(--text-primary)', () => {
seedStore(useSettingsStore, { settings: buildSettings({ dark_mode: 'dark' }) });
render(<DisplaySettingsTab />);
const darkBtn = screen.getByRole('button', { name: /^Dark$/i });
const lightBtn = screen.getByRole('button', { name: /^Light$/i });
const autoBtn = screen.getByRole('button', { name: /Auto/i });
expect(darkBtn.style.border).toContain('var(--text-primary)');
expect(lightBtn.style.border).toContain('var(--border-primary)');
expect(autoBtn.style.border).toContain('var(--border-primary)');
});
it('FE-COMP-DISPLAY-015: clicking a language button calls updateSetting with that language code', async () => {
const user = userEvent.setup();
const updateSetting = vi.fn().mockResolvedValue(undefined);
@@ -193,9 +135,9 @@ describe('DisplaySettingsTab', () => {
it('FE-COMP-DISPLAY-026: updateSetting failure shows toast error', async () => {
const user = userEvent.setup();
const updateSetting = vi.fn().mockRejectedValue(new Error('Server error'));
seedStore(useSettingsStore, { settings: buildSettings({ dark_mode: 'light' }), updateSetting });
seedStore(useSettingsStore, { settings: buildSettings({ temperature_unit: 'celsius' }), updateSetting });
render(<><ToastContainer /><DisplaySettingsTab /></>);
await user.click(screen.getByText('Dark'));
await user.click(screen.getByText('°F Fahrenheit'));
await screen.findByText('Server error');
});
@@ -1,5 +1,5 @@
import React, { useState, useEffect, useRef } from 'react'
import { Palette, Sun, Moon, Monitor, ChevronDown, Check } from 'lucide-react'
import { Palette, ChevronDown, Check } from 'lucide-react'
import { SUPPORTED_LANGUAGES, useTranslation } from '../../i18n'
import { useSettingsStore } from '../../store/settingsStore'
import { useToast } from '../shared/Toast'
@@ -51,48 +51,6 @@ export default function DisplaySettingsTab(): React.ReactElement {
<p className="text-xs text-content-faint mt-2">{t('settings.currencyHint')}</p>
</div>
{/* Color Mode */}
<div>
<label className="block text-sm font-medium mb-2 text-content-secondary">{t('settings.colorMode')}</label>
<div className="flex gap-3" style={{ flexWrap: 'wrap' }}>
{[
{ value: 'light', label: t('settings.light'), icon: Sun },
{ value: 'dark', label: t('settings.dark'), icon: Moon },
{ value: 'auto', label: t('settings.auto'), icon: Monitor },
].map(opt => {
const current = settings.dark_mode
const isActive = current === opt.value || (opt.value === 'light' && current === false) || (opt.value === 'dark' && current === true)
return (
<button
key={opt.value}
onClick={async () => {
try {
await updateSetting('dark_mode', opt.value)
} catch (e: unknown) { toast.error(e instanceof Error ? e.message : t('common.error')) }
}}
style={{
display: 'flex', alignItems: 'center', gap: 6,
padding: '10px 14px', borderRadius: 10, cursor: 'pointer', flex: '1 1 0', justifyContent: 'center', minWidth: 0,
fontFamily: 'inherit', fontSize: 14, fontWeight: 500,
border: isActive ? '2px solid var(--text-primary)' : '2px solid var(--border-primary)',
background: isActive ? 'var(--bg-hover)' : 'var(--bg-card)',
color: 'var(--text-primary)',
transition: 'all 0.15s',
}}
>
<span className="hidden sm:inline-flex"><opt.icon size={16} /></span>
{opt.value === 'auto' ? (
<>
<span className="hidden sm:inline">{opt.label}</span>
<span className="sm:hidden">Auto</span>
</>
) : opt.label}
</button>
)
})}
</div>
</div>
{/* Language */}
<div>
<label className="block text-sm font-medium mb-2 text-content-secondary">{t('settings.language')}</label>
+4 -1
View File
@@ -1,9 +1,10 @@
import React from 'react'
import { Settings, Palette, Map, Bell, Plug, CloudOff, User, Info } from 'lucide-react'
import { Settings, Palette, Paintbrush, Map, Bell, Plug, CloudOff, User, Info } from 'lucide-react'
import { useTranslation } from '../i18n'
import PageShell from '../components/Layout/PageShell'
import PageSidebar, { type PageSidebarTab } from '../components/Layout/PageSidebar'
import DisplaySettingsTab from '../components/Settings/DisplaySettingsTab'
import AppearanceSettingsTab from '../components/Settings/AppearanceSettingsTab'
import MapSettingsTab from '../components/Settings/MapSettingsTab'
import NotificationsTab from '../components/Settings/NotificationsTab'
import IntegrationsTab from '../components/Settings/IntegrationsTab'
@@ -19,6 +20,7 @@ export default function SettingsPage(): React.ReactElement {
const tabs: PageSidebarTab[] = [
{ id: 'display', label: t('settings.tabs.display'), icon: Palette },
{ id: 'appearance', label: t('settings.tabs.appearance'), icon: Paintbrush },
{ id: 'map', label: t('settings.tabs.map'), icon: Map },
{ id: 'notifications', label: t('settings.tabs.notifications'), icon: Bell },
...(hasIntegrations
@@ -54,6 +56,7 @@ export default function SettingsPage(): React.ReactElement {
footer={appVersion ? `v${appVersion} · self-hosted` : 'self-hosted'}
>
{activeTab === 'display' && <DisplaySettingsTab />}
{activeTab === 'appearance' && <AppearanceSettingsTab />}
{activeTab === 'map' && <MapSettingsTab />}
{activeTab === 'notifications' && <NotificationsTab />}
{activeTab === 'integrations' && hasIntegrations && <IntegrationsTab />}
+42
View File
@@ -332,6 +332,48 @@ const settings: TranslationStrings = {
'settings.aiParsing.multimodalHint': 'للنماذج القادرة على الرؤية — يرسل ملف PDF الأصلي بدلًا من النص المستخرج.',
'settings.aiParsing.toast.saved': 'تم حفظ إعدادات الذكاء الاصطناعي',
'settings.aiParsing.toast.saveError': 'تعذّر حفظ إعدادات الذكاء الاصطناعي',
'settings.tabs.appearance': 'Appearance',
'settings.appearance.theme': 'Theme',
'settings.appearance.scheme': 'Color scheme',
'settings.appearance.scheme.default': 'Default',
'settings.appearance.scheme.highContrast': 'High contrast',
'settings.appearance.scheme.indigo': 'Indigo',
'settings.appearance.scheme.teal': 'Teal',
'settings.appearance.scheme.rose': 'Rose',
'settings.appearance.scheme.amber': 'Amber',
'settings.appearance.scheme.violet': 'Violet',
'settings.appearance.scheme.custom': 'Custom',
'settings.appearance.customAccent': 'Custom accent',
'settings.appearance.contrastOk': 'Good contrast',
'settings.appearance.contrastLow': 'Low contrast',
'settings.appearance.readability': 'Readability',
'settings.appearance.transparency': 'Transparency',
'settings.appearance.transparencyHint': 'Glassy translucent surfaces. Turn off for solid, higher-contrast backgrounds.',
'settings.appearance.reduceMotion': 'Reduce motion',
'settings.appearance.reduceMotionHint': 'Minimize animations and transitions.',
'settings.appearance.density': 'Density',
'settings.appearance.comfortable': 'Comfortable',
'settings.appearance.compact': 'Compact',
'settings.appearance.textSize': 'Text size',
'settings.appearance.advancedTextSizes': 'Advanced text sizes',
'settings.appearance.hideAdvanced': 'Hide advanced',
'settings.appearance.tier.title': 'Titles',
'settings.appearance.tier.subtitle': 'Subtitles',
'settings.appearance.tier.body': 'Body',
'settings.appearance.tier.caption': 'Captions',
'settings.appearance.dashboardWidgets': 'Dashboard widgets',
'settings.appearance.dashboardWidgetsHint': 'Show or hide dashboard widgets independently on desktop and mobile.',
'settings.appearance.desktop': 'Desktop',
'settings.appearance.mobile': 'Mobile',
'settings.appearance.widget.sidebar': 'Right sidebar',
'settings.appearance.widget.currency': 'Currency',
'settings.appearance.widget.timezones': 'Timezones',
'settings.appearance.widget.upcomingReservations': 'Upcoming reservations',
'settings.appearance.widget.atlas': 'Atlas / countries',
'settings.appearance.widget.tripsTotal': 'Trips total',
'settings.appearance.widget.daysTraveled': 'Days traveled',
'settings.appearance.widget.distanceFlown': 'Distance flown',
'settings.appearance.reset': 'Reset to defaults',
};
export default settings;
+42
View File
@@ -342,6 +342,48 @@ const settings: TranslationStrings = {
'settings.aiParsing.multimodalHint': 'Para modelos com capacidade de visão — envia o PDF original em vez do texto extraído.',
'settings.aiParsing.toast.saved': 'Configurações de IA salvas',
'settings.aiParsing.toast.saveError': 'Não foi possível salvar as configurações de IA',
'settings.tabs.appearance': 'Appearance',
'settings.appearance.theme': 'Theme',
'settings.appearance.scheme': 'Color scheme',
'settings.appearance.scheme.default': 'Default',
'settings.appearance.scheme.highContrast': 'High contrast',
'settings.appearance.scheme.indigo': 'Indigo',
'settings.appearance.scheme.teal': 'Teal',
'settings.appearance.scheme.rose': 'Rose',
'settings.appearance.scheme.amber': 'Amber',
'settings.appearance.scheme.violet': 'Violet',
'settings.appearance.scheme.custom': 'Custom',
'settings.appearance.customAccent': 'Custom accent',
'settings.appearance.contrastOk': 'Good contrast',
'settings.appearance.contrastLow': 'Low contrast',
'settings.appearance.readability': 'Readability',
'settings.appearance.transparency': 'Transparency',
'settings.appearance.transparencyHint': 'Glassy translucent surfaces. Turn off for solid, higher-contrast backgrounds.',
'settings.appearance.reduceMotion': 'Reduce motion',
'settings.appearance.reduceMotionHint': 'Minimize animations and transitions.',
'settings.appearance.density': 'Density',
'settings.appearance.comfortable': 'Comfortable',
'settings.appearance.compact': 'Compact',
'settings.appearance.textSize': 'Text size',
'settings.appearance.advancedTextSizes': 'Advanced text sizes',
'settings.appearance.hideAdvanced': 'Hide advanced',
'settings.appearance.tier.title': 'Titles',
'settings.appearance.tier.subtitle': 'Subtitles',
'settings.appearance.tier.body': 'Body',
'settings.appearance.tier.caption': 'Captions',
'settings.appearance.dashboardWidgets': 'Dashboard widgets',
'settings.appearance.dashboardWidgetsHint': 'Show or hide dashboard widgets independently on desktop and mobile.',
'settings.appearance.desktop': 'Desktop',
'settings.appearance.mobile': 'Mobile',
'settings.appearance.widget.sidebar': 'Right sidebar',
'settings.appearance.widget.currency': 'Currency',
'settings.appearance.widget.timezones': 'Timezones',
'settings.appearance.widget.upcomingReservations': 'Upcoming reservations',
'settings.appearance.widget.atlas': 'Atlas / countries',
'settings.appearance.widget.tripsTotal': 'Trips total',
'settings.appearance.widget.daysTraveled': 'Days traveled',
'settings.appearance.widget.distanceFlown': 'Distance flown',
'settings.appearance.reset': 'Reset to defaults',
};
export default settings;
+42
View File
@@ -339,6 +339,48 @@ const settings: TranslationStrings = {
'settings.aiParsing.multimodalHint': 'Pro modely se schopností zpracovat obraz — odešle původní PDF místo extrahovaného textu.',
'settings.aiParsing.toast.saved': 'Nastavení AI uloženo',
'settings.aiParsing.toast.saveError': 'Nastavení AI se nepodařilo uložit',
'settings.tabs.appearance': 'Appearance',
'settings.appearance.theme': 'Theme',
'settings.appearance.scheme': 'Color scheme',
'settings.appearance.scheme.default': 'Default',
'settings.appearance.scheme.highContrast': 'High contrast',
'settings.appearance.scheme.indigo': 'Indigo',
'settings.appearance.scheme.teal': 'Teal',
'settings.appearance.scheme.rose': 'Rose',
'settings.appearance.scheme.amber': 'Amber',
'settings.appearance.scheme.violet': 'Violet',
'settings.appearance.scheme.custom': 'Custom',
'settings.appearance.customAccent': 'Custom accent',
'settings.appearance.contrastOk': 'Good contrast',
'settings.appearance.contrastLow': 'Low contrast',
'settings.appearance.readability': 'Readability',
'settings.appearance.transparency': 'Transparency',
'settings.appearance.transparencyHint': 'Glassy translucent surfaces. Turn off for solid, higher-contrast backgrounds.',
'settings.appearance.reduceMotion': 'Reduce motion',
'settings.appearance.reduceMotionHint': 'Minimize animations and transitions.',
'settings.appearance.density': 'Density',
'settings.appearance.comfortable': 'Comfortable',
'settings.appearance.compact': 'Compact',
'settings.appearance.textSize': 'Text size',
'settings.appearance.advancedTextSizes': 'Advanced text sizes',
'settings.appearance.hideAdvanced': 'Hide advanced',
'settings.appearance.tier.title': 'Titles',
'settings.appearance.tier.subtitle': 'Subtitles',
'settings.appearance.tier.body': 'Body',
'settings.appearance.tier.caption': 'Captions',
'settings.appearance.dashboardWidgets': 'Dashboard widgets',
'settings.appearance.dashboardWidgetsHint': 'Show or hide dashboard widgets independently on desktop and mobile.',
'settings.appearance.desktop': 'Desktop',
'settings.appearance.mobile': 'Mobile',
'settings.appearance.widget.sidebar': 'Right sidebar',
'settings.appearance.widget.currency': 'Currency',
'settings.appearance.widget.timezones': 'Timezones',
'settings.appearance.widget.upcomingReservations': 'Upcoming reservations',
'settings.appearance.widget.atlas': 'Atlas / countries',
'settings.appearance.widget.tripsTotal': 'Trips total',
'settings.appearance.widget.daysTraveled': 'Days traveled',
'settings.appearance.widget.distanceFlown': 'Distance flown',
'settings.appearance.reset': 'Reset to defaults',
};
export default settings;
+42
View File
@@ -346,6 +346,48 @@ const settings: TranslationStrings = {
'settings.aiParsing.multimodalHint': 'Für Modelle mit Bildverständnis — sendet das Original-PDF statt extrahiertem Text.',
'settings.aiParsing.toast.saved': 'KI-Einstellungen gespeichert',
'settings.aiParsing.toast.saveError': 'KI-Einstellungen konnten nicht gespeichert werden',
'settings.tabs.appearance': 'Erscheinungsbild',
'settings.appearance.theme': 'Theme',
'settings.appearance.scheme': 'Farbschema',
'settings.appearance.scheme.default': 'Standard',
'settings.appearance.scheme.highContrast': 'Hoher Kontrast',
'settings.appearance.scheme.indigo': 'Indigo',
'settings.appearance.scheme.teal': 'Türkis',
'settings.appearance.scheme.rose': 'Rosé',
'settings.appearance.scheme.amber': 'Bernstein',
'settings.appearance.scheme.violet': 'Violett',
'settings.appearance.scheme.custom': 'Eigene',
'settings.appearance.customAccent': 'Eigene Akzentfarbe',
'settings.appearance.contrastOk': 'Guter Kontrast',
'settings.appearance.contrastLow': 'Niedriger Kontrast',
'settings.appearance.readability': 'Lesbarkeit',
'settings.appearance.transparency': 'Transparenz',
'settings.appearance.transparencyHint': 'Glasige, durchscheinende Flächen. Aus = solide, kontrastreichere Hintergründe.',
'settings.appearance.reduceMotion': 'Bewegung reduzieren',
'settings.appearance.reduceMotionHint': 'Animationen und Übergänge minimieren.',
'settings.appearance.density': 'Dichte',
'settings.appearance.comfortable': 'Komfortabel',
'settings.appearance.compact': 'Kompakt',
'settings.appearance.textSize': 'Textgröße',
'settings.appearance.advancedTextSizes': 'Erweiterte Textgrößen',
'settings.appearance.hideAdvanced': 'Erweitert ausblenden',
'settings.appearance.tier.title': 'Titel',
'settings.appearance.tier.subtitle': 'Untertitel',
'settings.appearance.tier.body': 'Fließtext',
'settings.appearance.tier.caption': 'Beschriftungen',
'settings.appearance.dashboardWidgets': 'Dashboard-Widgets',
'settings.appearance.dashboardWidgetsHint': 'Dashboard-Widgets unabhängig für Desktop und Mobil ein-/ausblenden.',
'settings.appearance.desktop': 'Desktop',
'settings.appearance.mobile': 'Mobil',
'settings.appearance.widget.sidebar': 'Rechte Seitenleiste',
'settings.appearance.widget.currency': 'Währung',
'settings.appearance.widget.timezones': 'Zeitzonen',
'settings.appearance.widget.upcomingReservations': 'Anstehende Reservierungen',
'settings.appearance.widget.atlas': 'Atlas / Länder',
'settings.appearance.widget.tripsTotal': 'Reisen gesamt',
'settings.appearance.widget.daysTraveled': 'Reisetage',
'settings.appearance.widget.distanceFlown': 'Flugdistanz',
'settings.appearance.reset': 'Auf Standard zurücksetzen',
};
export default settings;
+42
View File
@@ -340,6 +340,48 @@ const settings: TranslationStrings = {
'settings.aiParsing.multimodalHint': 'For vision-capable models — sends the original PDF instead of extracted text.',
'settings.aiParsing.toast.saved': 'AI settings saved',
'settings.aiParsing.toast.saveError': 'Could not save AI settings',
'settings.tabs.appearance': 'Appearance',
'settings.appearance.theme': 'Theme',
'settings.appearance.scheme': 'Color scheme',
'settings.appearance.scheme.default': 'Default',
'settings.appearance.scheme.highContrast': 'High contrast',
'settings.appearance.scheme.indigo': 'Indigo',
'settings.appearance.scheme.teal': 'Teal',
'settings.appearance.scheme.rose': 'Rose',
'settings.appearance.scheme.amber': 'Amber',
'settings.appearance.scheme.violet': 'Violet',
'settings.appearance.scheme.custom': 'Custom',
'settings.appearance.customAccent': 'Custom accent',
'settings.appearance.contrastOk': 'Good contrast',
'settings.appearance.contrastLow': 'Low contrast',
'settings.appearance.readability': 'Readability',
'settings.appearance.transparency': 'Transparency',
'settings.appearance.transparencyHint': 'Glassy translucent surfaces. Turn off for solid, higher-contrast backgrounds.',
'settings.appearance.reduceMotion': 'Reduce motion',
'settings.appearance.reduceMotionHint': 'Minimize animations and transitions.',
'settings.appearance.density': 'Density',
'settings.appearance.comfortable': 'Comfortable',
'settings.appearance.compact': 'Compact',
'settings.appearance.textSize': 'Text size',
'settings.appearance.advancedTextSizes': 'Advanced text sizes',
'settings.appearance.hideAdvanced': 'Hide advanced',
'settings.appearance.tier.title': 'Titles',
'settings.appearance.tier.subtitle': 'Subtitles',
'settings.appearance.tier.body': 'Body',
'settings.appearance.tier.caption': 'Captions',
'settings.appearance.dashboardWidgets': 'Dashboard widgets',
'settings.appearance.dashboardWidgetsHint': 'Show or hide dashboard widgets independently on desktop and mobile.',
'settings.appearance.desktop': 'Desktop',
'settings.appearance.mobile': 'Mobile',
'settings.appearance.widget.sidebar': 'Right sidebar',
'settings.appearance.widget.currency': 'Currency',
'settings.appearance.widget.timezones': 'Timezones',
'settings.appearance.widget.upcomingReservations': 'Upcoming reservations',
'settings.appearance.widget.atlas': 'Atlas / countries',
'settings.appearance.widget.tripsTotal': 'Trips total',
'settings.appearance.widget.daysTraveled': 'Days traveled',
'settings.appearance.widget.distanceFlown': 'Distance flown',
'settings.appearance.reset': 'Reset to defaults',
};
export default settings;
+42
View File
@@ -345,6 +345,48 @@ const settings: TranslationStrings = {
'settings.aiParsing.multimodalHint': 'Para modelos con visión: envía el PDF original en lugar del texto extraído.',
'settings.aiParsing.toast.saved': 'Ajustes de IA guardados',
'settings.aiParsing.toast.saveError': 'No se han podido guardar los ajustes de IA',
'settings.tabs.appearance': 'Appearance',
'settings.appearance.theme': 'Theme',
'settings.appearance.scheme': 'Color scheme',
'settings.appearance.scheme.default': 'Default',
'settings.appearance.scheme.highContrast': 'High contrast',
'settings.appearance.scheme.indigo': 'Indigo',
'settings.appearance.scheme.teal': 'Teal',
'settings.appearance.scheme.rose': 'Rose',
'settings.appearance.scheme.amber': 'Amber',
'settings.appearance.scheme.violet': 'Violet',
'settings.appearance.scheme.custom': 'Custom',
'settings.appearance.customAccent': 'Custom accent',
'settings.appearance.contrastOk': 'Good contrast',
'settings.appearance.contrastLow': 'Low contrast',
'settings.appearance.readability': 'Readability',
'settings.appearance.transparency': 'Transparency',
'settings.appearance.transparencyHint': 'Glassy translucent surfaces. Turn off for solid, higher-contrast backgrounds.',
'settings.appearance.reduceMotion': 'Reduce motion',
'settings.appearance.reduceMotionHint': 'Minimize animations and transitions.',
'settings.appearance.density': 'Density',
'settings.appearance.comfortable': 'Comfortable',
'settings.appearance.compact': 'Compact',
'settings.appearance.textSize': 'Text size',
'settings.appearance.advancedTextSizes': 'Advanced text sizes',
'settings.appearance.hideAdvanced': 'Hide advanced',
'settings.appearance.tier.title': 'Titles',
'settings.appearance.tier.subtitle': 'Subtitles',
'settings.appearance.tier.body': 'Body',
'settings.appearance.tier.caption': 'Captions',
'settings.appearance.dashboardWidgets': 'Dashboard widgets',
'settings.appearance.dashboardWidgetsHint': 'Show or hide dashboard widgets independently on desktop and mobile.',
'settings.appearance.desktop': 'Desktop',
'settings.appearance.mobile': 'Mobile',
'settings.appearance.widget.sidebar': 'Right sidebar',
'settings.appearance.widget.currency': 'Currency',
'settings.appearance.widget.timezones': 'Timezones',
'settings.appearance.widget.upcomingReservations': 'Upcoming reservations',
'settings.appearance.widget.atlas': 'Atlas / countries',
'settings.appearance.widget.tripsTotal': 'Trips total',
'settings.appearance.widget.daysTraveled': 'Days traveled',
'settings.appearance.widget.distanceFlown': 'Distance flown',
'settings.appearance.reset': 'Reset to defaults',
};
export default settings;
+42
View File
@@ -350,6 +350,48 @@ const settings: TranslationStrings = {
'settings.aiParsing.multimodalHint': 'Pour les modèles capables d\'analyser des images — envoie le PDF d\'origine au lieu du texte extrait.',
'settings.aiParsing.toast.saved': 'Paramètres d\'IA enregistrés',
'settings.aiParsing.toast.saveError': 'Impossible d\'enregistrer les paramètres d\'IA',
'settings.tabs.appearance': 'Appearance',
'settings.appearance.theme': 'Theme',
'settings.appearance.scheme': 'Color scheme',
'settings.appearance.scheme.default': 'Default',
'settings.appearance.scheme.highContrast': 'High contrast',
'settings.appearance.scheme.indigo': 'Indigo',
'settings.appearance.scheme.teal': 'Teal',
'settings.appearance.scheme.rose': 'Rose',
'settings.appearance.scheme.amber': 'Amber',
'settings.appearance.scheme.violet': 'Violet',
'settings.appearance.scheme.custom': 'Custom',
'settings.appearance.customAccent': 'Custom accent',
'settings.appearance.contrastOk': 'Good contrast',
'settings.appearance.contrastLow': 'Low contrast',
'settings.appearance.readability': 'Readability',
'settings.appearance.transparency': 'Transparency',
'settings.appearance.transparencyHint': 'Glassy translucent surfaces. Turn off for solid, higher-contrast backgrounds.',
'settings.appearance.reduceMotion': 'Reduce motion',
'settings.appearance.reduceMotionHint': 'Minimize animations and transitions.',
'settings.appearance.density': 'Density',
'settings.appearance.comfortable': 'Comfortable',
'settings.appearance.compact': 'Compact',
'settings.appearance.textSize': 'Text size',
'settings.appearance.advancedTextSizes': 'Advanced text sizes',
'settings.appearance.hideAdvanced': 'Hide advanced',
'settings.appearance.tier.title': 'Titles',
'settings.appearance.tier.subtitle': 'Subtitles',
'settings.appearance.tier.body': 'Body',
'settings.appearance.tier.caption': 'Captions',
'settings.appearance.dashboardWidgets': 'Dashboard widgets',
'settings.appearance.dashboardWidgetsHint': 'Show or hide dashboard widgets independently on desktop and mobile.',
'settings.appearance.desktop': 'Desktop',
'settings.appearance.mobile': 'Mobile',
'settings.appearance.widget.sidebar': 'Right sidebar',
'settings.appearance.widget.currency': 'Currency',
'settings.appearance.widget.timezones': 'Timezones',
'settings.appearance.widget.upcomingReservations': 'Upcoming reservations',
'settings.appearance.widget.atlas': 'Atlas / countries',
'settings.appearance.widget.tripsTotal': 'Trips total',
'settings.appearance.widget.daysTraveled': 'Days traveled',
'settings.appearance.widget.distanceFlown': 'Distance flown',
'settings.appearance.reset': 'Reset to defaults',
};
export default settings;
+42
View File
@@ -351,6 +351,48 @@ const settings: TranslationStrings = {
'settings.aiParsing.multimodalHint': 'Για μοντέλα με δυνατότητα όρασης — στέλνει το αρχικό PDF αντί για το εξαγόμενο κείμενο.',
'settings.aiParsing.toast.saved': 'Οι ρυθμίσεις AI αποθηκεύτηκαν',
'settings.aiParsing.toast.saveError': 'Δεν ήταν δυνατή η αποθήκευση των ρυθμίσεων AI',
'settings.tabs.appearance': 'Appearance',
'settings.appearance.theme': 'Theme',
'settings.appearance.scheme': 'Color scheme',
'settings.appearance.scheme.default': 'Default',
'settings.appearance.scheme.highContrast': 'High contrast',
'settings.appearance.scheme.indigo': 'Indigo',
'settings.appearance.scheme.teal': 'Teal',
'settings.appearance.scheme.rose': 'Rose',
'settings.appearance.scheme.amber': 'Amber',
'settings.appearance.scheme.violet': 'Violet',
'settings.appearance.scheme.custom': 'Custom',
'settings.appearance.customAccent': 'Custom accent',
'settings.appearance.contrastOk': 'Good contrast',
'settings.appearance.contrastLow': 'Low contrast',
'settings.appearance.readability': 'Readability',
'settings.appearance.transparency': 'Transparency',
'settings.appearance.transparencyHint': 'Glassy translucent surfaces. Turn off for solid, higher-contrast backgrounds.',
'settings.appearance.reduceMotion': 'Reduce motion',
'settings.appearance.reduceMotionHint': 'Minimize animations and transitions.',
'settings.appearance.density': 'Density',
'settings.appearance.comfortable': 'Comfortable',
'settings.appearance.compact': 'Compact',
'settings.appearance.textSize': 'Text size',
'settings.appearance.advancedTextSizes': 'Advanced text sizes',
'settings.appearance.hideAdvanced': 'Hide advanced',
'settings.appearance.tier.title': 'Titles',
'settings.appearance.tier.subtitle': 'Subtitles',
'settings.appearance.tier.body': 'Body',
'settings.appearance.tier.caption': 'Captions',
'settings.appearance.dashboardWidgets': 'Dashboard widgets',
'settings.appearance.dashboardWidgetsHint': 'Show or hide dashboard widgets independently on desktop and mobile.',
'settings.appearance.desktop': 'Desktop',
'settings.appearance.mobile': 'Mobile',
'settings.appearance.widget.sidebar': 'Right sidebar',
'settings.appearance.widget.currency': 'Currency',
'settings.appearance.widget.timezones': 'Timezones',
'settings.appearance.widget.upcomingReservations': 'Upcoming reservations',
'settings.appearance.widget.atlas': 'Atlas / countries',
'settings.appearance.widget.tripsTotal': 'Trips total',
'settings.appearance.widget.daysTraveled': 'Days traveled',
'settings.appearance.widget.distanceFlown': 'Distance flown',
'settings.appearance.reset': 'Reset to defaults',
};
export default settings;
+42
View File
@@ -344,6 +344,48 @@ const settings: TranslationStrings = {
'settings.aiParsing.multimodalHint': 'Képfelismerésre képes modellekhez — az eredeti PDF-et küldi a kinyert szöveg helyett.',
'settings.aiParsing.toast.saved': 'AI-beállítások elmentve',
'settings.aiParsing.toast.saveError': 'Az AI-beállítások mentése nem sikerült',
'settings.tabs.appearance': 'Appearance',
'settings.appearance.theme': 'Theme',
'settings.appearance.scheme': 'Color scheme',
'settings.appearance.scheme.default': 'Default',
'settings.appearance.scheme.highContrast': 'High contrast',
'settings.appearance.scheme.indigo': 'Indigo',
'settings.appearance.scheme.teal': 'Teal',
'settings.appearance.scheme.rose': 'Rose',
'settings.appearance.scheme.amber': 'Amber',
'settings.appearance.scheme.violet': 'Violet',
'settings.appearance.scheme.custom': 'Custom',
'settings.appearance.customAccent': 'Custom accent',
'settings.appearance.contrastOk': 'Good contrast',
'settings.appearance.contrastLow': 'Low contrast',
'settings.appearance.readability': 'Readability',
'settings.appearance.transparency': 'Transparency',
'settings.appearance.transparencyHint': 'Glassy translucent surfaces. Turn off for solid, higher-contrast backgrounds.',
'settings.appearance.reduceMotion': 'Reduce motion',
'settings.appearance.reduceMotionHint': 'Minimize animations and transitions.',
'settings.appearance.density': 'Density',
'settings.appearance.comfortable': 'Comfortable',
'settings.appearance.compact': 'Compact',
'settings.appearance.textSize': 'Text size',
'settings.appearance.advancedTextSizes': 'Advanced text sizes',
'settings.appearance.hideAdvanced': 'Hide advanced',
'settings.appearance.tier.title': 'Titles',
'settings.appearance.tier.subtitle': 'Subtitles',
'settings.appearance.tier.body': 'Body',
'settings.appearance.tier.caption': 'Captions',
'settings.appearance.dashboardWidgets': 'Dashboard widgets',
'settings.appearance.dashboardWidgetsHint': 'Show or hide dashboard widgets independently on desktop and mobile.',
'settings.appearance.desktop': 'Desktop',
'settings.appearance.mobile': 'Mobile',
'settings.appearance.widget.sidebar': 'Right sidebar',
'settings.appearance.widget.currency': 'Currency',
'settings.appearance.widget.timezones': 'Timezones',
'settings.appearance.widget.upcomingReservations': 'Upcoming reservations',
'settings.appearance.widget.atlas': 'Atlas / countries',
'settings.appearance.widget.tripsTotal': 'Trips total',
'settings.appearance.widget.daysTraveled': 'Days traveled',
'settings.appearance.widget.distanceFlown': 'Distance flown',
'settings.appearance.reset': 'Reset to defaults',
};
export default settings;
+42
View File
@@ -343,6 +343,48 @@ const settings: TranslationStrings = {
'settings.aiParsing.multimodalHint': 'Untuk model yang mendukung visi — mengirim PDF asli alih-alih teks yang diekstrak.',
'settings.aiParsing.toast.saved': 'Pengaturan AI disimpan',
'settings.aiParsing.toast.saveError': 'Tidak dapat menyimpan pengaturan AI',
'settings.tabs.appearance': 'Appearance',
'settings.appearance.theme': 'Theme',
'settings.appearance.scheme': 'Color scheme',
'settings.appearance.scheme.default': 'Default',
'settings.appearance.scheme.highContrast': 'High contrast',
'settings.appearance.scheme.indigo': 'Indigo',
'settings.appearance.scheme.teal': 'Teal',
'settings.appearance.scheme.rose': 'Rose',
'settings.appearance.scheme.amber': 'Amber',
'settings.appearance.scheme.violet': 'Violet',
'settings.appearance.scheme.custom': 'Custom',
'settings.appearance.customAccent': 'Custom accent',
'settings.appearance.contrastOk': 'Good contrast',
'settings.appearance.contrastLow': 'Low contrast',
'settings.appearance.readability': 'Readability',
'settings.appearance.transparency': 'Transparency',
'settings.appearance.transparencyHint': 'Glassy translucent surfaces. Turn off for solid, higher-contrast backgrounds.',
'settings.appearance.reduceMotion': 'Reduce motion',
'settings.appearance.reduceMotionHint': 'Minimize animations and transitions.',
'settings.appearance.density': 'Density',
'settings.appearance.comfortable': 'Comfortable',
'settings.appearance.compact': 'Compact',
'settings.appearance.textSize': 'Text size',
'settings.appearance.advancedTextSizes': 'Advanced text sizes',
'settings.appearance.hideAdvanced': 'Hide advanced',
'settings.appearance.tier.title': 'Titles',
'settings.appearance.tier.subtitle': 'Subtitles',
'settings.appearance.tier.body': 'Body',
'settings.appearance.tier.caption': 'Captions',
'settings.appearance.dashboardWidgets': 'Dashboard widgets',
'settings.appearance.dashboardWidgetsHint': 'Show or hide dashboard widgets independently on desktop and mobile.',
'settings.appearance.desktop': 'Desktop',
'settings.appearance.mobile': 'Mobile',
'settings.appearance.widget.sidebar': 'Right sidebar',
'settings.appearance.widget.currency': 'Currency',
'settings.appearance.widget.timezones': 'Timezones',
'settings.appearance.widget.upcomingReservations': 'Upcoming reservations',
'settings.appearance.widget.atlas': 'Atlas / countries',
'settings.appearance.widget.tripsTotal': 'Trips total',
'settings.appearance.widget.daysTraveled': 'Days traveled',
'settings.appearance.widget.distanceFlown': 'Distance flown',
'settings.appearance.reset': 'Reset to defaults',
};
export default settings;
+42
View File
@@ -343,6 +343,48 @@ const settings: TranslationStrings = {
'settings.aiParsing.multimodalHint': 'Per i modelli con capacità visive: invia il PDF originale invece del testo estratto.',
'settings.aiParsing.toast.saved': 'Impostazioni AI salvate',
'settings.aiParsing.toast.saveError': 'Impossibile salvare le impostazioni AI',
'settings.tabs.appearance': 'Appearance',
'settings.appearance.theme': 'Theme',
'settings.appearance.scheme': 'Color scheme',
'settings.appearance.scheme.default': 'Default',
'settings.appearance.scheme.highContrast': 'High contrast',
'settings.appearance.scheme.indigo': 'Indigo',
'settings.appearance.scheme.teal': 'Teal',
'settings.appearance.scheme.rose': 'Rose',
'settings.appearance.scheme.amber': 'Amber',
'settings.appearance.scheme.violet': 'Violet',
'settings.appearance.scheme.custom': 'Custom',
'settings.appearance.customAccent': 'Custom accent',
'settings.appearance.contrastOk': 'Good contrast',
'settings.appearance.contrastLow': 'Low contrast',
'settings.appearance.readability': 'Readability',
'settings.appearance.transparency': 'Transparency',
'settings.appearance.transparencyHint': 'Glassy translucent surfaces. Turn off for solid, higher-contrast backgrounds.',
'settings.appearance.reduceMotion': 'Reduce motion',
'settings.appearance.reduceMotionHint': 'Minimize animations and transitions.',
'settings.appearance.density': 'Density',
'settings.appearance.comfortable': 'Comfortable',
'settings.appearance.compact': 'Compact',
'settings.appearance.textSize': 'Text size',
'settings.appearance.advancedTextSizes': 'Advanced text sizes',
'settings.appearance.hideAdvanced': 'Hide advanced',
'settings.appearance.tier.title': 'Titles',
'settings.appearance.tier.subtitle': 'Subtitles',
'settings.appearance.tier.body': 'Body',
'settings.appearance.tier.caption': 'Captions',
'settings.appearance.dashboardWidgets': 'Dashboard widgets',
'settings.appearance.dashboardWidgetsHint': 'Show or hide dashboard widgets independently on desktop and mobile.',
'settings.appearance.desktop': 'Desktop',
'settings.appearance.mobile': 'Mobile',
'settings.appearance.widget.sidebar': 'Right sidebar',
'settings.appearance.widget.currency': 'Currency',
'settings.appearance.widget.timezones': 'Timezones',
'settings.appearance.widget.upcomingReservations': 'Upcoming reservations',
'settings.appearance.widget.atlas': 'Atlas / countries',
'settings.appearance.widget.tripsTotal': 'Trips total',
'settings.appearance.widget.daysTraveled': 'Days traveled',
'settings.appearance.widget.distanceFlown': 'Distance flown',
'settings.appearance.reset': 'Reset to defaults',
};
export default settings;
+42
View File
@@ -320,6 +320,48 @@ const settings: TranslationStrings = {
'settings.aiParsing.multimodalHint': '画像認識対応モデル向け — 抽出したテキストの代わりに元のPDFを送信します。',
'settings.aiParsing.toast.saved': 'AI設定を保存しました',
'settings.aiParsing.toast.saveError': 'AI設定を保存できませんでした',
'settings.tabs.appearance': 'Appearance',
'settings.appearance.theme': 'Theme',
'settings.appearance.scheme': 'Color scheme',
'settings.appearance.scheme.default': 'Default',
'settings.appearance.scheme.highContrast': 'High contrast',
'settings.appearance.scheme.indigo': 'Indigo',
'settings.appearance.scheme.teal': 'Teal',
'settings.appearance.scheme.rose': 'Rose',
'settings.appearance.scheme.amber': 'Amber',
'settings.appearance.scheme.violet': 'Violet',
'settings.appearance.scheme.custom': 'Custom',
'settings.appearance.customAccent': 'Custom accent',
'settings.appearance.contrastOk': 'Good contrast',
'settings.appearance.contrastLow': 'Low contrast',
'settings.appearance.readability': 'Readability',
'settings.appearance.transparency': 'Transparency',
'settings.appearance.transparencyHint': 'Glassy translucent surfaces. Turn off for solid, higher-contrast backgrounds.',
'settings.appearance.reduceMotion': 'Reduce motion',
'settings.appearance.reduceMotionHint': 'Minimize animations and transitions.',
'settings.appearance.density': 'Density',
'settings.appearance.comfortable': 'Comfortable',
'settings.appearance.compact': 'Compact',
'settings.appearance.textSize': 'Text size',
'settings.appearance.advancedTextSizes': 'Advanced text sizes',
'settings.appearance.hideAdvanced': 'Hide advanced',
'settings.appearance.tier.title': 'Titles',
'settings.appearance.tier.subtitle': 'Subtitles',
'settings.appearance.tier.body': 'Body',
'settings.appearance.tier.caption': 'Captions',
'settings.appearance.dashboardWidgets': 'Dashboard widgets',
'settings.appearance.dashboardWidgetsHint': 'Show or hide dashboard widgets independently on desktop and mobile.',
'settings.appearance.desktop': 'Desktop',
'settings.appearance.mobile': 'Mobile',
'settings.appearance.widget.sidebar': 'Right sidebar',
'settings.appearance.widget.currency': 'Currency',
'settings.appearance.widget.timezones': 'Timezones',
'settings.appearance.widget.upcomingReservations': 'Upcoming reservations',
'settings.appearance.widget.atlas': 'Atlas / countries',
'settings.appearance.widget.tripsTotal': 'Trips total',
'settings.appearance.widget.daysTraveled': 'Days traveled',
'settings.appearance.widget.distanceFlown': 'Distance flown',
'settings.appearance.reset': 'Reset to defaults',
};
export default settings;
+42
View File
@@ -335,6 +335,48 @@ const settings: TranslationStrings = {
'settings.aiParsing.multimodalHint': '비전 지원 모델용 — 추출된 텍스트 대신 원본 PDF를 전송합니다.',
'settings.aiParsing.toast.saved': 'AI 설정이 저장되었습니다',
'settings.aiParsing.toast.saveError': 'AI 설정을 저장할 수 없습니다',
'settings.tabs.appearance': 'Appearance',
'settings.appearance.theme': 'Theme',
'settings.appearance.scheme': 'Color scheme',
'settings.appearance.scheme.default': 'Default',
'settings.appearance.scheme.highContrast': 'High contrast',
'settings.appearance.scheme.indigo': 'Indigo',
'settings.appearance.scheme.teal': 'Teal',
'settings.appearance.scheme.rose': 'Rose',
'settings.appearance.scheme.amber': 'Amber',
'settings.appearance.scheme.violet': 'Violet',
'settings.appearance.scheme.custom': 'Custom',
'settings.appearance.customAccent': 'Custom accent',
'settings.appearance.contrastOk': 'Good contrast',
'settings.appearance.contrastLow': 'Low contrast',
'settings.appearance.readability': 'Readability',
'settings.appearance.transparency': 'Transparency',
'settings.appearance.transparencyHint': 'Glassy translucent surfaces. Turn off for solid, higher-contrast backgrounds.',
'settings.appearance.reduceMotion': 'Reduce motion',
'settings.appearance.reduceMotionHint': 'Minimize animations and transitions.',
'settings.appearance.density': 'Density',
'settings.appearance.comfortable': 'Comfortable',
'settings.appearance.compact': 'Compact',
'settings.appearance.textSize': 'Text size',
'settings.appearance.advancedTextSizes': 'Advanced text sizes',
'settings.appearance.hideAdvanced': 'Hide advanced',
'settings.appearance.tier.title': 'Titles',
'settings.appearance.tier.subtitle': 'Subtitles',
'settings.appearance.tier.body': 'Body',
'settings.appearance.tier.caption': 'Captions',
'settings.appearance.dashboardWidgets': 'Dashboard widgets',
'settings.appearance.dashboardWidgetsHint': 'Show or hide dashboard widgets independently on desktop and mobile.',
'settings.appearance.desktop': 'Desktop',
'settings.appearance.mobile': 'Mobile',
'settings.appearance.widget.sidebar': 'Right sidebar',
'settings.appearance.widget.currency': 'Currency',
'settings.appearance.widget.timezones': 'Timezones',
'settings.appearance.widget.upcomingReservations': 'Upcoming reservations',
'settings.appearance.widget.atlas': 'Atlas / countries',
'settings.appearance.widget.tripsTotal': 'Trips total',
'settings.appearance.widget.daysTraveled': 'Days traveled',
'settings.appearance.widget.distanceFlown': 'Distance flown',
'settings.appearance.reset': 'Reset to defaults',
};
export default settings;
+42
View File
@@ -343,6 +343,48 @@ const settings: TranslationStrings = {
'settings.aiParsing.multimodalHint': 'Voor modellen met beeldherkenning — verstuurt de originele PDF in plaats van geëxtraheerde tekst.',
'settings.aiParsing.toast.saved': 'AI-instellingen opgeslagen',
'settings.aiParsing.toast.saveError': 'AI-instellingen konden niet worden opgeslagen',
'settings.tabs.appearance': 'Appearance',
'settings.appearance.theme': 'Theme',
'settings.appearance.scheme': 'Color scheme',
'settings.appearance.scheme.default': 'Default',
'settings.appearance.scheme.highContrast': 'High contrast',
'settings.appearance.scheme.indigo': 'Indigo',
'settings.appearance.scheme.teal': 'Teal',
'settings.appearance.scheme.rose': 'Rose',
'settings.appearance.scheme.amber': 'Amber',
'settings.appearance.scheme.violet': 'Violet',
'settings.appearance.scheme.custom': 'Custom',
'settings.appearance.customAccent': 'Custom accent',
'settings.appearance.contrastOk': 'Good contrast',
'settings.appearance.contrastLow': 'Low contrast',
'settings.appearance.readability': 'Readability',
'settings.appearance.transparency': 'Transparency',
'settings.appearance.transparencyHint': 'Glassy translucent surfaces. Turn off for solid, higher-contrast backgrounds.',
'settings.appearance.reduceMotion': 'Reduce motion',
'settings.appearance.reduceMotionHint': 'Minimize animations and transitions.',
'settings.appearance.density': 'Density',
'settings.appearance.comfortable': 'Comfortable',
'settings.appearance.compact': 'Compact',
'settings.appearance.textSize': 'Text size',
'settings.appearance.advancedTextSizes': 'Advanced text sizes',
'settings.appearance.hideAdvanced': 'Hide advanced',
'settings.appearance.tier.title': 'Titles',
'settings.appearance.tier.subtitle': 'Subtitles',
'settings.appearance.tier.body': 'Body',
'settings.appearance.tier.caption': 'Captions',
'settings.appearance.dashboardWidgets': 'Dashboard widgets',
'settings.appearance.dashboardWidgetsHint': 'Show or hide dashboard widgets independently on desktop and mobile.',
'settings.appearance.desktop': 'Desktop',
'settings.appearance.mobile': 'Mobile',
'settings.appearance.widget.sidebar': 'Right sidebar',
'settings.appearance.widget.currency': 'Currency',
'settings.appearance.widget.timezones': 'Timezones',
'settings.appearance.widget.upcomingReservations': 'Upcoming reservations',
'settings.appearance.widget.atlas': 'Atlas / countries',
'settings.appearance.widget.tripsTotal': 'Trips total',
'settings.appearance.widget.daysTraveled': 'Days traveled',
'settings.appearance.widget.distanceFlown': 'Distance flown',
'settings.appearance.reset': 'Reset to defaults',
};
export default settings;
+42
View File
@@ -344,6 +344,48 @@ const settings: TranslationStrings = {
'settings.aiParsing.multimodalHint': 'Dla modeli obsługujących obraz — wysyła oryginalny plik PDF zamiast wyodrębnionego tekstu.',
'settings.aiParsing.toast.saved': 'Zapisano ustawienia AI',
'settings.aiParsing.toast.saveError': 'Nie udało się zapisać ustawień AI',
'settings.tabs.appearance': 'Appearance',
'settings.appearance.theme': 'Theme',
'settings.appearance.scheme': 'Color scheme',
'settings.appearance.scheme.default': 'Default',
'settings.appearance.scheme.highContrast': 'High contrast',
'settings.appearance.scheme.indigo': 'Indigo',
'settings.appearance.scheme.teal': 'Teal',
'settings.appearance.scheme.rose': 'Rose',
'settings.appearance.scheme.amber': 'Amber',
'settings.appearance.scheme.violet': 'Violet',
'settings.appearance.scheme.custom': 'Custom',
'settings.appearance.customAccent': 'Custom accent',
'settings.appearance.contrastOk': 'Good contrast',
'settings.appearance.contrastLow': 'Low contrast',
'settings.appearance.readability': 'Readability',
'settings.appearance.transparency': 'Transparency',
'settings.appearance.transparencyHint': 'Glassy translucent surfaces. Turn off for solid, higher-contrast backgrounds.',
'settings.appearance.reduceMotion': 'Reduce motion',
'settings.appearance.reduceMotionHint': 'Minimize animations and transitions.',
'settings.appearance.density': 'Density',
'settings.appearance.comfortable': 'Comfortable',
'settings.appearance.compact': 'Compact',
'settings.appearance.textSize': 'Text size',
'settings.appearance.advancedTextSizes': 'Advanced text sizes',
'settings.appearance.hideAdvanced': 'Hide advanced',
'settings.appearance.tier.title': 'Titles',
'settings.appearance.tier.subtitle': 'Subtitles',
'settings.appearance.tier.body': 'Body',
'settings.appearance.tier.caption': 'Captions',
'settings.appearance.dashboardWidgets': 'Dashboard widgets',
'settings.appearance.dashboardWidgetsHint': 'Show or hide dashboard widgets independently on desktop and mobile.',
'settings.appearance.desktop': 'Desktop',
'settings.appearance.mobile': 'Mobile',
'settings.appearance.widget.sidebar': 'Right sidebar',
'settings.appearance.widget.currency': 'Currency',
'settings.appearance.widget.timezones': 'Timezones',
'settings.appearance.widget.upcomingReservations': 'Upcoming reservations',
'settings.appearance.widget.atlas': 'Atlas / countries',
'settings.appearance.widget.tripsTotal': 'Trips total',
'settings.appearance.widget.daysTraveled': 'Days traveled',
'settings.appearance.widget.distanceFlown': 'Distance flown',
'settings.appearance.reset': 'Reset to defaults',
};
export default settings;
+42
View File
@@ -345,6 +345,48 @@ const settings: TranslationStrings = {
'settings.aiParsing.multimodalHint': 'Для моделей с поддержкой изображений — отправляет исходный PDF вместо извлечённого текста.',
'settings.aiParsing.toast.saved': 'Настройки ИИ сохранены',
'settings.aiParsing.toast.saveError': 'Не удалось сохранить настройки ИИ',
'settings.tabs.appearance': 'Appearance',
'settings.appearance.theme': 'Theme',
'settings.appearance.scheme': 'Color scheme',
'settings.appearance.scheme.default': 'Default',
'settings.appearance.scheme.highContrast': 'High contrast',
'settings.appearance.scheme.indigo': 'Indigo',
'settings.appearance.scheme.teal': 'Teal',
'settings.appearance.scheme.rose': 'Rose',
'settings.appearance.scheme.amber': 'Amber',
'settings.appearance.scheme.violet': 'Violet',
'settings.appearance.scheme.custom': 'Custom',
'settings.appearance.customAccent': 'Custom accent',
'settings.appearance.contrastOk': 'Good contrast',
'settings.appearance.contrastLow': 'Low contrast',
'settings.appearance.readability': 'Readability',
'settings.appearance.transparency': 'Transparency',
'settings.appearance.transparencyHint': 'Glassy translucent surfaces. Turn off for solid, higher-contrast backgrounds.',
'settings.appearance.reduceMotion': 'Reduce motion',
'settings.appearance.reduceMotionHint': 'Minimize animations and transitions.',
'settings.appearance.density': 'Density',
'settings.appearance.comfortable': 'Comfortable',
'settings.appearance.compact': 'Compact',
'settings.appearance.textSize': 'Text size',
'settings.appearance.advancedTextSizes': 'Advanced text sizes',
'settings.appearance.hideAdvanced': 'Hide advanced',
'settings.appearance.tier.title': 'Titles',
'settings.appearance.tier.subtitle': 'Subtitles',
'settings.appearance.tier.body': 'Body',
'settings.appearance.tier.caption': 'Captions',
'settings.appearance.dashboardWidgets': 'Dashboard widgets',
'settings.appearance.dashboardWidgetsHint': 'Show or hide dashboard widgets independently on desktop and mobile.',
'settings.appearance.desktop': 'Desktop',
'settings.appearance.mobile': 'Mobile',
'settings.appearance.widget.sidebar': 'Right sidebar',
'settings.appearance.widget.currency': 'Currency',
'settings.appearance.widget.timezones': 'Timezones',
'settings.appearance.widget.upcomingReservations': 'Upcoming reservations',
'settings.appearance.widget.atlas': 'Atlas / countries',
'settings.appearance.widget.tripsTotal': 'Trips total',
'settings.appearance.widget.daysTraveled': 'Days traveled',
'settings.appearance.widget.distanceFlown': 'Distance flown',
'settings.appearance.reset': 'Reset to defaults',
};
export default settings;
+42
View File
@@ -340,6 +340,48 @@ const settings: TranslationStrings = {
'settings.aiParsing.multimodalHint': 'För modeller med bildstöd — skickar original-PDF:en istället för extraherad text.',
'settings.aiParsing.toast.saved': 'AI-inställningar sparade',
'settings.aiParsing.toast.saveError': 'Kunde inte spara AI-inställningarna',
'settings.tabs.appearance': 'Appearance',
'settings.appearance.theme': 'Theme',
'settings.appearance.scheme': 'Color scheme',
'settings.appearance.scheme.default': 'Default',
'settings.appearance.scheme.highContrast': 'High contrast',
'settings.appearance.scheme.indigo': 'Indigo',
'settings.appearance.scheme.teal': 'Teal',
'settings.appearance.scheme.rose': 'Rose',
'settings.appearance.scheme.amber': 'Amber',
'settings.appearance.scheme.violet': 'Violet',
'settings.appearance.scheme.custom': 'Custom',
'settings.appearance.customAccent': 'Custom accent',
'settings.appearance.contrastOk': 'Good contrast',
'settings.appearance.contrastLow': 'Low contrast',
'settings.appearance.readability': 'Readability',
'settings.appearance.transparency': 'Transparency',
'settings.appearance.transparencyHint': 'Glassy translucent surfaces. Turn off for solid, higher-contrast backgrounds.',
'settings.appearance.reduceMotion': 'Reduce motion',
'settings.appearance.reduceMotionHint': 'Minimize animations and transitions.',
'settings.appearance.density': 'Density',
'settings.appearance.comfortable': 'Comfortable',
'settings.appearance.compact': 'Compact',
'settings.appearance.textSize': 'Text size',
'settings.appearance.advancedTextSizes': 'Advanced text sizes',
'settings.appearance.hideAdvanced': 'Hide advanced',
'settings.appearance.tier.title': 'Titles',
'settings.appearance.tier.subtitle': 'Subtitles',
'settings.appearance.tier.body': 'Body',
'settings.appearance.tier.caption': 'Captions',
'settings.appearance.dashboardWidgets': 'Dashboard widgets',
'settings.appearance.dashboardWidgetsHint': 'Show or hide dashboard widgets independently on desktop and mobile.',
'settings.appearance.desktop': 'Desktop',
'settings.appearance.mobile': 'Mobile',
'settings.appearance.widget.sidebar': 'Right sidebar',
'settings.appearance.widget.currency': 'Currency',
'settings.appearance.widget.timezones': 'Timezones',
'settings.appearance.widget.upcomingReservations': 'Upcoming reservations',
'settings.appearance.widget.atlas': 'Atlas / countries',
'settings.appearance.widget.tripsTotal': 'Trips total',
'settings.appearance.widget.daysTraveled': 'Days traveled',
'settings.appearance.widget.distanceFlown': 'Distance flown',
'settings.appearance.reset': 'Reset to defaults',
};
export default settings;
+42
View File
@@ -340,6 +340,48 @@ const settings: TranslationStrings = {
'settings.aiParsing.multimodalHint': 'Görüntü işleyebilen modeller için — çıkarılan metin yerine orijinal PDF\'yi gönderir.',
'settings.aiParsing.toast.saved': 'Yapay zekâ ayarları kaydedildi',
'settings.aiParsing.toast.saveError': 'Yapay zekâ ayarları kaydedilemedi',
'settings.tabs.appearance': 'Appearance',
'settings.appearance.theme': 'Theme',
'settings.appearance.scheme': 'Color scheme',
'settings.appearance.scheme.default': 'Default',
'settings.appearance.scheme.highContrast': 'High contrast',
'settings.appearance.scheme.indigo': 'Indigo',
'settings.appearance.scheme.teal': 'Teal',
'settings.appearance.scheme.rose': 'Rose',
'settings.appearance.scheme.amber': 'Amber',
'settings.appearance.scheme.violet': 'Violet',
'settings.appearance.scheme.custom': 'Custom',
'settings.appearance.customAccent': 'Custom accent',
'settings.appearance.contrastOk': 'Good contrast',
'settings.appearance.contrastLow': 'Low contrast',
'settings.appearance.readability': 'Readability',
'settings.appearance.transparency': 'Transparency',
'settings.appearance.transparencyHint': 'Glassy translucent surfaces. Turn off for solid, higher-contrast backgrounds.',
'settings.appearance.reduceMotion': 'Reduce motion',
'settings.appearance.reduceMotionHint': 'Minimize animations and transitions.',
'settings.appearance.density': 'Density',
'settings.appearance.comfortable': 'Comfortable',
'settings.appearance.compact': 'Compact',
'settings.appearance.textSize': 'Text size',
'settings.appearance.advancedTextSizes': 'Advanced text sizes',
'settings.appearance.hideAdvanced': 'Hide advanced',
'settings.appearance.tier.title': 'Titles',
'settings.appearance.tier.subtitle': 'Subtitles',
'settings.appearance.tier.body': 'Body',
'settings.appearance.tier.caption': 'Captions',
'settings.appearance.dashboardWidgets': 'Dashboard widgets',
'settings.appearance.dashboardWidgetsHint': 'Show or hide dashboard widgets independently on desktop and mobile.',
'settings.appearance.desktop': 'Desktop',
'settings.appearance.mobile': 'Mobile',
'settings.appearance.widget.sidebar': 'Right sidebar',
'settings.appearance.widget.currency': 'Currency',
'settings.appearance.widget.timezones': 'Timezones',
'settings.appearance.widget.upcomingReservations': 'Upcoming reservations',
'settings.appearance.widget.atlas': 'Atlas / countries',
'settings.appearance.widget.tripsTotal': 'Trips total',
'settings.appearance.widget.daysTraveled': 'Days traveled',
'settings.appearance.widget.distanceFlown': 'Distance flown',
'settings.appearance.reset': 'Reset to defaults',
};
export default settings;
+42
View File
@@ -344,6 +344,48 @@ const settings: TranslationStrings = {
'settings.aiParsing.multimodalHint': 'Для моделей із підтримкою зображень — надсилає оригінальний PDF замість вилученого тексту.',
'settings.aiParsing.toast.saved': 'Налаштування ШІ збережено',
'settings.aiParsing.toast.saveError': 'Не вдалося зберегти налаштування ШІ',
'settings.tabs.appearance': 'Appearance',
'settings.appearance.theme': 'Theme',
'settings.appearance.scheme': 'Color scheme',
'settings.appearance.scheme.default': 'Default',
'settings.appearance.scheme.highContrast': 'High contrast',
'settings.appearance.scheme.indigo': 'Indigo',
'settings.appearance.scheme.teal': 'Teal',
'settings.appearance.scheme.rose': 'Rose',
'settings.appearance.scheme.amber': 'Amber',
'settings.appearance.scheme.violet': 'Violet',
'settings.appearance.scheme.custom': 'Custom',
'settings.appearance.customAccent': 'Custom accent',
'settings.appearance.contrastOk': 'Good contrast',
'settings.appearance.contrastLow': 'Low contrast',
'settings.appearance.readability': 'Readability',
'settings.appearance.transparency': 'Transparency',
'settings.appearance.transparencyHint': 'Glassy translucent surfaces. Turn off for solid, higher-contrast backgrounds.',
'settings.appearance.reduceMotion': 'Reduce motion',
'settings.appearance.reduceMotionHint': 'Minimize animations and transitions.',
'settings.appearance.density': 'Density',
'settings.appearance.comfortable': 'Comfortable',
'settings.appearance.compact': 'Compact',
'settings.appearance.textSize': 'Text size',
'settings.appearance.advancedTextSizes': 'Advanced text sizes',
'settings.appearance.hideAdvanced': 'Hide advanced',
'settings.appearance.tier.title': 'Titles',
'settings.appearance.tier.subtitle': 'Subtitles',
'settings.appearance.tier.body': 'Body',
'settings.appearance.tier.caption': 'Captions',
'settings.appearance.dashboardWidgets': 'Dashboard widgets',
'settings.appearance.dashboardWidgetsHint': 'Show or hide dashboard widgets independently on desktop and mobile.',
'settings.appearance.desktop': 'Desktop',
'settings.appearance.mobile': 'Mobile',
'settings.appearance.widget.sidebar': 'Right sidebar',
'settings.appearance.widget.currency': 'Currency',
'settings.appearance.widget.timezones': 'Timezones',
'settings.appearance.widget.upcomingReservations': 'Upcoming reservations',
'settings.appearance.widget.atlas': 'Atlas / countries',
'settings.appearance.widget.tripsTotal': 'Trips total',
'settings.appearance.widget.daysTraveled': 'Days traveled',
'settings.appearance.widget.distanceFlown': 'Distance flown',
'settings.appearance.reset': 'Reset to defaults',
};
export default settings;
+42
View File
@@ -324,6 +324,48 @@ const settings: TranslationStrings = {
'settings.aiParsing.multimodalHint': '適用於具備視覺能力的模型 — 傳送原始 PDF,而非擷取出的文字。',
'settings.aiParsing.toast.saved': 'AI 設定已儲存',
'settings.aiParsing.toast.saveError': '無法儲存 AI 設定',
'settings.tabs.appearance': 'Appearance',
'settings.appearance.theme': 'Theme',
'settings.appearance.scheme': 'Color scheme',
'settings.appearance.scheme.default': 'Default',
'settings.appearance.scheme.highContrast': 'High contrast',
'settings.appearance.scheme.indigo': 'Indigo',
'settings.appearance.scheme.teal': 'Teal',
'settings.appearance.scheme.rose': 'Rose',
'settings.appearance.scheme.amber': 'Amber',
'settings.appearance.scheme.violet': 'Violet',
'settings.appearance.scheme.custom': 'Custom',
'settings.appearance.customAccent': 'Custom accent',
'settings.appearance.contrastOk': 'Good contrast',
'settings.appearance.contrastLow': 'Low contrast',
'settings.appearance.readability': 'Readability',
'settings.appearance.transparency': 'Transparency',
'settings.appearance.transparencyHint': 'Glassy translucent surfaces. Turn off for solid, higher-contrast backgrounds.',
'settings.appearance.reduceMotion': 'Reduce motion',
'settings.appearance.reduceMotionHint': 'Minimize animations and transitions.',
'settings.appearance.density': 'Density',
'settings.appearance.comfortable': 'Comfortable',
'settings.appearance.compact': 'Compact',
'settings.appearance.textSize': 'Text size',
'settings.appearance.advancedTextSizes': 'Advanced text sizes',
'settings.appearance.hideAdvanced': 'Hide advanced',
'settings.appearance.tier.title': 'Titles',
'settings.appearance.tier.subtitle': 'Subtitles',
'settings.appearance.tier.body': 'Body',
'settings.appearance.tier.caption': 'Captions',
'settings.appearance.dashboardWidgets': 'Dashboard widgets',
'settings.appearance.dashboardWidgetsHint': 'Show or hide dashboard widgets independently on desktop and mobile.',
'settings.appearance.desktop': 'Desktop',
'settings.appearance.mobile': 'Mobile',
'settings.appearance.widget.sidebar': 'Right sidebar',
'settings.appearance.widget.currency': 'Currency',
'settings.appearance.widget.timezones': 'Timezones',
'settings.appearance.widget.upcomingReservations': 'Upcoming reservations',
'settings.appearance.widget.atlas': 'Atlas / countries',
'settings.appearance.widget.tripsTotal': 'Trips total',
'settings.appearance.widget.daysTraveled': 'Days traveled',
'settings.appearance.widget.distanceFlown': 'Distance flown',
'settings.appearance.reset': 'Reset to defaults',
};
export default settings;
+42
View File
@@ -322,6 +322,48 @@ const settings: TranslationStrings = {
'settings.aiParsing.multimodalHint': '适用于支持视觉的模型——发送原始 PDF 而非提取的文本。',
'settings.aiParsing.toast.saved': 'AI 设置已保存',
'settings.aiParsing.toast.saveError': '无法保存 AI 设置',
'settings.tabs.appearance': 'Appearance',
'settings.appearance.theme': 'Theme',
'settings.appearance.scheme': 'Color scheme',
'settings.appearance.scheme.default': 'Default',
'settings.appearance.scheme.highContrast': 'High contrast',
'settings.appearance.scheme.indigo': 'Indigo',
'settings.appearance.scheme.teal': 'Teal',
'settings.appearance.scheme.rose': 'Rose',
'settings.appearance.scheme.amber': 'Amber',
'settings.appearance.scheme.violet': 'Violet',
'settings.appearance.scheme.custom': 'Custom',
'settings.appearance.customAccent': 'Custom accent',
'settings.appearance.contrastOk': 'Good contrast',
'settings.appearance.contrastLow': 'Low contrast',
'settings.appearance.readability': 'Readability',
'settings.appearance.transparency': 'Transparency',
'settings.appearance.transparencyHint': 'Glassy translucent surfaces. Turn off for solid, higher-contrast backgrounds.',
'settings.appearance.reduceMotion': 'Reduce motion',
'settings.appearance.reduceMotionHint': 'Minimize animations and transitions.',
'settings.appearance.density': 'Density',
'settings.appearance.comfortable': 'Comfortable',
'settings.appearance.compact': 'Compact',
'settings.appearance.textSize': 'Text size',
'settings.appearance.advancedTextSizes': 'Advanced text sizes',
'settings.appearance.hideAdvanced': 'Hide advanced',
'settings.appearance.tier.title': 'Titles',
'settings.appearance.tier.subtitle': 'Subtitles',
'settings.appearance.tier.body': 'Body',
'settings.appearance.tier.caption': 'Captions',
'settings.appearance.dashboardWidgets': 'Dashboard widgets',
'settings.appearance.dashboardWidgetsHint': 'Show or hide dashboard widgets independently on desktop and mobile.',
'settings.appearance.desktop': 'Desktop',
'settings.appearance.mobile': 'Mobile',
'settings.appearance.widget.sidebar': 'Right sidebar',
'settings.appearance.widget.currency': 'Currency',
'settings.appearance.widget.timezones': 'Timezones',
'settings.appearance.widget.upcomingReservations': 'Upcoming reservations',
'settings.appearance.widget.atlas': 'Atlas / countries',
'settings.appearance.widget.tripsTotal': 'Trips total',
'settings.appearance.widget.daysTraveled': 'Days traveled',
'settings.appearance.widget.distanceFlown': 'Distance flown',
'settings.appearance.reset': 'Reset to defaults',
};
export default settings;