diff --git a/client/src/components/Roadtrip/useRoadtripRoutes.ts b/client/src/components/Roadtrip/useRoadtripRoutes.ts index 928e96596..62dcd5d0f 100644 --- a/client/src/components/Roadtrip/useRoadtripRoutes.ts +++ b/client/src/components/Roadtrip/useRoadtripRoutes.ts @@ -332,6 +332,20 @@ export function useRoadtripRoutes( if (s) daySnaps[stopKey(stop)] = s }) } + // What was asked for against what the road turned out to be. Only + // the second engine reports it, and only when it answered: an OSRM + // fallback leaves `avoidance` absent, which is the honest reading + // of "the weighting never happened". Collected here because the + // rail plans in the browser and never sees the server's own copy + // of this field — without it the "not honoured" badge could not + // appear at all, and a motorway-free drive that is not one read as + // if the setting had held. + if (r.avoidance) { + const missed = r.avoidance.asked.filter(cls => !r.avoidance!.achieved.includes(cls)) + if (missed.length) { + collectedMisses[day.dayId] = [...new Set([...(collectedMisses[day.dayId] ?? []), ...missed])] + } + } Object.assign(dayLegs, foldRouteRun(run, stopAt, r, mode)) return } catch (err) { @@ -443,6 +457,8 @@ export function useRoadtripRoutes( return out }, [chains, plan, quietDays, window, legsByDay, seamLegs, viasByDay, connectDays]) const seamKey = seams.map(s => `${legKey(s.from, s.to)}#${seamShape(s.from, viasByDay)}`).join(';') + /** When the last seam request went out, across every run of the effect below. */ + const lastSeamRequestAt = useRef(0) useEffect(() => { if (!seams.length) return @@ -450,6 +466,20 @@ export function useRoadtripRoutes( void (async () => { for (const seam of seams) { if (controller.signal.aborted) return + // Paced before the request, against a clock that outlives this effect. + // + // Waiting AFTER one instead spaced nothing: storing a seam changes + // seamLegs, which is a dependency of the seams memo, which shortens + // seamKey, which is this effect's first dependency — so React tore the + // effect down mid-wait, the cleanup aborted the sleep, and the next run + // fired straight away. Thirteen seams went out at round-trip speed, the + // shared routing hosts answered the tail with 429, and a refused seam + // writes no state, so nothing changed to make the effect try again: the + // map drew the trip in pieces and the totals came back short. + const since = performance.now() - lastSeamRequestAt.current + if (since < REQUEST_SPACING_MS) await sleep(REQUEST_SPACING_MS - since, controller.signal) + if (controller.signal.aborted) return + lastSeamRequestAt.current = performance.now() const mode = resolveLegMode( { isPlace: true, leg_transport_mode: seam.from.legMode }, { isPlace: true, incoming_leg_transport_mode: seam.to.incomingLegMode }, @@ -496,7 +526,6 @@ export function useRoadtripRoutes( // invented. Retried on the next run of this effect. if (controller.signal.aborted) return } - await sleep(REQUEST_SPACING_MS, controller.signal) } })() return () => controller.abort() diff --git a/client/src/pages/tripPlanner/useTripPlanner.roadtrip.test.tsx b/client/src/pages/tripPlanner/useTripPlanner.roadtrip.test.tsx index 4947af668..833343af6 100644 --- a/client/src/pages/tripPlanner/useTripPlanner.roadtrip.test.tsx +++ b/client/src/pages/tripPlanner/useTripPlanner.roadtrip.test.tsx @@ -963,7 +963,7 @@ describe('useTripPlanner road trip: other ways of driving a leg', () => { expect(rt.alt.close).not.toHaveBeenCalled() }) - it('FE-TP-ROAD-040: another road is stored as a via where it differs most, not as a polyline', async () => { + it('FE-TP-ROAD-040: another road replaces the leg it reshapes, as a via rather than a polyline', async () => { routedDay() rt.alt.open = { dayId: 5, index: 0, loading: false, error: false, @@ -973,7 +973,11 @@ describe('useTripPlanner road trip: other ways of driving a leg', () => { await act(async () => { await result.current.chooseRouteAlternative(0) }) - expect(rt.vias.add).toHaveBeenCalledWith(5, 0, 53.1, 11.9) + // Replacing, not appending: the alternatives were computed for the two bare + // endpoints, so a leg that still carries its old via routes somewhere the + // preview never drew. + expect(rt.vias.addMany).toHaveBeenCalledWith(5, [{ after_order_index: 0, lat: 53.1, lng: 11.9 }], [0]) + expect(rt.vias.add).not.toHaveBeenCalled() expect(rt.alt.close).toHaveBeenCalled() }) @@ -983,7 +987,7 @@ describe('useTripPlanner road trip: other ways of driving a leg', () => { dayId: 5, index: 0, loading: false, error: false, routes: [{ divergence: { lat: 53.1, lng: 11.9 } }], } - rt.vias.add.mockRejectedValue(new Error('rejected')) + rt.vias.addMany.mockRejectedValue(new Error('rejected')) const { result } = await renderRoadtrip() rt.alt.close.mockClear() diff --git a/client/src/pages/tripPlanner/useTripPlanner.ts b/client/src/pages/tripPlanner/useTripPlanner.ts index 0ec888c56..fd8c498d5 100644 --- a/client/src/pages/tripPlanner/useTripPlanner.ts +++ b/client/src/pages/tripPlanner/useTripPlanner.ts @@ -128,7 +128,23 @@ export function useTripPlanner() { // drive-first reading of the same trip. Per trip and per session, like the tab choice: // someone planning a road trip stays in it across reloads without it leaking into // their next, non-driving trip. - const [roadtripMode, setRoadtripMode] = useState(() => sessionStorage.getItem(`trip-roadtrip-${tripId}`) === '1') + const [storedRoadtripMode, setRoadtripMode] = useState(() => sessionStorage.getItem(`trip-roadtrip-${tripId}`) === '1') + // Declared here rather than with the other layout state further down, because + // road-trip mode is decided on it and the assignment and place lists below are + // decided on that. One subscriber for the whole hook. + const [isMobile, setIsMobile] = useState(() => window.innerWidth < 768) + useEffect(() => { + const mq = window.matchMedia('(max-width: 767px)') + const handler = (e: MediaQueryListEvent) => setIsMobile(e.matches) + mq.addEventListener('change', handler) + return () => mq.removeEventListener('change', handler) + }, []) + // The phone shell has no road-trip surface at all: no rail, no drive lines, and + // no switch to turn the mode back off. Narrowing a desktop window past the + // phone breakpoint used to carry the flag across anyway, which took the trip + // overview pill away with nothing in its place and listed every booked night + // twice. The flag is kept, so widening the window again returns to the drive. + const roadtripMode = storedRoadtripMode && !isMobile // Two reasons a stop can be road-trip-only, and they are not the same reason. // // The switch is the traveller's: it hides the petrol stations and rest areas @@ -488,13 +504,6 @@ export function useTripPlanner() { }, [alwaysShowRoutesDefault]) const [mapTransportDetail, setMapTransportDetail] = useState(null) - const [isMobile, setIsMobile] = useState(() => window.innerWidth < 768) - useEffect(() => { - const mq = window.matchMedia('(max-width: 767px)') - const handler = (e: MediaQueryListEvent) => setIsMobile(e.matches) - mq.addEventListener('change', handler) - return () => mq.removeEventListener('change', handler) - }, []) // Layout is width-driven (isMobile); the drag bridge is pointer-driven (isTouch). // Conflating them is what left a tablet's places list undraggable-but-unscrollable (#1432). const isTouch = useIsTouch() @@ -618,7 +627,14 @@ export function useTripPlanner() { for (const boundary of dayBoundaries.boundaries) await dayBoundaries.save(boundary.day_number, null) } catch (err: unknown) { toast.error(err instanceof Error ? err.message : t('common.unknownError')) } } : undefined - const roadtripVias = useRoadtripVias(tripId, roadtripActive) + // Fed whenever the addon is on, not only while the mode is being looked at. + // The three handlers that re-anchor a day's vias — assign, remove, reorder — + // are reachable from the place inspector in both modes, and with an empty list + // they computed an empty correction and wrote nothing while the server kept + // every via pointing at a position that had moved. Deleting a stop with the + // switch off then left the detour on the wrong leg, for the traveller and for + // a collaborator who never turned the mode on at all. + const roadtripVias = useRoadtripVias(tripId, !!enabledAddons.roadtrip) const refuel = useRefuelSearch() const roadtripRoutes = useRoadtripRoutes( tripId, @@ -1382,8 +1398,21 @@ export function useTripPlanner() { const day = roadtripRoutes.days.find(d => d.dayId === open.dayId) const stop = day?.stops[open.index] if (!day || !stop) return + const ownerDayId = stop.ownerDayId ?? day.dayId + const legIndex = stop.ownerIndex ?? open.index try { - await roadtripVias.add(stop.ownerDayId ?? day.dayId, stop.ownerIndex ?? open.index, alt.divergence.lat, alt.divergence.lng) + // Replaces the leg rather than appending to it. The alternatives under the + // button were computed for the two bare endpoints — the road already being + // driven is offered separately as `current` — so a leg that already carries + // a via cannot produce the line the preview drew. Appending put the new + // point behind the old one and routed A, south to the old via, north to the + // new one, then B: a zigzag matching neither the preview nor the distance + // printed on it. Same write the direct branch above already uses. + await roadtripVias.addMany( + ownerDayId, + [{ after_order_index: legIndex, lat: alt.divergence.lat, lng: alt.divergence.lng }], + [legIndex], + ) routeAlternatives.close() } catch (err: unknown) { toast.error(err instanceof Error ? err.message : t('common.unknownError')) diff --git a/shared/src/i18n/ar/trip.ts b/shared/src/i18n/ar/trip.ts index 80fab2148..a39c7aaea 100644 --- a/shared/src/i18n/ar/trip.ts +++ b/shared/src/i18n/ar/trip.ts @@ -24,6 +24,7 @@ const trip: TranslationStrings = { 'trip.toast.placeDeleted': 'تم حذف المكان', 'trip.toast.selectDay': 'يرجى اختيار يوم أولًا', 'trip.toast.assignedToDay': 'تم إسناد المكان إلى اليوم', + 'trip.toast.loadError': 'تعذر تحميل الرحلة', 'trip.toast.reorderError': 'فشل إعادة الترتيب', 'trip.toast.reservationUpdated': 'تم تحديث الحجز', 'trip.toast.reservationAdded': 'تمت إضافة الحجز', diff --git a/shared/src/i18n/br/trip.ts b/shared/src/i18n/br/trip.ts index 48c6dbc4e..ea2cd0410 100644 --- a/shared/src/i18n/br/trip.ts +++ b/shared/src/i18n/br/trip.ts @@ -20,6 +20,7 @@ const trip: TranslationStrings = { 'trip.toast.placeDeleted': 'Lugar excluído', 'trip.toast.selectDay': 'Selecione um dia primeiro', 'trip.toast.assignedToDay': 'Lugar atribuído ao dia', + 'trip.toast.loadError': 'Não foi possível carregar a viagem', 'trip.toast.reorderError': 'Falha ao reordenar', 'trip.toast.reservationUpdated': 'Reserva atualizada', 'trip.toast.reservationAdded': 'Reserva adicionada', diff --git a/shared/src/i18n/ca/trip.ts b/shared/src/i18n/ca/trip.ts index caa24f908..e521ba59a 100644 --- a/shared/src/i18n/ca/trip.ts +++ b/shared/src/i18n/ca/trip.ts @@ -20,6 +20,7 @@ const trip: TranslationStrings = { 'trip.toast.placeDeleted': 'Lloc eliminat', 'trip.toast.selectDay': 'Selecciona un dia primer', 'trip.toast.assignedToDay': 'Lloc assignat al dia', + 'trip.toast.loadError': "No s'ha pogut carregar el viatge", 'trip.toast.reorderError': "No s'ha pogut reordenar", 'trip.toast.reservationUpdated': 'Reserva actualitzada', 'trip.toast.reservationAdded': 'Reserva afegida', diff --git a/shared/src/i18n/cs/trip.ts b/shared/src/i18n/cs/trip.ts index 5298554e3..a7131943a 100644 --- a/shared/src/i18n/cs/trip.ts +++ b/shared/src/i18n/cs/trip.ts @@ -24,6 +24,7 @@ const trip: TranslationStrings = { 'trip.toast.placeDeleted': 'Místo bylo smazáno', 'trip.toast.selectDay': 'Prosím nejdříve vyberte den', 'trip.toast.assignedToDay': 'Místo bylo přiřazeno ke dni', + 'trip.toast.loadError': 'Cestu se nepodařilo načíst', 'trip.toast.reorderError': 'Nepodařilo se změnit pořadí', 'trip.toast.reservationUpdated': 'Rezervace aktualizována', 'trip.toast.reservationAdded': 'Rezervace přidána', diff --git a/shared/src/i18n/de/trip.ts b/shared/src/i18n/de/trip.ts index f49779719..6c851bf1e 100644 --- a/shared/src/i18n/de/trip.ts +++ b/shared/src/i18n/de/trip.ts @@ -24,6 +24,7 @@ const trip: TranslationStrings = { 'trip.toast.placeDeleted': 'Ort gelöscht', 'trip.toast.selectDay': 'Bitte wähle zuerst einen Tag aus', 'trip.toast.assignedToDay': 'Ort wurde dem Tag zugewiesen', + 'trip.toast.loadError': 'Reise konnte nicht geladen werden', 'trip.toast.reorderError': 'Fehler beim Sortieren', 'trip.toast.reservationUpdated': 'Reservierung aktualisiert', 'trip.toast.reservationAdded': 'Reservierung hinzugefügt', diff --git a/shared/src/i18n/en/trip.ts b/shared/src/i18n/en/trip.ts index 1c63d19c4..de458bbf4 100644 --- a/shared/src/i18n/en/trip.ts +++ b/shared/src/i18n/en/trip.ts @@ -24,6 +24,7 @@ const trip: TranslationStrings = { 'trip.toast.placeDeleted': 'Place deleted', 'trip.toast.selectDay': 'Please select a day first', 'trip.toast.assignedToDay': 'Place assigned to day', + 'trip.toast.loadError': 'Could not load the trip', 'trip.toast.reorderError': 'Failed to reorder', 'trip.toast.reservationUpdated': 'Reservation updated', 'trip.toast.reservationAdded': 'Reservation added', diff --git a/shared/src/i18n/es/trip.ts b/shared/src/i18n/es/trip.ts index 86ce081f0..6ae8c10bf 100644 --- a/shared/src/i18n/es/trip.ts +++ b/shared/src/i18n/es/trip.ts @@ -24,6 +24,7 @@ const trip: TranslationStrings = { 'trip.toast.placeDeleted': 'Lugar eliminado', 'trip.toast.selectDay': 'Selecciona primero un día', 'trip.toast.assignedToDay': 'Lugar asignado al día', + 'trip.toast.loadError': 'No se pudo cargar el viaje', 'trip.toast.reorderError': 'No se pudo reordenar', 'trip.toast.reservationUpdated': 'Reserva actualizada', 'trip.toast.reservationAdded': 'Reserva añadida', diff --git a/shared/src/i18n/fr/trip.ts b/shared/src/i18n/fr/trip.ts index 50aa864fe..f3e7d4c0b 100644 --- a/shared/src/i18n/fr/trip.ts +++ b/shared/src/i18n/fr/trip.ts @@ -24,6 +24,7 @@ const trip: TranslationStrings = { 'trip.toast.placeDeleted': 'Lieu supprimé', 'trip.toast.selectDay': "Veuillez d'abord sélectionner un jour", 'trip.toast.assignedToDay': 'Lieu attribué au planning', + 'trip.toast.loadError': 'Impossible de charger le voyage', 'trip.toast.reorderError': 'Échec de la réorganisation', 'trip.toast.reservationUpdated': 'Réservation mise à jour', 'trip.toast.reservationAdded': 'Réservation ajoutée', diff --git a/shared/src/i18n/gr/trip.ts b/shared/src/i18n/gr/trip.ts index 2a2f2956c..4c74bec7c 100644 --- a/shared/src/i18n/gr/trip.ts +++ b/shared/src/i18n/gr/trip.ts @@ -24,6 +24,7 @@ const trip: TranslationStrings = { 'trip.toast.placeDeleted': 'Το μέρος διαγράφηκε', 'trip.toast.selectDay': 'Παρακαλώ επιλέξτε πρώτα μια ημέρα', 'trip.toast.assignedToDay': 'Το μέρος ανατέθηκε στην ημέρα', + 'trip.toast.loadError': 'Δεν ήταν δυνατή η φόρτωση του ταξιδιού', 'trip.toast.reorderError': 'Η αναδιάταξη απέτυχε', 'trip.toast.reservationUpdated': 'Η κράτηση ενημερώθηκε', 'trip.toast.reservationAdded': 'Η κράτηση προστέθηκε', diff --git a/shared/src/i18n/hu/trip.ts b/shared/src/i18n/hu/trip.ts index 203e34c9a..fb7722a0a 100644 --- a/shared/src/i18n/hu/trip.ts +++ b/shared/src/i18n/hu/trip.ts @@ -20,6 +20,7 @@ const trip: TranslationStrings = { 'trip.toast.placeDeleted': 'Hely törölve', 'trip.toast.selectDay': 'Kérjük, először válassz egy napot', 'trip.toast.assignedToDay': 'Hely hozzárendelve a naphoz', + 'trip.toast.loadError': 'Az utazást nem sikerült betölteni', 'trip.toast.reorderError': 'Nem sikerült átrendezni', 'trip.toast.reservationUpdated': 'Foglalás frissítve', 'trip.toast.reservationAdded': 'Foglalás hozzáadva', diff --git a/shared/src/i18n/id/trip.ts b/shared/src/i18n/id/trip.ts index 0cccd756a..0bffcaeb5 100644 --- a/shared/src/i18n/id/trip.ts +++ b/shared/src/i18n/id/trip.ts @@ -24,6 +24,7 @@ const trip: TranslationStrings = { 'trip.toast.placeDeleted': 'Tempat dihapus', 'trip.toast.selectDay': 'Pilih hari terlebih dahulu', 'trip.toast.assignedToDay': 'Tempat ditambahkan ke hari', + 'trip.toast.loadError': 'Tidak dapat memuat perjalanan', 'trip.toast.reorderError': 'Gagal mengurutkan ulang', 'trip.toast.reservationUpdated': 'Reservasi diperbarui', 'trip.toast.reservationAdded': 'Reservasi ditambahkan', diff --git a/shared/src/i18n/it/trip.ts b/shared/src/i18n/it/trip.ts index a32af2a24..c387edb1e 100644 --- a/shared/src/i18n/it/trip.ts +++ b/shared/src/i18n/it/trip.ts @@ -20,6 +20,7 @@ const trip: TranslationStrings = { 'trip.toast.placeDeleted': 'Luogo eliminato', 'trip.toast.selectDay': 'Seleziona prima un giorno', 'trip.toast.assignedToDay': 'Luogo assegnato al giorno', + 'trip.toast.loadError': 'Impossibile caricare il viaggio', 'trip.toast.reorderError': 'Impossibile riordinare', 'trip.toast.reservationUpdated': 'Prenotazione aggiornata', 'trip.toast.reservationAdded': 'Prenotazione aggiunta', diff --git a/shared/src/i18n/ja/trip.ts b/shared/src/i18n/ja/trip.ts index 73334682c..c30584a26 100644 --- a/shared/src/i18n/ja/trip.ts +++ b/shared/src/i18n/ja/trip.ts @@ -24,6 +24,7 @@ const trip: TranslationStrings = { 'trip.toast.placeDeleted': '場所を削除しました', 'trip.toast.selectDay': 'まず日を選択してください', 'trip.toast.assignedToDay': '場所を日に割り当てました', + 'trip.toast.loadError': '旅行を読み込めませんでした', 'trip.toast.reorderError': '並び替えに失敗しました', 'trip.toast.reservationUpdated': '予約を更新しました', 'trip.toast.reservationAdded': '予約を追加しました', diff --git a/shared/src/i18n/ko/trip.ts b/shared/src/i18n/ko/trip.ts index e915ef9d3..e29db7c70 100644 --- a/shared/src/i18n/ko/trip.ts +++ b/shared/src/i18n/ko/trip.ts @@ -24,6 +24,7 @@ const trip: TranslationStrings = { 'trip.toast.placeDeleted': '장소가 삭제되었습니다', 'trip.toast.selectDay': '먼저 날을 선택하세요', 'trip.toast.assignedToDay': '장소가 날에 배정되었습니다', + 'trip.toast.loadError': '여행을 불러오지 못했습니다', 'trip.toast.reorderError': '순서 변경 실패', 'trip.toast.reservationUpdated': '예약이 업데이트되었습니다', 'trip.toast.reservationAdded': '예약이 추가되었습니다', diff --git a/shared/src/i18n/nl/trip.ts b/shared/src/i18n/nl/trip.ts index e661947f4..96268a1f6 100644 --- a/shared/src/i18n/nl/trip.ts +++ b/shared/src/i18n/nl/trip.ts @@ -24,6 +24,7 @@ const trip: TranslationStrings = { 'trip.toast.placeDeleted': 'Plaats verwijderd', 'trip.toast.selectDay': 'Selecteer eerst een dag', 'trip.toast.assignedToDay': 'Plaats toegewezen aan dag', + 'trip.toast.loadError': 'Kan de reis niet laden', 'trip.toast.reorderError': 'Herordenen mislukt', 'trip.toast.reservationUpdated': 'Reservering bijgewerkt', 'trip.toast.reservationAdded': 'Reservering toegevoegd', diff --git a/shared/src/i18n/pl/trip.ts b/shared/src/i18n/pl/trip.ts index 2d9d1b4af..3e7fe6093 100644 --- a/shared/src/i18n/pl/trip.ts +++ b/shared/src/i18n/pl/trip.ts @@ -20,6 +20,7 @@ const trip: TranslationStrings = { 'trip.toast.placeDeleted': 'Miejsce zostało usunięte', 'trip.toast.selectDay': 'Proszę najpierw wybrać dzień', 'trip.toast.assignedToDay': 'Miejsce przypisane do dnia', + 'trip.toast.loadError': 'Nie udało się wczytać podróży', 'trip.toast.reorderError': 'Nie udało się zmienić kolejności', 'trip.toast.reservationUpdated': 'Rezerwacja została zaktualizowana', 'trip.toast.reservationAdded': 'Rezerwacja została dodana', diff --git a/shared/src/i18n/ru/trip.ts b/shared/src/i18n/ru/trip.ts index 03ab8fa3e..d4af42394 100644 --- a/shared/src/i18n/ru/trip.ts +++ b/shared/src/i18n/ru/trip.ts @@ -24,6 +24,7 @@ const trip: TranslationStrings = { 'trip.toast.placeDeleted': 'Место удалено', 'trip.toast.selectDay': 'Сначала выберите день', 'trip.toast.assignedToDay': 'Место назначено на день', + 'trip.toast.loadError': 'Не удалось загрузить поездку', 'trip.toast.reorderError': 'Ошибка изменения порядка', 'trip.toast.reservationUpdated': 'Бронирование обновлено', 'trip.toast.reservationAdded': 'Бронирование добавлено', diff --git a/shared/src/i18n/sv/trip.ts b/shared/src/i18n/sv/trip.ts index 5698736a7..fdbe3a270 100644 --- a/shared/src/i18n/sv/trip.ts +++ b/shared/src/i18n/sv/trip.ts @@ -24,6 +24,7 @@ const trip: TranslationStrings = { 'trip.toast.placeDeleted': 'Plats raderad', 'trip.toast.selectDay': 'Vänligen välj en dag först', 'trip.toast.assignedToDay': 'Plats tilldelad till dag', + 'trip.toast.loadError': 'Det gick inte att ladda resan', 'trip.toast.reorderError': 'Misslyckades att ordna om', 'trip.toast.reservationUpdated': 'Reservation uppdaterad', 'trip.toast.reservationAdded': 'Reservation tillagd', diff --git a/shared/src/i18n/tr/trip.ts b/shared/src/i18n/tr/trip.ts index dea377b73..5ca806d1a 100644 --- a/shared/src/i18n/tr/trip.ts +++ b/shared/src/i18n/tr/trip.ts @@ -24,6 +24,7 @@ const trip: TranslationStrings = { 'trip.toast.placeDeleted': 'Yer silindi', 'trip.toast.selectDay': 'Lütfen önce bir gün seçin', 'trip.toast.assignedToDay': 'Yer güne atandı', + 'trip.toast.loadError': 'Gezi yüklenemedi', 'trip.toast.reorderError': 'Sıralama yapılamadı', 'trip.toast.reservationUpdated': 'Rezervasyon güncellendi', 'trip.toast.reservationAdded': 'Rezervasyon eklendi', diff --git a/shared/src/i18n/uk/trip.ts b/shared/src/i18n/uk/trip.ts index 26daa19a1..52056a901 100644 --- a/shared/src/i18n/uk/trip.ts +++ b/shared/src/i18n/uk/trip.ts @@ -24,6 +24,7 @@ const trip: TranslationStrings = { 'trip.toast.placeDeleted': 'Місце видалено', 'trip.toast.selectDay': 'Спочатку виберіть день', 'trip.toast.assignedToDay': 'Місце призначено на день', + 'trip.toast.loadError': 'Не вдалося завантажити подорож', 'trip.toast.reorderError': 'Помилка зміни порядку', 'trip.toast.reservationUpdated': 'Бронювання оновлено', 'trip.toast.reservationAdded': 'Бронювання додано', diff --git a/shared/src/i18n/vi/trip.ts b/shared/src/i18n/vi/trip.ts index f65b205b5..3d59efe1c 100644 --- a/shared/src/i18n/vi/trip.ts +++ b/shared/src/i18n/vi/trip.ts @@ -24,6 +24,7 @@ const trip: TranslationStrings = { 'trip.toast.placeDeleted': 'Địa điểm đã bị xóa', 'trip.toast.selectDay': 'Vui lòng chọn một ngày trước', 'trip.toast.assignedToDay': 'Địa điểm đã được gán vào ngày', + 'trip.toast.loadError': 'Không thể tải chuyến đi', 'trip.toast.reorderError': 'Không thể sắp xếp lại', 'trip.toast.reservationUpdated': 'Đã cập nhật đặt chỗ', 'trip.toast.reservationAdded': 'Đã thêm đặt chỗ', diff --git a/shared/src/i18n/zh-TW/trip.ts b/shared/src/i18n/zh-TW/trip.ts index db60279bc..9c7f3615b 100644 --- a/shared/src/i18n/zh-TW/trip.ts +++ b/shared/src/i18n/zh-TW/trip.ts @@ -24,6 +24,7 @@ const trip: TranslationStrings = { 'trip.toast.placeDeleted': '地點已刪除', 'trip.toast.selectDay': '請先選擇一天', 'trip.toast.assignedToDay': '地點已分配到當天', + 'trip.toast.loadError': '無法載入行程', 'trip.toast.reorderError': '排序失敗', 'trip.toast.reservationUpdated': '預訂已更新', 'trip.toast.reservationAdded': '預訂已新增', diff --git a/shared/src/i18n/zh/trip.ts b/shared/src/i18n/zh/trip.ts index 43c95cfcb..4f70b3c6e 100644 --- a/shared/src/i18n/zh/trip.ts +++ b/shared/src/i18n/zh/trip.ts @@ -24,6 +24,7 @@ const trip: TranslationStrings = { 'trip.toast.placeDeleted': '地点已删除', 'trip.toast.selectDay': '请先选择一天', 'trip.toast.assignedToDay': '地点已分配到当天', + 'trip.toast.loadError': '无法加载行程', 'trip.toast.reorderError': '排序失败', 'trip.toast.reservationUpdated': '预订已更新', 'trip.toast.reservationAdded': '预订已添加',