diff --git a/client/public/images/portals/check24.png b/client/public/images/portals/check24.png new file mode 100644 index 000000000..ef931c5da Binary files /dev/null and b/client/public/images/portals/check24.png differ diff --git a/client/public/images/portals/pincamp.png b/client/public/images/portals/pincamp.png new file mode 100644 index 000000000..d878e863a Binary files /dev/null and b/client/public/images/portals/pincamp.png differ diff --git a/client/public/images/portals/pitchup.ico b/client/public/images/portals/pitchup.ico new file mode 100644 index 000000000..24b7881cd Binary files /dev/null and b/client/public/images/portals/pitchup.ico differ diff --git a/client/public/images/portals/trivago.png b/client/public/images/portals/trivago.png new file mode 100644 index 000000000..1eb2abb50 Binary files /dev/null and b/client/public/images/portals/trivago.png differ diff --git a/client/src/App.tsx b/client/src/App.tsx index 509a943be..ceadffc3d 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -24,6 +24,7 @@ import { tripRepo } from './repo/tripRepo' import { readStartDestination, tripStartPath, DEFAULT_START_PAGE, DEFAULT_START_TRIP_TAB, SETTINGS_WAIT_MS, START_DESTINATION_ROUTE } from './utils/startDestination' import { usePermissionsStore, PermissionLevel } from './store/permissionsStore' import { useInAppNotificationListener } from './hooks/useInAppNotificationListener.ts' +import { useRoadtripPreferencesSync } from './hooks/useRoadtripPreferencesSync' import { registerSyncTriggers, unregisterSyncTriggers } from './sync/syncTriggers' import OfflineBanner from './components/Layout/OfflineBanner' import { SystemNoticeHost } from './components/SystemNotices/SystemNoticeHost.js' @@ -364,6 +365,7 @@ export default function App() { const { settings } = useSettingsStore() useInAppNotificationListener() + useRoadtripPreferencesSync() useEffect(() => { if (isAuthenticated) { diff --git a/client/src/api/assignmentEndDay.ts b/client/src/api/assignmentEndDay.ts new file mode 100644 index 000000000..ba146d4f7 --- /dev/null +++ b/client/src/api/assignmentEndDay.ts @@ -0,0 +1,7 @@ +import { assignmentSchema, type AssignmentEndDayRequest } from '@trek/shared' +import { apiClient } from './client' + +export async function saveAssignmentEndDay(tripId: number | string, id: number, body: AssignmentEndDayRequest) { + const saved = await apiClient.put(`/trips/${tripId}/assignments/${id}/end-day`, body) + return assignmentSchema.parse(saved.data.assignment) +} diff --git a/client/src/api/wsEventPolicy.ts b/client/src/api/wsEventPolicy.ts index 46c22033a..1ad8c95ba 100644 --- a/client/src/api/wsEventPolicy.ts +++ b/client/src/api/wsEventPolicy.ts @@ -35,6 +35,7 @@ export const HANDLED_OUTSIDE_TRIP_STORE = [ // second copy of the same list to keep in step. 'roadtripVia:changed', 'roadtripTrack:changed', + 'roadtripPreferences:changed', // In-app notifications — hooks/useInAppNotificationListener 'notification:new', 'notification:updated', diff --git a/client/src/components/Admin/DefaultUserSettingsTab.tsx b/client/src/components/Admin/DefaultUserSettingsTab.tsx index d89fa5964..b789f2d1a 100644 --- a/client/src/components/Admin/DefaultUserSettingsTab.tsx +++ b/client/src/components/Admin/DefaultUserSettingsTab.tsx @@ -19,6 +19,7 @@ import { type GlMapProvider, } from '../Map/glProviders' import { useAuthStore } from '../../store/authStore' +import RoutingInstanceFields, { type RoutingDefaults } from './RoutingInstanceFields' const MAP_PRESETS = [ { name: 'OpenStreetMap', url: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png' }, @@ -34,7 +35,7 @@ const MAP_PRESETS = [ { name: 'Stadia Smooth', url: 'https://tiles.stadiamaps.com/tiles/alidade_smooth/{z}/{x}/{y}{r}.png' }, ] -type Defaults = { +type Defaults = RoutingDefaults & { temperature_unit?: string distance_unit?: DistanceUnit dark_mode?: string | boolean @@ -43,8 +44,6 @@ type Defaults = { blur_booking_codes?: boolean map_tile_url?: string carto_api_key?: string - routing_base_url?: string - valhalla_base_url?: string map_provider?: string mapbox_access_token?: string mapbox_style?: string @@ -121,8 +120,6 @@ export default function DefaultUserSettingsTab(): React.ReactElement { const managed = useAuthStore((s) => s.managed) const [mapboxToken, setMapboxToken] = useState('') const [cartoKey, setCartoKey] = useState('') - const [routingBase, setRoutingBase] = useState('') - const [valhallaBase, setValhallaBase] = useState('') const [mapboxStyle, setMapboxStyle] = useState('') useEffect(() => { @@ -132,8 +129,6 @@ export default function DefaultUserSettingsTab(): React.ReactElement { setMapTileUrl(normalizeTileUrl(data.map_tile_url || '')) setMapboxToken(data.mapbox_access_token || '') setCartoKey(data.carto_api_key || '') - setRoutingBase(data.routing_base_url || '') - setValhallaBase(data.valhalla_base_url || '') setMapboxStyle(provider === 'leaflet' ? (data.mapbox_style || '') : styleForProvider(provider, provider === 'maplibre-gl' ? data.maplibre_style : data.mapbox_style)) setLoaded(true) }).catch(() => setLoaded(true)) @@ -156,8 +151,6 @@ export default function DefaultUserSettingsTab(): React.ReactElement { if (key === 'map_tile_url') setMapTileUrl('') if (key === 'mapbox_access_token') setMapboxToken('') if (key === 'carto_api_key') setCartoKey('') - if (key === 'routing_base_url') setRoutingBase('') - if (key === 'valhalla_base_url') setValhallaBase('') if (key === 'mapbox_style' || key === 'maplibre_style') { const provider = normalizeProvider(defaults.map_provider) setMapboxStyle(provider === 'leaflet' ? '' : defaultStyleForProvider(provider)) @@ -387,53 +380,10 @@ export default function DefaultUserSettingsTab(): React.ReactElement {
{t('admin.defaultSettings.cartoKeyHint')}
)} - {/* Instance configuration, not a taste, which is why it sits here rather - than in a user's own Map settings. The origin has to appear in the CSP - `connect-src` the server emits at boot, and that list is built from - this default — a value on a personal settings row is read by the route - calculator and then refused by the browser, so the whole app stops - routing with no error it could report. The write route refuses one - from a non-admin for the same reason. */} {!managed && ( -{t('settings.routingBaseHint')}
-{t('settings.valhallaBaseHint')}
-{t(router.hint)}
+