Each trip gets a checklist whose items group under free-text categories
(Documents, Clothing, Toiletries, Health, Electronics, Extras first, then
any custom ones alphabetically). Items are either shared — every member
sees and can tick them, and checked_by records who — or personal to one
member, which nobody else can see or touch. Items carry an optional
quantity, drag-reorder within their category, and "Uncheck all" resets the
list for the trip home.
The "Suggestions" modal is deterministic, offline advice derived from the
trip itself (src/server/util/packing.js) — no LLM and no external calls, so
it stays unit-testable and works on a self-hosted box. Nights scale
clothing quantities, flights add liquids/power-bank/check-in, rentals add
licence + IDP, ferries add motion-sickness tablets, tropical stops add sun
cream and repellent, and the destination country picks the plug type from a
bundled ~50-country table. Every suggestion carries a short reason, and
already-added ones are keyed by suggestion_key so they can't be duplicated.
Two rules deliberately differ from the naive reading, both regression-tested:
a latitude floor stops a December trip to Bangkok being tagged cold as well
as tropical, and only a flight segment's arrival airport counts, since the
first segment's departure airport is home rather than a destination.
checklist_items is a new table, so the existing CREATE TABLE IF NOT EXISTS
path creates it on upgrade; no MIGRATIONS entry is needed and existing data
is untouched.
docs/API.md documents the full contract. 113/113 tests pass.
- Transport entries carry an optional ordered waypoints array of
lat/lng/name points; /route attaches them to the ground leg the
transport bridges, and /api/directions accepts a via param so the
drawn road route detours through them
- Day editor gains a geocoded "Scenic waypoints" list on transport
entries; the map draws leg-coloured waypoint dots
- Escape waypoint names in the Leaflet tooltip (stored-XSS fix flagged
by security review: names are user-typed and Leaflet renders string
tooltips as HTML)
Legs cycle through the shared 8-hue palette by index: polylines (air
stays dashed), km-label accents, and the Legs-list number badges all
match, so multiple car routes are distinguishable. Badges use dark text
after a contrast check ruled out white on these hues.
- GET /api/directions proxies OSRM (OSRM_URL env, default public demo
server) with validation, Leaflet-order geometry, 24h capped cache
- Map draws straight lines first, then upgrades ground legs to
road-following polylines with routed km (marked road) as directions
arrive; silent fallback to great-circle on failure; air legs unchanged
- README documents OSRM_URL
- Auto-created transports carry auto_ref {from,to} stay ids; new
POST /api/trips/:id/transports/regenerate reconciles them against the
current stay order (re-date/re-title kept bridges preserving mode and
price, delete orphans, create missing) without touching manual
transports or flight-covered gaps
- Sync transports button in the calendar header with result toast
- The week band strip is now a drop target resolving the day from the
pointer position, so stays can be dropped onto spots covered by other
stays; overlapping stays stack in band lanes
Dragging any week-segment of a stay band onto an in-range day moves the
whole stay there, preserving its length (same one-PATCH date+end_date
shift the multi-day chips use). Clicking a band still opens the editor.
- trip_members.sort_order (per-user, migrated) drives GET /api/trips order
- PATCH /api/trips/:id/order updates only the caller's membership row
- Trip cards get a drag handle reusing the generalized row-reorder helper;
clicking the card still navigates
- Type set is now activity/stay/transport/flight/rental/note; hotel, travel
and immigration are removed with idempotent startup data migrations
(hotel->stay, travel->transport, immigration->activity with flag prefix)
- Transport entries carry an optional mode (train/bus/ferry/taxi/drive/other)
that drives the chip/map icon; route stops expose transport_mode
- Creating a stay auto-creates a bridging transport to its neighbouring
stays unless a transport/flight already covers the gap (one-shot)
- Summary: transports count replaces hotels/travelLegs
Calendar chips can be dragged onto another in-range day (multi-day entries shift end_date by the same delta in one PATCH); day-editor rows get a drag handle to reorder within a day, patching sort_order only for changed positions. Continues/dropoff ghost chips and stay bands are not draggable; touch devices fall back to the existing click/edit flow. Each stay band now gets a deterministic colour from an 8-hue palette by chronological index, mirrored as dots in the summary Areas list.
Grid items default to min-width:auto, so a long unbreakable chip (multi-leg flight code chains) forced its day column wider and deformed the week grid. Allow day cells and chips to shrink (min-width: 0) so the existing ellipsis rules apply, and pin the price badge so it never truncates.
The stay-bands restructure changed the calendar markup to per-week rows (.cal-weekdays / .cal-week / .cal-week-days / .cal-week-bands) but styles.css still only styled the old flat .calendar-grid, so day cells stacked vertically. Adds the 7-column grid layout for the new structure plus the missing stay-band styles (lanes, rounded ends, hover), and updates the narrow-screen rules.
Entries gain an optional inclusive end_date (backfilled via migration): flights and travel longer than 24h show a continuation marker on following days, and a new stay entry type marks a time frame in one area (e.g. 3 days Venice) rendered as continuous bands across the calendar weeks. Stays feed the map route as stops; the route summary gains stays count and a chronological areas list with day spans. 49 API tests.
Docker creates ./data root-owned on the host, but the app runs as the unprivileged node user, causing SQLITE_CANTOPEN on first deploy. New entrypoint starts as root, chowns DATA_DIR, then drops privileges with setpriv. Compose keeps the SQLite database in ./data next to the compose file. Also trims scripts/ and playwright artifacts from the image.