mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-19 21:31:46 +00:00
54e81b07859a3091da28ccf1e3dab166cce1e59a
5 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
bb477645a3 |
Support multi-leg (layover) flights (#1146)
* feat(transport): support multi-leg (layover) flights in the booking form
A flight booking can now hold an ordered chain of airports (e.g. FRA -> BER ->
HND) instead of a single departure/arrival pair. The route is entered as a list
of waypoints with a '+ add stop' button; each stop carries its own arrival and
departure time plus the airline/flight number of the segment leaving it, while
the whole booking keeps one price.
Stored without a schema change: the existing reservation_endpoints rows carry the
ordered waypoints (from/stop/to by sequence) and a metadata.legs array holds the
per-leg detail. Top-level metadata (departure_airport/arrival_airport/airline/
flight_number) mirrors the first and last leg, so a single-leg flight persists
exactly as before and legacy readers keep working.
* feat(planner): show each flight leg as its own day-plan entry, ordered by time
A multi-leg flight now expands into one entry per leg (BER -> FRA, then FRA ->
HND), each on its own day with its own times, instead of a single span. Each leg
is an addressable slot (reservation id + leg index) so places and notes can be
dropped into the layover gap between legs; the per-leg position is persisted in
metadata.legs[i].day_positions and survives a reload.
Day-plan items are now ordered chronologically: anything with a time (a place's
time, a flight leg, a timed note) sorts by that time, and untimed items inherit
the time of the item before them so they stay where they were placed.
* feat(planner): show the full multi-stop route in the bookings panel
The route row now lists every waypoint (FRA -> BER -> HND) by sequence instead of
just the first and last airport.
* feat(map): draw multi-leg flights as connected legs with a marker per airport
Both the Leaflet and Mapbox overlays now render a flight over all its waypoints:
one great-circle arc per leg and a marker at every airport, with the label
showing the full route and the summed distance. A single-leg flight is unchanged.
Also drops the floating stats badge that was drawn on transport arcs.
* fix(map): centre a clicked place above the bottom inspector panel
Selecting a place panned/flew it to the dead centre of the screen, where it sat
behind the detail card. Both overlays now bias the target into the visible area
above the bottom panel (Leaflet offsets the pan by the inspector inset; Mapbox
passes the padding to flyTo).
* feat: show the full multi-stop flight route in PDF and calendar export
The PDF day list and the ICS export now render the whole route (FRA → BER → HND)
for a multi-leg flight instead of just the first and last airport, falling back to
the flat metadata for single-leg flights. The ICS keeps a single event per booking.
* feat(import): group connecting flight legs into one multi-leg booking
When a booking confirmation contains several flight legs sharing a PNR that
connect at the same airport with a short layover (under 24h), they are now
imported as a single multi-leg booking (from/stop/to endpoints + metadata.legs)
instead of one booking per leg. A round trip (same PNR, multi-day gap) stays two
separate bookings, and a single flight is unchanged.
* i18n: translate the new flight-route strings into all languages
* i18n: translate the Costs page into every language
The Budget → Costs rework left the new costs.* strings untranslated in every
non-English locale (they fell back to English). Translate them across all
supported languages.
* Revert "fix(map): centre a clicked place above the bottom inspector panel"
This reverts commit
|
||
|
|
093e069ccc |
Backend/frontend hardening & consistency cleanups (#1113)
* refactor(auth): session token validation and password-change consistency * refactor(journey): entry field allow-list and public share-link consistency * refactor(mcp): align tool authorization with the REST permission checks * chore: input validation and sanitisation touch-ups (uploads, pdf, maps, backup, csp) |
||
|
|
247433fb2a |
feat(costs): rework Budget into Costs — Splitwise-style, multi-currency, mobile (#1106)
* fix(journey): authorize reads of the journey share link GET /api/journeys/:id/share-link now requires journey access (canAccessJourney), matching the create/delete share-link routes and the get_journey_share_link MCP tool. Returns no link when the caller lacks access to the journey. * feat(costs): rework Budget into Costs — Splitwise-style, multi-currency, mobile Renames the Budget addon to "Costs" (UI only) and reworks it into a Tricount/ Splitwise-style cost tracker: multiple payers per expense, equal split across chosen members, settle-up with persisted history + undo, 12 fixed categories, per-expense currency with live FX conversion to a user-set display currency (Settings -> Display), and locale-correct money formatting. Adds a desktop and a dedicated mobile layout. A migration backfills existing budget items (single payer, split members, currency). Closes #551 (per-expense currency). Also switches the app font to self-hosted Poppins (Geist for secondary subtext), replacing the Google Fonts CDN dependency. * fix(costs): neutral dashboard dark palette + liquid glass, full page width, entry-count badge - Dark mode used a warm oklch palette that read brownish; switch to the neutral zinc tokens used by the dashboard (#121215 bg, #f4f4f5 ink) and add a subtle backdrop-blur glass on cards. - Costs now uses the full available page width on desktop instead of a 1280px cap. - Render the expense count next to the Expenses title as a badge. - Adapt budget/journey unit tests to the new payer-based settlement model and the Costs rename (category default 'other', Costs tab/CostsPanel). * fix(costs): drop the entry-count badge, always show row edit/delete actions Removes the count badge next to the Expenses title and makes the per-row edit/delete actions permanently visible (no longer hover-only) on desktop too. * feat(costs): currency-native money formatting, custom select/date, rename addon to Costs - Format every amount in its own currency convention (symbol position, grouping and decimal separators) regardless of app language, via a currency->locale map (EUR -> '12,00 €', USD -> '$12.00', JPY -> '¥12', ...). Previously Intl used the app locale, so EUR showed the symbol in front under an English UI. - Use TREK's CustomSelect (searchable, with symbols) and CustomDatePicker in the add/edit expense modal instead of the native <select>/<input type=date>. - Rename the 'Budget Planner' add-on to 'Costs' in the admin list (display only; id/tables/permissions/MCP stay 'budget') via seed + a migration for existing DBs. * feat(auth): configurable session duration via SESSION_DURATION Adds a SESSION_DURATION env var (ms-style strings: 1h, 7d, 30d, ...) controlling how long a session stays valid before re-login. It drives both the trek_session JWT exp claim and the cookie maxAge from one source, so they never drift. Invalid values warn at startup and fall back to the default (24h — unchanged). The MFA challenge token and MCP OAuth tokens keep their own TTL. Implements the request from discussion #946. Documented in the env-var wiki page, .env.example and docker-compose.yml. |
||
|
|
abe1c549bd |
feat(transport): add bus, taxi, bicycle, ferry and other transport types (#1105)
Closes #718. Adds five new transport reservation types alongside the existing flight/train/car/cruise: bus, taxi, bicycle, ferry and a generic 'transport_other' catch-all. The new types are treated as first-class transports everywhere — the transport modal, day plan, route calculation, map overlays, file grouping and the PDF export — and are translated across all 20 locales. A dedicated 'transport_other' value is used for the catch-all so existing 'other' bookings are not reclassified as transport. |
||
|
|
f8eb1915fe |
fix(map): render transport reservations on Mapbox GL
ReservationOverlay was Leaflet-only: react-leaflet components, L.divIcon, panes, useMap/useMapEvents. When the user switched the planner map to Mapbox GL, the entire feature disappeared — no polylines, no endpoint badges, no clickable IATA labels. Add a matching overlay for the Mapbox renderer: - New reservationsMapbox.ts with an imperative `ReservationMapboxOverlay` class — mapbox-gl is imperative, so a React component wrapper would fight its own lifecycle every render. The manager owns one GeoJSON source + line layer for the arcs, one HTML `mapboxgl.Marker` per endpoint badge, and one per flight stats label. It cleans itself up when the map is rebuilt (style/token/3d toggle) or unmounted. - Geometry helpers (great-circle arc, antimeridian split, haversine, tz-aware duration math, label formatting) are copied from the Leaflet overlay so both renderers produce the same lines. Great-circle is useful even on the Mapbox globe because the mercator projection mode still draws the short-way line, and the antimeridian split prevents a NYC↔Tokyo flight from wrapping halfway around the planet. - Flights / cruises get geodesic arcs; trains / cars get straight lines. All four types get clickable endpoint badges with the matching lucide icon; only flights render the rotating mid-arc stats label (IATA → IATA · distance · duration) — same rule as the Leaflet overlay. - The stats label's rotation is recomputed on every `render` event by projecting two points straddling the arc midpoint, which keeps it parallel to the arc as the camera rotates/zooms on the globe. - Visibility thresholds mirror the Leaflet overlay (per-type min pixel distance before a line / endpoint label is worth drawing). - MapViewGL now accepts the `reservations`, `visibleConnectionIds`, `showReservationStats`, `onReservationClick` props that the Leaflet MapView already took. `visibleConnectionIds` is honoured the same way — the per-booking toggle in DayPlanSidebar controls which routes appear, so switching the renderer doesn't lose that UX. - Added a `mapReady` gate so the overlay can only add its source/layer once the map's `load` handler has attached the other trip sources; the gate resets on every style rebuild. |