import React, { useState, useEffect, useRef } from 'react' import { Languages, Map, ChevronDown, Rocket } from 'lucide-react' import { SUPPORTED_LANGUAGES, useTranslation } from '../../i18n' import { DEFAULT_SETTINGS } from '../../store/settingsStore' import CustomSelect from '../shared/CustomSelect' import { Segmented } from '../shared/dialogParts' import { fs } from '../shared/DialogShell' import { POPOVER } from '../Packing/packingPopoverStyles' import { PopoverItem } from '../Packing/PackingPopover' import { preferredNavAppOptions } from '../Planner/placeNavigation' import { SYMBOLS, currenciesWith } from '../Budget/BudgetPanel.constants' import Section from './Section' import ToggleSwitch from './ToggleSwitch' import { ChoiceChips, SettingRow, SettingRows } from './settingsKit' import { useSettingSaver } from './useSettingSaver' import { TRIP_TAB_IDS, TRIP_TAB_LABEL_KEYS } from '../../constants/tripTabs' import { DEFAULT_START_PAGE, DEFAULT_START_TRIP_TAB } from '../../utils/startDestination' import type { DistanceUnit } from '../../types' import { DEFAULT_WEEK_START } from '@trek/shared' import { weekStartOptions } from '../../utils/calendarWeek' /** A select on the right of a row: wide enough for a currency or a weekday, never wider than the row. */ const SELECT_BOX = 'w-[260px] max-w-full' export default function DisplaySettingsTab(): React.ReactElement { const { settings, save } = useSettingSaver() const { t, locale } = useTranslation() const [tempUnit, setTempUnit] = useState(settings.temperature_unit || DEFAULT_SETTINGS.temperature_unit) const [distanceUnit, setDistanceUnit] = useState(settings.distance_unit || DEFAULT_SETTINGS.distance_unit) const [langOpen, setLangOpen] = useState(false) const langDropdownRef = useRef(null) useEffect(() => { if (!langOpen) return const handler = (e: MouseEvent) => { if (langDropdownRef.current && !langDropdownRef.current.contains(e.target as Node)) setLangOpen(false) } document.addEventListener('mousedown', handler) return () => document.removeEventListener('mousedown', handler) }, [langOpen]) useEffect(() => { setTempUnit(settings.temperature_unit || DEFAULT_SETTINGS.temperature_unit) }, [settings.temperature_unit]) useEffect(() => { setDistanceUnit(settings.distance_unit || DEFAULT_SETTINGS.distance_unit) }, [settings.distance_unit]) const saveOnOff = (key: OnOffKey, value: boolean) => save(key, value) const startPage = settings.start_page === 'active_trip' ? 'active_trip' : DEFAULT_START_PAGE const startTripTab = settings.start_trip_tab || DEFAULT_START_TRIP_TAB const currentLanguage = SUPPORTED_LANGUAGES.find(o => o.value === settings.language) || SUPPORTED_LANGUAGES[0] const pickLanguage = (value: string) => save('language', value) return ( <>
{/* Where opening TREK lands */} { await save('start_page', value) }} /> } /> {/* Which planner tab the trip opens on — only meaningful for 'active_trip' */} {startPage === 'active_trip' && ( { await save('start_trip_tab', String(v)) }} options={TRIP_TAB_IDS.map(id => ({ value: id, label: t(TRIP_TAB_LABEL_KEYS[id]) }))} /> } /> )}
{/* Display currency. Unset ('') means "no personal preference": Costs then shows each trip in its own currency, instead of forcing every trip through one. */} { await save('default_currency', String(v)) }} options={[ { value: '', label: t('settings.currencyTrip') }, ...currenciesWith(settings.default_currency || '').map(c => ({ value: c, label: `${c} — ${SYMBOLS[c] || c}` })), ]} searchable /> } /> {/* Language: every option at a glance on a wide window, a compact list on a narrow one */}
({ value: opt.value, label: opt.label }))} onChange={pickLanguage} />
{langOpen && (
{SUPPORTED_LANGUAGES.map(opt => ( { setLangOpen(false) await pickLanguage(opt.value) }} /> ))}
)}
{/* Place names (#1799): searches can answer in another language than the app. */} { await save('place_language', String(v)) }} options={[ { value: '', label: t('settings.placeLanguageApp') }, ...SUPPORTED_LANGUAGES.map(opt => ({ value: opt.value, label: opt.label })), ]} searchable /> } /> {/* Temperature */} { setTempUnit(value) await save('temperature_unit', value) }} /> } /> {/* Distance */} label={t('settings.distance')} value={distanceUnit} options={[ { value: 'metric', label: 'km Metric' }, { value: 'imperial', label: 'mi Imperial' }, ]} onChange={async value => { setDistanceUnit(value) await save('distance_unit', value) }} /> } /> {/* Time Format */} ({ value: opt.value, label: <>{opt.short}{` (${opt.example})`}, }))} onChange={async value => { await save('time_format', value) }} /> } /> {/* Week start: the first column of every date picker (#2029) */} { await save('week_start', String(v)) }} options={weekStartOptions(locale)} /> } />
{/* Preferred map app (#2423): opt-in, the picker stays the default */} { await save('preferred_nav_app', String(v)) }} options={preferredNavAppOptions(t)} /> } /> {/* Date first in day headings (#1953) */} saveOnOff('day_date_first', value)} /> {/* Booking route labels */} saveOnOff('map_booking_labels', value)} /> {/* Always show booking routes */} saveOnOff('map_always_show_routes', value)} /> {/* Unplanned places as small markers (#2024) */} saveOnOff('map_compact_unplanned', value)} /> {/* Explore places on the map (POI category pill) */} saveOnOff('map_poi_pill_enabled', value)} /> {/* Blur Booking Codes */} saveOnOff('blur_booking_codes', value)} /> {/* Optimize route from accommodation */} saveOnOff('optimize_from_accommodation', value)} />
) } type OnOffKey = 'day_date_first' | 'map_compact_unplanned' | 'map_booking_labels' | 'map_always_show_routes' | 'map_poi_pill_enabled' | 'blur_booking_codes' | 'optimize_from_accommodation' /** One yes-or-no preference as a row with a switch, the way the planner's settings dialogs ask it. */ function OnOffSetting({ label, hint, on, onChange }: { label: string; hint?: string; on: boolean; onChange: (value: boolean) => void }) { return ( onChange(!on)} label={label} />} /> ) }