Brownfield strangler migration of the backend onto NestJS modules
(auth, trips, days, places, assignments, packing, todo, budget,
reservations, collab, files, photos, journey, share, settings, backup,
oidc, oauth, admin, atlas, vacay, weather, airports, maps, categories,
tags, notifications, system-notices) served through a per-prefix
dispatcher, keeping the existing SQLite/better-sqlite3 DB and JWT
httpOnly cookie auth, with behavioural parity for every route.
Client: React 19 upgrade, "page = wiring container + data hook"
pattern across all pages, per-domain Zustand stores bound to
@trek/shared contracts, and decomposition of the large components
(DayPlanSidebar, PackingListPanel, CollabNotes, FileManager,
MemoriesPanel, PlacesSidebar, CollabChat, SystemNoticeModal,
BudgetPanel, PlaceFormModal, ...) into focused render units backed by
in-file hooks.
Apply the shared global request pipeline (helmet/CSP, CORS, HSTS,
forced HTTPS, the global MFA policy and request logging) to the NestJS
instance as well, so a migrated route is protected identically to the
legacy fallback rather than bypassing it.
Re-adds the share_map permission toggle to the journey share settings UI so
owners can control whether the map is visible on the public share page.
Fixes horizontal scrollbar on the public journey page caused by decorative
hero circles with negative offsets overflowing the viewport.
Map permission is always enabled on new links (share always includes map).
Removed the toggle from the share settings UI since the map is now always
part of the combined timeline+map view with no standalone value in toggling it.
Desktop entry cards on the public share page now open MobileEntryView on click,
matching the mobile behaviour added in #826.
- #828: exclude 'map' from availableViews on mobile; MobileMapTimeline already
shows combined map+timeline so the standalone map tab is redundant
- #827: cap timeline feed column at xl:max-w-[50%] on ≥1280px viewports so the
map aside is not dwarfed on wide monitors; applies to both desktop two-column
layouts (JourneyPublicPage)
- #826: wire MobileMapTimeline onEntryClick to setViewingEntry; render
MobileEntryView with readOnly + public photo URL builder so photos load via
the share token endpoint; add publicPhotoUrl prop to MobileEntryView so
photo URLs are routable for both authenticated and public-share contexts
- sidebarMapItems now derives dayIdx from all timeline dates (not just
located-entry dates), so markers stay color-aligned with day headers
even when some days have no location
- scroll-sync no longer calls highlightMarker for unlocated entries,
preventing the map from clearing or misfiring when the scroll winner
has no corresponding marker
- same dayIdx fix applied to JourneyPublicPage desktop two-column view
formatDate() in both JourneyDetailPage and JourneyPublicPage passed
undefined/'en' as the locale to toLocaleDateString, so weekday/month
names always followed the browser's language instead of the app's
selected UI language. Thread the selected locale through from
useTranslation() in both pages.
Public view still falls back to 'en' when no settings locale is
available (shared links can be opened by anyone).
MobileMapTimeline received the raw entries array, bypassing the
synthetic-container filter applied to timelineEntries. On screens
below the lg breakpoint (<1024px) the [Trip Photos] sync container
leaked back into the combined map+timeline view.
- Budget table column alignment: the NAME data cell had
`display: flex` directly on the <td>, which pulled it out of the
table-layout and desynced the column widths between data rows and the
AddItemRow. Moved the flex wrapper into a <div> inside the cell.
Closes#759
- Packing list: template-apply and bulk-import handlers called
`window.location.reload()` to refresh the list, which re-rendered the
whole trip loading screen. Both flows now merge the returned items
into the trip store instead. Closes#760
- Journey timeline: move-up / move-down arrows were rendered on
skeleton suggestions — skeletons are places from the linked trip and
don't participate in sort order. Skip canReorder when
entry.type === 'skeleton'. Closes#763
- Journey public view: the synthetic `[Trip Photos]` and `Gallery`
entries produced by syncTripPhotos were leaking into the public
timeline and map. The owner view already strips these in
JourneyDetailPage — apply the same filter on JourneyPublicPage.
Gallery photos still come from every entry so a shared gallery keeps
showing the trip-synced photos. Closes#764
- Journey thumbnails: public gallery grid was loading the original
asset for every tile. `photoUrl()` now takes an optional kind and the
grid requests `thumbnail`; the lightbox still opens the original.
Synology thumbnail default bumped from `sm` (240px) to `m` (320px)
because `sm` looked pixelated on retina. Closes#761
Merge the separate Timeline and Map tabs into a single fullscreen
combined view on mobile (<1024px). A Leaflet map fills the background
while a horizontal snap-scroll carousel of entry cards sits at the
bottom. Scrolling the carousel auto-focuses the corresponding map
marker; tapping a marker scrolls to the card. Tapping a card opens
a new fullscreen entry view with edit/delete actions.
- New: MobileMapTimeline, MobileEntryCard, MobileEntryView components
- New: useIsMobile hook (matchMedia < 1024px)
- JourneyMap: fullScreen + paddingBottom props, focusMarker guard
- Desktop layout completely unchanged
- Public share page gets the same combined view (read-only)
- Fix: entry editor now portaled to body (iOS stacking context)
- Fix: pros/cons dark mode input backgrounds
- Fix: mood button borders in dark mode
- Fix: location icon color (neutral instead of green/indigo)
Introduce trek_photos as central photo registry. Frontend uses
/api/photos/:id/:kind instead of provider-specific URLs. Adding
a new photo provider is now backend-only work.
- New trek_photos table (migration 98) with photo_id FK in
trip_photos and journey_photos
- Unified /api/photos/:id/thumbnail|original|info endpoint
- photoResolverService for central resolution and streaming
- ProviderPicker: add "All Photos" tab, rename tabs, fix i18n
- Localize all hardcoded strings in JourneyDetailPage (14 langs)
- Fix date formatting to use browser locale instead of hardcoded 'en'
- Journey stats as styled tile cards
- 5-table schema (journeys, entries, photos, trips, contributors) with migrations 87-91
- Trip-to-Journey sync engine with skeleton entries and photo sync
- Full CRUD API for journeys, entries, photos with Immich/Synology integration
- Timeline, Gallery and Map views with entry editor (markdown, mood, weather, pros/cons)
- Journey frontpage with hero card, stats and trip suggestions
- Public share links with token-based access and photo proxy
- PDF photo book export (Polarsteps-inspired)
- Dashboard redesign: mobile greeting, live trip hero, quick actions, unified card design
- BottomNav profile sheet with settings/admin/logout
- DayPlan mobile inline place picker
- TripFormModal members management
- Vacay calendar trip date indicator dots
- Fix contributor photo access (403) for journey Immich/Synology photos
- Trip deletion cleanup for journey skeleton entries
- i18n: 231 new keys across all 14 languages (native translations, no fallbacks)