From d09a62fcc8298b65be274c2fda0063bedd9732b8 Mon Sep 17 00:00:00 2001 From: Maurice Date: Fri, 26 Jun 2026 22:11:56 +0200 Subject: [PATCH] refactor(planner): move the import-review bridge effect into the page hook MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TripPlannerPage held a useEffect (the background-import → review bridge), which trips the page-pattern check (pages must stay wiring containers). Move the effect and its store/IndexedDB wiring into useTripPlanner where the rest of the import-review state already lives. --- client/src/pages/TripPlannerPage.tsx | 30 ++----------------- .../src/pages/tripPlanner/useTripPlanner.ts | 28 ++++++++++++++++- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/client/src/pages/TripPlannerPage.tsx b/client/src/pages/TripPlannerPage.tsx index 2ddad0921..268e781a0 100644 --- a/client/src/pages/TripPlannerPage.tsx +++ b/client/src/pages/TripPlannerPage.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react' +import React, { useState } from 'react' import ReactDOM from 'react-dom' import { useParams, useNavigate, useSearchParams } from 'react-router-dom' import { useTripStore } from '../store/tripStore' @@ -18,7 +18,6 @@ import TripMembersModal from '../components/Trips/TripMembersModal' import { ReservationModal } from '../components/Planner/ReservationModal' import { TransportModal } from '../components/Planner/TransportModal' import BookingImportModal from '../components/Planner/BookingImportModal' -import { useBackgroundTasksStore } from '../store/backgroundTasksStore' import AirTrailImportModal from '../components/Planner/AirTrailImportModal' // MemoriesPanel moved to Journey addon import ReservationsPanel from '../components/Planner/ReservationsPanel' @@ -36,7 +35,6 @@ import { Map, X, PanelLeftClose, PanelLeftOpen, PanelRightClose, PanelRightOpen, import { useTranslation } from '../i18n' import { addonsApi, accommodationsApi, authApi, tripsApi, assignmentsApi, mapsApi } from '../api/client' import { accommodationRepo } from '../repo/accommodationRepo' -import { offlineDb, getImportFiles, deleteImportFiles } from '../db/offlineDb' import { useAuthStore } from '../store/authStore' import ConfirmDialog from '../components/shared/ConfirmDialog' import { useResizablePanels } from '../hooks/useResizablePanels' @@ -196,7 +194,7 @@ export default function TripPlannerPage(): React.ReactElement | null { bookingForAssignmentId, setBookingForAssignmentId, showTransportModal, setShowTransportModal, editingTransport, setEditingTransport, transportModalDayId, setTransportModalDayId, - reservationPrefill, transportPrefill, importReviewActive, startImportReview, advanceImportReview, + reservationPrefill, transportPrefill, importReviewActive, advanceImportReview, routeShown, setRouteShown, routeProfile, setRouteProfile, fitKey, setFitKey, mobileSidebarOpen, setMobileSidebarOpen, mobilePlanScrollTopRef, mobilePlacesScrollTopRef, deletePlaceId, setDeletePlaceId, deletePlaceIds, setDeletePlaceIds, @@ -212,30 +210,6 @@ export default function TripPlannerPage(): React.ReactElement | null { mapTileUrl, defaultCenter, defaultZoom, fontStyle, splashDone, } = useTripPlanner() - // Bridge: when a finished background import is sent here for review (the user hit - // "review" in the background widget, on this or any page), open the per-item flow. - const bgTasks = useBackgroundTasksStore((s) => s.tasks) - const dismissBgTask = useBackgroundTasksStore((s) => s.dismiss) - useEffect(() => { - const task = bgTasks.find( - (tk) => tk.tripId === String(tripId) && tk.status === 'done' && tk.reviewRequested && !tk.consumed, - ) - if (task && task.items && task.items.length > 0) { - // Hand the items (and the source files, to attach to each booking) to the review flow - // and clear the widget entry — once the user hit "review", the background card is done. - const items = task.items - const jobId = task.id - const inMemory = task.sourceFiles - dismissBgTask(jobId) - // Prefer the in-memory files (immediate path); after a reload they live in IndexedDB. - void (async () => { - const files = inMemory && inMemory.length ? inMemory : await getImportFiles(jobId) - deleteImportFiles(jobId) - startImportReview(items, files) - })() - } - }, [bgTasks, tripId, startImportReview, dismissBgTask]) - const poi = usePoiExplore() const [glMap, setGlMap] = useState(null) const poiPillEnabled = useSettingsStore(s => s.settings.map_poi_pill_enabled) !== false diff --git a/client/src/pages/tripPlanner/useTripPlanner.ts b/client/src/pages/tripPlanner/useTripPlanner.ts index e447d2ea8..3017628ee 100644 --- a/client/src/pages/tripPlanner/useTripPlanner.ts +++ b/client/src/pages/tripPlanner/useTripPlanner.ts @@ -11,7 +11,8 @@ import { addonsApi, accommodationsApi, authApi, tripsApi, assignmentsApi, health import { parsedItemToDraft, isTransportItem, type BookingReviewDraft } from '../../components/Planner/parsedItemToDraft' import type { BookingImportPreviewItem } from '@trek/shared' import { accommodationRepo } from '../../repo/accommodationRepo' -import { offlineDb } from '../../db/offlineDb' +import { offlineDb, getImportFiles, deleteImportFiles } from '../../db/offlineDb' +import { useBackgroundTasksStore } from '../../store/backgroundTasksStore' import { useAuthStore } from '../../store/authStore' import { useResizablePanels } from '../../hooks/useResizablePanels' import { useTripWebSocket } from '../../hooks/useTripWebSocket' @@ -714,6 +715,31 @@ export function useTripPlanner() { openImportItem(items[0]) } + // Bridge: when a finished background import is sent here for review (the user hit + // "review" in the background widget, on this or any page), open the per-item flow. + // Lives in the hook so the page stays a pure wiring container. + const bgTasks = useBackgroundTasksStore((s) => s.tasks) + const dismissBgTask = useBackgroundTasksStore((s) => s.dismiss) + useEffect(() => { + const task = bgTasks.find( + (tk) => tk.tripId === String(tripId) && tk.status === 'done' && tk.reviewRequested && !tk.consumed, + ) + if (task && task.items && task.items.length > 0) { + // Hand the items (and the source files, to attach to each booking) to the review flow + // and clear the widget entry — once the user hit "review", the background card is done. + const items = task.items + const jobId = task.id + const inMemory = task.sourceFiles + dismissBgTask(jobId) + // Prefer the in-memory files (immediate path); after a reload they live in IndexedDB. + void (async () => { + const files = inMemory && inMemory.length ? inMemory : await getImportFiles(jobId) + deleteImportFiles(jobId) + startImportReview(items, files) + })() + } + }, [bgTasks, tripId, startImportReview, dismissBgTask]) + // Called when a reviewed item's modal closes (saved or skipped): open the next, // or finish the review session and refresh accommodations. const advanceImportReview = () => {