mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-19 13:21:46 +00:00
remove route_calculation setting, always use OSRM routing (#1064)
The per-user route_calculation toggle was a second, hidden on/off layer on top of the day footer's show-route button, and made it easy to end up with straight-line routes for no obvious reason. Drop the setting entirely: routing is always on, the footer toggle stays the single switch. Old stored values are simply ignored (settings are key-value, no migration needed).
This commit is contained in:
@@ -20,7 +20,6 @@ type Defaults = {
|
||||
temperature_unit?: string
|
||||
dark_mode?: string | boolean
|
||||
time_format?: string
|
||||
route_calculation?: boolean
|
||||
blur_booking_codes?: boolean
|
||||
map_tile_url?: string
|
||||
}
|
||||
@@ -208,22 +207,6 @@ export default function DefaultUserSettingsTab(): React.ReactElement {
|
||||
))}
|
||||
</OptionRow>
|
||||
|
||||
{/* Route Calculation */}
|
||||
<OptionRow label={<>{t('settings.routeCalculation')} <ResetButton field="route_calculation" /></>}>
|
||||
{([
|
||||
{ value: true, label: t('settings.on') || 'On' },
|
||||
{ value: false, label: t('settings.off') || 'Off' },
|
||||
] as const).map(opt => (
|
||||
<OptionButton
|
||||
key={String(opt.value)}
|
||||
active={defaults.route_calculation === opt.value}
|
||||
onClick={() => save({ route_calculation: opt.value })}
|
||||
>
|
||||
{opt.label}
|
||||
</OptionButton>
|
||||
))}
|
||||
</OptionRow>
|
||||
|
||||
{/* Blur Booking Codes */}
|
||||
<OptionRow label={<>{t('settings.blurBookingCodes')} <ResetButton field="blur_booking_codes" /></>}>
|
||||
{([
|
||||
|
||||
@@ -260,7 +260,6 @@ const DayPlanSidebar = React.memo(function DayPlanSidebar({
|
||||
const { t, language, locale } = useTranslation()
|
||||
const ctxMenu = useContextMenu()
|
||||
const timeFormat = useSettingsStore(s => s.settings.time_format) || '24h'
|
||||
const routeCalcEnabled = useSettingsStore(s => s.settings.route_calculation) !== false
|
||||
const tripActions = useRef(useTripStore.getState()).current
|
||||
const can = useCanDo()
|
||||
const canEditDays = can('day_edit', trip)
|
||||
@@ -507,7 +506,7 @@ const DayPlanSidebar = React.memo(function DayPlanSidebar({
|
||||
// the start place's assignment id. Shares RouteCalculator's cache with the map.
|
||||
useEffect(() => {
|
||||
if (legsAbortRef.current) legsAbortRef.current.abort()
|
||||
if (!selectedDayId || !routeCalcEnabled || !routeShown) { setRouteLegs({}); return }
|
||||
if (!selectedDayId || !routeShown) { setRouteLegs({}); return }
|
||||
const merged = mergedItemsMap[selectedDayId] || []
|
||||
const runs: { id: number; lat: number; lng: number }[][] = []
|
||||
let cur: { id: number; lat: number; lng: number }[] = []
|
||||
@@ -536,7 +535,7 @@ const DayPlanSidebar = React.memo(function DayPlanSidebar({
|
||||
}
|
||||
if (!controller.signal.aborted) setRouteLegs(map)
|
||||
})()
|
||||
}, [selectedDayId, routeCalcEnabled, routeShown, routeProfile, mergedItemsMap])
|
||||
}, [selectedDayId, routeShown, routeProfile, mergedItemsMap])
|
||||
|
||||
const openAddNote = (dayId, e) => {
|
||||
e?.stopPropagation()
|
||||
|
||||
@@ -27,7 +27,7 @@ beforeEach(() => {
|
||||
resetAllStores();
|
||||
seedStore(useAuthStore, { user: buildUser(), isAuthenticated: true });
|
||||
seedStore(useTripStore, { trip: buildTrip({ id: 1 }) });
|
||||
seedStore(useSettingsStore, { settings: { time_format: '24h', blur_booking_codes: false, temperature_unit: 'celsius', language: 'en', dark_mode: false, default_currency: 'USD', default_lat: 48.8566, default_lng: 2.3522, default_zoom: 10, map_tile_url: '', show_place_description: false, route_calculation: false } });
|
||||
seedStore(useSettingsStore, { settings: { time_format: '24h', blur_booking_codes: false, temperature_unit: 'celsius', language: 'en', dark_mode: false, default_currency: 'USD', default_lat: 48.8566, default_lng: 2.3522, default_zoom: 10, map_tile_url: '', show_place_description: false } });
|
||||
});
|
||||
|
||||
describe('ReservationsPanel', () => {
|
||||
@@ -211,7 +211,7 @@ describe('ReservationsPanel', () => {
|
||||
});
|
||||
|
||||
it('FE-PLANNER-RESP-022: confirmation number is blurred when blur_booking_codes=true', () => {
|
||||
seedStore(useSettingsStore, { settings: { time_format: '24h', blur_booking_codes: true, temperature_unit: 'celsius', language: 'en', dark_mode: false, default_currency: 'USD', default_lat: 48.8566, default_lng: 2.3522, default_zoom: 10, map_tile_url: '', show_place_description: false, route_calculation: false } });
|
||||
seedStore(useSettingsStore, { settings: { time_format: '24h', blur_booking_codes: true, temperature_unit: 'celsius', language: 'en', dark_mode: false, default_currency: 'USD', default_lat: 48.8566, default_lng: 2.3522, default_zoom: 10, map_tile_url: '', show_place_description: false } });
|
||||
const res = buildReservation({ confirmation_number: 'ABC123', status: 'confirmed' });
|
||||
render(<ReservationsPanel {...defaultProps} reservations={[res]} />);
|
||||
const codeEl = screen.getByText('ABC123');
|
||||
@@ -220,7 +220,7 @@ describe('ReservationsPanel', () => {
|
||||
|
||||
it('FE-PLANNER-RESP-023: confirmation code revealed on hover when blurred', async () => {
|
||||
const user = userEvent.setup();
|
||||
seedStore(useSettingsStore, { settings: { time_format: '24h', blur_booking_codes: true, temperature_unit: 'celsius', language: 'en', dark_mode: false, default_currency: 'USD', default_lat: 48.8566, default_lng: 2.3522, default_zoom: 10, map_tile_url: '', show_place_description: false, route_calculation: false } });
|
||||
seedStore(useSettingsStore, { settings: { time_format: '24h', blur_booking_codes: true, temperature_unit: 'celsius', language: 'en', dark_mode: false, default_currency: 'USD', default_lat: 48.8566, default_lng: 2.3522, default_zoom: 10, map_tile_url: '', show_place_description: false } });
|
||||
const res = buildReservation({ confirmation_number: 'ABC123', status: 'confirmed' });
|
||||
render(<ReservationsPanel {...defaultProps} reservations={[res]} />);
|
||||
const codeEl = screen.getByText('ABC123');
|
||||
|
||||
@@ -161,29 +161,6 @@ describe('DisplaySettingsTab', () => {
|
||||
expect(updateSetting).toHaveBeenCalledWith('time_format', '24h');
|
||||
});
|
||||
|
||||
it('FE-COMP-DISPLAY-021: shows Route Calculation section', () => {
|
||||
render(<DisplaySettingsTab />);
|
||||
expect(screen.getByText(/route calculation/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('FE-COMP-DISPLAY-022: route calculation On button is active when route_calculation is true', () => {
|
||||
seedStore(useSettingsStore, { settings: buildSettings({ route_calculation: true }) });
|
||||
render(<DisplaySettingsTab />);
|
||||
const onButtons = screen.getAllByText(/^On$/i);
|
||||
const routeCalcOnBtn = onButtons[0].closest('button')!;
|
||||
expect(routeCalcOnBtn.style.border).toContain('var(--text-primary)');
|
||||
});
|
||||
|
||||
it('FE-COMP-DISPLAY-023: clicking route calculation Off calls updateSetting with false', async () => {
|
||||
const user = userEvent.setup();
|
||||
const updateSetting = vi.fn().mockResolvedValue(undefined);
|
||||
seedStore(useSettingsStore, { settings: buildSettings({ route_calculation: true }), updateSetting });
|
||||
render(<DisplaySettingsTab />);
|
||||
const offButtons = screen.getAllByText(/^Off$/i);
|
||||
await user.click(offButtons[0]);
|
||||
expect(updateSetting).toHaveBeenCalledWith('route_calculation', false);
|
||||
});
|
||||
|
||||
it('FE-COMP-DISPLAY-024: shows Blur Booking Codes section', () => {
|
||||
render(<DisplaySettingsTab />);
|
||||
expect(screen.getByText(/blur booking codes/i)).toBeInTheDocument();
|
||||
|
||||
@@ -214,36 +214,6 @@ export default function DisplaySettingsTab(): React.ReactElement {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Route Calculation */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--text-secondary)' }}>{t('settings.routeCalculation')}</label>
|
||||
<div className="flex gap-3">
|
||||
{[
|
||||
{ value: true, label: t('settings.on') || 'On' },
|
||||
{ value: false, label: t('settings.off') || 'Off' },
|
||||
].map(opt => (
|
||||
<button
|
||||
key={String(opt.value)}
|
||||
onClick={async () => {
|
||||
try { await updateSetting('route_calculation', opt.value) }
|
||||
catch (e: unknown) { toast.error(e instanceof Error ? e.message : t('common.error')) }
|
||||
}}
|
||||
style={{
|
||||
display: 'flex', alignItems: 'center', gap: 8,
|
||||
padding: '10px 20px', borderRadius: 10, cursor: 'pointer',
|
||||
fontFamily: 'inherit', fontSize: 14, fontWeight: 500,
|
||||
border: (settings.route_calculation !== false) === opt.value ? '2px solid var(--text-primary)' : '2px solid var(--border-primary)',
|
||||
background: (settings.route_calculation !== false) === opt.value ? 'var(--bg-hover)' : 'var(--bg-card)',
|
||||
color: 'var(--text-primary)',
|
||||
transition: 'all 0.15s',
|
||||
}}
|
||||
>
|
||||
{opt.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Booking route labels */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--text-secondary)' }}>{t('settings.bookingLabels')}</label>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useState, useCallback, useRef, useEffect, useMemo } from 'react'
|
||||
import { useSettingsStore } from '../store/settingsStore'
|
||||
import { useTripStore } from '../store/tripStore'
|
||||
import { calculateRouteWithLegs } from '../components/Map/RouteCalculator'
|
||||
import type { TripStoreState } from '../store/tripStore'
|
||||
@@ -9,14 +8,13 @@ const TRANSPORT_TYPES = ['flight', 'train', 'bus', 'car', 'cruise']
|
||||
|
||||
/**
|
||||
* Manages route calculation state for a selected day. Extracts geo-coded waypoints from
|
||||
* day assignments, draws a straight-line route, and optionally fetches per-segment
|
||||
* driving/walking durations via OSRM. Aborts in-flight requests when the day changes.
|
||||
* day assignments, draws a straight-line route immediately, then upgrades it to real OSRM
|
||||
* road geometry with per-segment durations. Aborts in-flight requests when the day changes.
|
||||
*/
|
||||
export function useRouteCalculation(tripStore: TripStoreState, selectedDayId: number | null, enabled: boolean = true, profile: 'driving' | 'walking' | 'cycling' = 'driving') {
|
||||
const [route, setRoute] = useState<[number, number][][] | null>(null)
|
||||
const [routeInfo, setRouteInfo] = useState<RouteResult | null>(null)
|
||||
const [routeSegments, setRouteSegments] = useState<RouteSegment[]>([])
|
||||
const routeCalcEnabled = useSettingsStore((s) => s.settings.route_calculation) !== false
|
||||
const routeAbortRef = useRef<AbortController | null>(null)
|
||||
const reservationsForSignature = useTripStore((s) => s.reservations)
|
||||
|
||||
@@ -88,9 +86,8 @@ export function useRouteCalculation(tripStore: TripStoreState, selectedDayId: nu
|
||||
if (runs.length === 0) { setRoute(null); setRouteSegments([]); return }
|
||||
|
||||
// Draw straight lines immediately for snappiness, then upgrade to the real
|
||||
// OSRM road geometry. If route calc is disabled, keep the straight lines.
|
||||
// OSRM road geometry.
|
||||
setRoute(straightLines())
|
||||
if (!routeCalcEnabled) { setRouteSegments([]); return }
|
||||
|
||||
const controller = new AbortController()
|
||||
routeAbortRef.current = controller
|
||||
@@ -113,7 +110,7 @@ export function useRouteCalculation(tripStore: TripStoreState, selectedDayId: nu
|
||||
// Aborted (day changed) — newer call owns the state. Anything else: keep straight lines.
|
||||
if (!(err instanceof Error) || err.name !== 'AbortError') setRouteSegments([])
|
||||
}
|
||||
}, [routeCalcEnabled, enabled, profile])
|
||||
}, [enabled, profile])
|
||||
|
||||
// Stable signature for transport reservations on the selected day — changes when a transport
|
||||
// is added, removed, or repositioned, ensuring route recalc fires even on transport-only reorders.
|
||||
|
||||
@@ -200,7 +200,6 @@ const ar: Record<string, string | { name: string; category: string }[]> = {
|
||||
'settings.language': 'اللغة',
|
||||
'settings.temperature': 'وحدة الحرارة',
|
||||
'settings.timeFormat': 'تنسيق الوقت',
|
||||
'settings.routeCalculation': 'حساب المسار',
|
||||
'settings.blurBookingCodes': 'إخفاء رموز الحجز',
|
||||
'settings.notifications': 'الإشعارات',
|
||||
'settings.notifyTripInvite': 'دعوات الرحلات',
|
||||
|
||||
@@ -195,7 +195,6 @@ const br: Record<string, string | { name: string; category: string }[]> = {
|
||||
'settings.language': 'Idioma',
|
||||
'settings.temperature': 'Unidade de temperatura',
|
||||
'settings.timeFormat': 'Formato de hora',
|
||||
'settings.routeCalculation': 'Cálculo de rota',
|
||||
'settings.blurBookingCodes': 'Ocultar códigos de reserva',
|
||||
'settings.notifications': 'Notificações',
|
||||
'settings.notifyTripInvite': 'Convites de viagem',
|
||||
|
||||
@@ -196,7 +196,6 @@ const cs: Record<string, string | { name: string; category: string }[]> = {
|
||||
'settings.language': 'Jazyk',
|
||||
'settings.temperature': 'Jednotky teploty',
|
||||
'settings.timeFormat': 'Formát času',
|
||||
'settings.routeCalculation': 'Výpočet trasy',
|
||||
'settings.blurBookingCodes': 'Skrýt rezervační kódy',
|
||||
'settings.notifications': 'Oznámení',
|
||||
'settings.notifyTripInvite': 'Pozvánky na cesty',
|
||||
|
||||
@@ -198,7 +198,6 @@ const de: Record<string, string | { name: string; category: string }[]> = {
|
||||
'settings.language': 'Sprache',
|
||||
'settings.temperature': 'Temperatureinheit',
|
||||
'settings.timeFormat': 'Zeitformat',
|
||||
'settings.routeCalculation': 'Routenberechnung',
|
||||
'settings.bookingLabels': 'Orts-Labels auf Buchungsrouten',
|
||||
'settings.bookingLabelsHint': 'Zeigt Bahnhofs-/Flughafennamen auf der Karte. Wenn aus, wird nur das Icon angezeigt.',
|
||||
'settings.blurBookingCodes': 'Buchungscodes verbergen',
|
||||
|
||||
@@ -212,7 +212,6 @@ const en: Record<string, string | { name: string; category: string }[]> = {
|
||||
'settings.language': 'Language',
|
||||
'settings.temperature': 'Temperature Unit',
|
||||
'settings.timeFormat': 'Time Format',
|
||||
'settings.routeCalculation': 'Route Calculation',
|
||||
'settings.bookingLabels': 'Booking route labels',
|
||||
'settings.bookingLabelsHint': 'Show station / airport names on the map. When off, only the icon is shown.',
|
||||
'settings.blurBookingCodes': 'Blur Booking Codes',
|
||||
|
||||
@@ -196,7 +196,6 @@ const es: Record<string, string> = {
|
||||
'settings.language': 'Idioma',
|
||||
'settings.temperature': 'Unidad de temperatura',
|
||||
'settings.timeFormat': 'Formato de hora',
|
||||
'settings.routeCalculation': 'Cálculo de ruta',
|
||||
'settings.blurBookingCodes': 'Difuminar códigos de reserva',
|
||||
'settings.notifications': 'Notificaciones',
|
||||
'settings.notifyTripInvite': 'Invitaciones de viaje',
|
||||
|
||||
@@ -195,7 +195,6 @@ const fr: Record<string, string> = {
|
||||
'settings.language': 'Langue',
|
||||
'settings.temperature': 'Unité de température',
|
||||
'settings.timeFormat': 'Format de l\'heure',
|
||||
'settings.routeCalculation': 'Calcul d\'itinéraire',
|
||||
'settings.blurBookingCodes': 'Masquer les codes de réservation',
|
||||
'settings.notifications': 'Notifications',
|
||||
'settings.notifyTripInvite': 'Invitations de voyage',
|
||||
|
||||
@@ -195,7 +195,6 @@ const hu: Record<string, string | { name: string; category: string }[]> = {
|
||||
'settings.language': 'Nyelv',
|
||||
'settings.temperature': 'Hőmérséklet egység',
|
||||
'settings.timeFormat': 'Időformátum',
|
||||
'settings.routeCalculation': 'Útvonalszámítás',
|
||||
'settings.blurBookingCodes': 'Foglalási kódok elrejtése',
|
||||
'settings.notifications': 'Értesítések',
|
||||
'settings.notifyTripInvite': 'Utazási meghívók',
|
||||
|
||||
@@ -198,7 +198,6 @@ const id: Record<string, string | { name: string; category: string }[]> = {
|
||||
'settings.language': 'Bahasa',
|
||||
'settings.temperature': 'Satuan Suhu',
|
||||
'settings.timeFormat': 'Format Waktu',
|
||||
'settings.routeCalculation': 'Perhitungan Rute',
|
||||
'settings.blurBookingCodes': 'Sembunyikan Kode Pemesanan',
|
||||
'settings.notifications': 'Notifikasi',
|
||||
'settings.notifyTripInvite': 'Undangan perjalanan',
|
||||
|
||||
@@ -195,7 +195,6 @@ const it: Record<string, string | { name: string; category: string }[]> = {
|
||||
'settings.language': 'Lingua',
|
||||
'settings.temperature': 'Unità di Temperatura',
|
||||
'settings.timeFormat': 'Formato Ora',
|
||||
'settings.routeCalculation': 'Calcolo Percorso',
|
||||
'settings.blurBookingCodes': 'Nascondi codici di prenotazione',
|
||||
'settings.notifications': 'Notifiche',
|
||||
'settings.notifyTripInvite': 'Inviti di viaggio',
|
||||
|
||||
@@ -212,7 +212,6 @@ const ja: Record<string, string | { name: string; category: string }[]> = {
|
||||
'settings.language': '言語',
|
||||
'settings.temperature': '温度単位',
|
||||
'settings.timeFormat': '時刻形式',
|
||||
'settings.routeCalculation': '経路計算',
|
||||
'settings.bookingLabels': '予約ルートのラベル',
|
||||
'settings.bookingLabelsHint': '地図に駅・空港名を表示。オフ時はアイコンのみ。',
|
||||
'settings.blurBookingCodes': '予約コードをぼかす',
|
||||
|
||||
@@ -212,7 +212,6 @@ const ko: Record<string, string | { name: string; category: string }[]> = {
|
||||
'settings.language': '언어',
|
||||
'settings.temperature': '온도 단위',
|
||||
'settings.timeFormat': '시간 형식',
|
||||
'settings.routeCalculation': '경로 계산',
|
||||
'settings.bookingLabels': '예약 경로 레이블',
|
||||
'settings.bookingLabelsHint': '지도에 역 / 공항 이름을 표시합니다. 끄면 아이콘만 표시됩니다.',
|
||||
'settings.blurBookingCodes': '예약 코드 흐리게',
|
||||
|
||||
@@ -195,7 +195,6 @@ const nl: Record<string, string> = {
|
||||
'settings.language': 'Taal',
|
||||
'settings.temperature': 'Temperatuureenheid',
|
||||
'settings.timeFormat': 'Tijdnotatie',
|
||||
'settings.routeCalculation': 'Routeberekening',
|
||||
'settings.blurBookingCodes': 'Boekingscodes vervagen',
|
||||
'settings.notifications': 'Meldingen',
|
||||
'settings.notifyTripInvite': 'Reisuitnodigingen',
|
||||
|
||||
@@ -178,7 +178,6 @@ const pl: Record<string, string | { name: string; category: string }[]> = {
|
||||
'settings.language': 'Język',
|
||||
'settings.temperature': 'Jednostka temperatury',
|
||||
'settings.timeFormat': 'Format czasu',
|
||||
'settings.routeCalculation': 'Obliczanie trasy',
|
||||
'settings.blurBookingCodes': 'Rozmyj kody rezerwacji',
|
||||
'settings.notifications': 'Powiadomienia',
|
||||
'settings.notifyTripInvite': 'Zaproszenia do podróży',
|
||||
|
||||
@@ -195,7 +195,6 @@ const ru: Record<string, string> = {
|
||||
'settings.language': 'Язык',
|
||||
'settings.temperature': 'Единица температуры',
|
||||
'settings.timeFormat': 'Формат времени',
|
||||
'settings.routeCalculation': 'Расчёт маршрута',
|
||||
'settings.blurBookingCodes': 'Скрыть коды бронирования',
|
||||
'settings.notifications': 'Уведомления',
|
||||
'settings.notifyTripInvite': 'Приглашения в поездку',
|
||||
|
||||
@@ -212,7 +212,6 @@ const tr: Record<string, string | { name: string; category: string }[]> = {
|
||||
'settings.language': 'Dil',
|
||||
'settings.temperature': 'Sıcaklık birimi',
|
||||
'settings.timeFormat': 'Saat biçimi',
|
||||
'settings.routeCalculation': 'Route Calculation',
|
||||
'settings.bookingLabels': 'Booking route labels',
|
||||
'settings.bookingLabelsHint': 'Show station / airport names on the map. When off, only the icon is shown.',
|
||||
'settings.blurBookingCodes': 'Blur Booking Codes',
|
||||
|
||||
@@ -209,7 +209,6 @@ const uk: Record<string, string> = {
|
||||
'settings.language': 'Мова',
|
||||
'settings.temperature': 'Одиниця температури',
|
||||
'settings.timeFormat': 'Формат часу',
|
||||
'settings.routeCalculation': 'Розрахунок маршруту',
|
||||
'settings.blurBookingCodes': 'Приховати коди бронювання',
|
||||
'settings.notifications': 'Сповіщення',
|
||||
'settings.notifyTripInvite': 'Запрошення до поїздки',
|
||||
|
||||
@@ -195,7 +195,6 @@ const zh: Record<string, string> = {
|
||||
'settings.language': '语言',
|
||||
'settings.temperature': '温度单位',
|
||||
'settings.timeFormat': '时间格式',
|
||||
'settings.routeCalculation': '路线计算',
|
||||
'settings.blurBookingCodes': '模糊预订代码',
|
||||
'settings.notifications': '通知',
|
||||
'settings.notifyTripInvite': '旅行邀请',
|
||||
|
||||
@@ -195,7 +195,6 @@ const zhTw: Record<string, string> = {
|
||||
'settings.language': '語言',
|
||||
'settings.temperature': '溫度單位',
|
||||
'settings.timeFormat': '時間格式',
|
||||
'settings.routeCalculation': '路線計算',
|
||||
'settings.blurBookingCodes': '模糊預訂程式碼',
|
||||
'settings.notifications': '通知',
|
||||
'settings.notifyTripInvite': '旅行邀請',
|
||||
|
||||
@@ -857,7 +857,6 @@ describe('DashboardPage', () => {
|
||||
temperature_unit: 'fahrenheit',
|
||||
time_format: '12h',
|
||||
show_place_description: false,
|
||||
route_calculation: false,
|
||||
blur_booking_codes: false,
|
||||
dashboard_currency: 'on',
|
||||
dashboard_timezone: 'on',
|
||||
|
||||
@@ -830,7 +830,7 @@ export default function TripPlannerPage(): React.ReactElement | null {
|
||||
hasInspector={!!selectedPlace}
|
||||
hasDayDetail={!!showDayDetail && !selectedPlace}
|
||||
reservations={reservations}
|
||||
showReservationStats={settings.route_calculation !== false}
|
||||
showReservationStats={true}
|
||||
visibleConnectionIds={visibleConnections}
|
||||
onReservationClick={(rid) => {
|
||||
const r = reservations.find(x => x.id === rid)
|
||||
|
||||
@@ -215,7 +215,6 @@ export interface Settings {
|
||||
temperature_unit: string
|
||||
time_format: string
|
||||
show_place_description: boolean
|
||||
route_calculation?: boolean
|
||||
blur_booking_codes?: boolean
|
||||
map_booking_labels?: boolean
|
||||
map_provider?: 'leaflet' | 'mapbox-gl'
|
||||
|
||||
@@ -258,7 +258,6 @@ export function buildSettings(overrides: Partial<Settings> = {}): Settings {
|
||||
temperature_unit: 'fahrenheit',
|
||||
time_format: '12h',
|
||||
show_place_description: false,
|
||||
route_calculation: false,
|
||||
blur_booking_codes: false,
|
||||
...overrides,
|
||||
};
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { renderHook, act } from '@testing-library/react';
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { useRouteCalculation } from '../../../src/hooks/useRouteCalculation';
|
||||
import { useSettingsStore } from '../../../src/store/settingsStore';
|
||||
import { useTripStore } from '../../../src/store/tripStore';
|
||||
import { buildAssignment, buildPlace } from '../../helpers/factories';
|
||||
import type { TripStoreState } from '../../../src/store/tripStore';
|
||||
@@ -47,8 +46,6 @@ const MOCK_ROUTE_WITH_LEGS = {
|
||||
describe('useRouteCalculation', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
// Default: route_calculation disabled
|
||||
useSettingsStore.setState({ settings: { route_calculation: false } as any });
|
||||
// Reset trip store assignments so each test starts clean
|
||||
useTripStore.setState({ assignments: {} } as any);
|
||||
(calculateRouteWithLegs as ReturnType<typeof vi.fn>).mockResolvedValue(MOCK_ROUTE_WITH_LEGS);
|
||||
@@ -93,9 +90,7 @@ describe('useRouteCalculation', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('FE-HOOK-ROUTE-004: with route_calculation enabled, calls calculateRouteWithLegs', async () => {
|
||||
useSettingsStore.setState({ settings: { route_calculation: true } as any });
|
||||
|
||||
it('FE-HOOK-ROUTE-004: calls calculateRouteWithLegs and exposes the returned segments', async () => {
|
||||
const p1 = buildPlace({ lat: 48.8566, lng: 2.3522 });
|
||||
const p2 = buildPlace({ lat: 51.5074, lng: -0.1278 });
|
||||
const a1 = buildAssignment({ day_id: 5, order_index: 0, place: p1 });
|
||||
@@ -112,28 +107,7 @@ describe('useRouteCalculation', () => {
|
||||
expect(result.current.routeSegments).toEqual(MOCK_SEGMENTS);
|
||||
});
|
||||
|
||||
it('FE-HOOK-ROUTE-005: with route_calculation disabled, does not call calculateRouteWithLegs', async () => {
|
||||
useSettingsStore.setState({ settings: { route_calculation: false } as any });
|
||||
|
||||
const p1 = buildPlace({ lat: 48.8566, lng: 2.3522 });
|
||||
const p2 = buildPlace({ lat: 51.5074, lng: -0.1278 });
|
||||
const a1 = buildAssignment({ day_id: 5, order_index: 0, place: p1 });
|
||||
const a2 = buildAssignment({ day_id: 5, order_index: 1, place: p2 });
|
||||
const store = buildMockStore({ '5': [a1, a2] });
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useRouteCalculation(store as TripStoreState, 5)
|
||||
);
|
||||
|
||||
await act(async () => {});
|
||||
|
||||
expect(calculateRouteWithLegs).not.toHaveBeenCalled();
|
||||
expect(result.current.routeSegments).toEqual([]);
|
||||
});
|
||||
|
||||
it('FE-HOOK-ROUTE-006: assignments are sorted by order_index before extracting waypoints', async () => {
|
||||
useSettingsStore.setState({ settings: { route_calculation: true } as any });
|
||||
|
||||
const p1 = buildPlace({ lat: 10, lng: 10 });
|
||||
const p2 = buildPlace({ lat: 20, lng: 20 });
|
||||
// order_index 1 comes before 0 in the array, but should be sorted
|
||||
@@ -170,7 +144,6 @@ describe('useRouteCalculation', () => {
|
||||
});
|
||||
|
||||
it('FE-HOOK-ROUTE-008: AbortController.abort() is called when selectedDayId changes', async () => {
|
||||
useSettingsStore.setState({ settings: { route_calculation: true } as any });
|
||||
|
||||
// Make calculateRouteWithLegs resolve slowly
|
||||
let resolveSegments!: (val: typeof MOCK_ROUTE_WITH_LEGS) => void;
|
||||
@@ -209,7 +182,6 @@ describe('useRouteCalculation', () => {
|
||||
});
|
||||
|
||||
it('FE-HOOK-ROUTE-009: AbortError from calculateSegments does not set routeSegments to []', async () => {
|
||||
useSettingsStore.setState({ settings: { route_calculation: true } as any });
|
||||
|
||||
const abortError = new Error('Aborted');
|
||||
abortError.name = 'AbortError';
|
||||
@@ -231,7 +203,6 @@ describe('useRouteCalculation', () => {
|
||||
});
|
||||
|
||||
it('FE-HOOK-ROUTE-010: non-AbortError from calculateSegments sets routeSegments to []', async () => {
|
||||
useSettingsStore.setState({ settings: { route_calculation: true } as any });
|
||||
|
||||
(calculateRouteWithLegs as ReturnType<typeof vi.fn>).mockRejectedValueOnce(new Error('Network error'));
|
||||
|
||||
@@ -282,7 +253,6 @@ describe('useRouteCalculation', () => {
|
||||
});
|
||||
|
||||
it('FE-HOOK-ROUTE-013: route recalculates when assignments change via store update', async () => {
|
||||
useSettingsStore.setState({ settings: { route_calculation: true } as any });
|
||||
|
||||
const p1 = buildPlace({ lat: 10, lng: 10 });
|
||||
const p2 = buildPlace({ lat: 20, lng: 20 });
|
||||
|
||||
Reference in New Issue
Block a user