feat(admin): let admins set a default currency for new users

Adds a currency picker to Admin > User Defaults. Stored as the default_currency
user-default, so users who have not picked their own currency inherit it in
Costs.
This commit is contained in:
Maurice
2026-06-17 23:12:30 +02:00
parent 17245c5a8c
commit 63fb5a9c89
2 changed files with 22 additions and 0 deletions
@@ -6,6 +6,7 @@ import { useToast } from '../shared/Toast'
import Section from '../Settings/Section' import Section from '../Settings/Section'
import CustomSelect from '../shared/CustomSelect' import CustomSelect from '../shared/CustomSelect'
import { MapView } from '../Map/MapView' import { MapView } from '../Map/MapView'
import { CURRENCIES, SYMBOLS } from '../Budget/BudgetPanel.constants'
import type { Place } from '../../types' import type { Place } from '../../types'
const MAP_PRESETS = [ const MAP_PRESETS = [
@@ -20,6 +21,7 @@ type Defaults = {
temperature_unit?: string temperature_unit?: string
dark_mode?: string | boolean dark_mode?: string | boolean
time_format?: string time_format?: string
default_currency?: string
blur_booking_codes?: boolean blur_booking_codes?: boolean
map_tile_url?: string map_tile_url?: string
map_provider?: string map_provider?: string
@@ -226,6 +228,23 @@ export default function DefaultUserSettingsTab(): React.ReactElement {
))} ))}
</OptionRow> </OptionRow>
{/* Default Currency */}
<div>
<label className="block text-sm font-medium mb-1.5 text-content-secondary">
{t('settings.currency')} <ResetButton field="default_currency" />
</label>
<CustomSelect
value={defaults.default_currency || ''}
onChange={(value: string) => { if (value) save({ default_currency: value }) }}
placeholder={t('settings.currency')}
searchable
options={CURRENCIES.map(c => ({ value: c, label: SYMBOLS[c] ? `${c} ${SYMBOLS[c]}` : c }))}
size="sm"
style={{ maxWidth: 240 }}
/>
<p className="text-xs mt-1 text-content-faint">{t('settings.currencyHint')}</p>
</div>
{/* Blur Booking Codes */} {/* Blur Booking Codes */}
<OptionRow label={<>{t('settings.blurBookingCodes')} <ResetButton field="blur_booking_codes" /></>}> <OptionRow label={<>{t('settings.blurBookingCodes')} <ResetButton field="blur_booking_codes" /></>}>
{([ {([
+3
View File
@@ -10,6 +10,9 @@ export const DEFAULTABLE_USER_SETTING_KEYS = [
'temperature_unit', 'temperature_unit',
'dark_mode', 'dark_mode',
'time_format', 'time_format',
// Instance-wide default currency for Costs (new users inherit it until they
// pick their own). Free-form ISO code, validated on the client.
'default_currency',
'blur_booking_codes', 'blur_booking_codes',
'map_tile_url', 'map_tile_url',
// Instance-wide Mapbox defaults: an admin can set a shared token + style so the // Instance-wide Mapbox defaults: an admin can set a shared token + style so the