Add transport sync button and allow dropping stays onto band-covered days
- 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
This commit is contained in:
+11
-2
@@ -48,6 +48,7 @@ entries (id INTEGER PK, trip_id INTEGER NOT NULL REFERENCES trips(id),
|
||||
segments TEXT, -- JSON array, flight entries only (see below)
|
||||
rental TEXT, -- JSON object, rental entries only (see below)
|
||||
transport_mode TEXT, -- transport entries only (see below)
|
||||
auto_ref TEXT, -- JSON {from,to} stay ids; non-null = auto-created transport (see below)
|
||||
created_at TEXT DEFAULT current_timestamp)
|
||||
entry_participants (entry_id INTEGER REFERENCES entries(id), user_id INTEGER REFERENCES users(id),
|
||||
PRIMARY KEY (entry_id, user_id))
|
||||
@@ -62,7 +63,15 @@ Entry `type` ∈ `flight | transport | activity | rental | stay | note`.
|
||||
|
||||
`transport` covers ground/sea travel (the old `travel` type). Optional `transport_mode` ∈ `train | bus | ferry | taxi | drive | other` (nullable; drives the icon in the UI). Validation: only allowed when the effective `type === 'transport'` (else 400 `transport_mode is only allowed on transport entries`); PATCH `transport_mode: null` clears it. Entry JSON always includes `transport_mode` (string or null).
|
||||
|
||||
**Auto-transport between stays**: when a `stay` entry is POSTed and it has a chronological neighbour stay (the nearest stay before and/or after it, ordered by `date`), the server auto-creates one `transport` entry per neighbour pair — titled `"<from short name> → <to short name>"` (short name = first comma-segment of the stay's `location_name`, fallback `title`), dated on the **later** stay's `date`, `transport_mode` null, no price/location, `sort_order` 0. Skipped when any `transport` or `flight` entry already exists with `date` between the earlier stay's end (`end_date` or `date`) and the later stay's `date` (inclusive). One-shot: fires only on stay **creation** (never PATCH), so deleting an auto-created transport does not resurrect it. The POST response is unchanged (`201 {entry}` = the stay); clients should refetch the entry list.
|
||||
**Auto-transport between stays**: when a `stay` entry is POSTed and it has a chronological neighbour stay (the nearest stay before and/or after it, ordered by `date`), the server auto-creates one `transport` entry per neighbour pair — titled `"<from short name> → <to short name>"` (short name = first comma-segment of the stay's `location_name`, fallback `title`), dated on the **later** stay's `date`, `transport_mode` null, no price/location, `sort_order` 0, and `auto_ref` set to `{"from": <earlier stay id>, "to": <later stay id>}` (stored JSON, returned parsed; null on every other entry — the marker for "auto-created"). Skipped when any `transport` or `flight` entry already exists with `date` between the earlier stay's end (`end_date` or `date`) and the later stay's `date` (inclusive). One-shot: fires only on stay **creation** (never PATCH), so deleting an auto-created transport does not resurrect it. The POST response is unchanged (`201 {entry}` = the stay); clients should refetch the entry list.
|
||||
|
||||
**Regenerating auto-transports** — `POST /api/trips/:id/transports/regenerate` (any member, no body) reconciles auto transports after stays have been moved/reshuffled:
|
||||
|
||||
1. Compute the **desired pairs**: stays in chronological `(date, id)` order → each adjacent pair `(earlier, later)`, skipping pairs whose window (`earlier.end_date||date` .. `later.date`, inclusive; skip if inverted/overlapping) contains a **manual** transport (`auto_ref` null) or any flight.
|
||||
2. Existing auto transports (`type='transport'`, `auto_ref` non-null) whose `{from,to}` matches a desired pair are **kept but re-synced**: `date` set to the pair's current transition date (`later.date`), title regenerated to the current `"A → B"`; `transport_mode`, price/split, times, details are preserved. Auto transports matching no desired pair are **deleted**.
|
||||
3. Missing desired pairs get a fresh auto transport (same shape as creation-time).
|
||||
|
||||
Response: `200 {created, updated, deleted}` (counts). Auto transports from databases predating `auto_ref` have it null and are treated as manual (never touched). Stays MAY overlap in dates (two locations on the same day, e.g. temporarily while reshuffling) — overlapping adjacent pairs simply produce no auto transport, and the calendar renders overlapping stays in stacked band lanes.
|
||||
|
||||
### Multi-day entries & stays
|
||||
|
||||
@@ -171,7 +180,7 @@ User JSON shape everywhere: `{id, display_name}`.
|
||||
| `PATCH /api/entries/:id` | any subset of the above | `200 {entry}` (member of the entry's trip required; `participants` replaces the whole set) |
|
||||
| `DELETE /api/entries/:id` | — | `204` (also deletes its entry_participants rows) |
|
||||
|
||||
Entry JSON shape (always full row): `{id, trip_id, date, end_date, type, title, details, start_time, end_time, location_name, lat, lng, sort_order, price, paid_by, split_mode, participants, segments, rental, transport_mode}` where `participants` is an array of user ids (`[]` = all members) and `segments` is the parsed array or `null`.
|
||||
Entry JSON shape (always full row): `{id, trip_id, date, end_date, type, title, details, start_time, end_time, location_name, lat, lng, sort_order, price, paid_by, split_mode, participants, segments, rental, transport_mode, auto_ref}` where `participants` is an array of user ids (`[]` = all members), `segments` is the parsed array or `null`, and `auto_ref` is the parsed `{from,to}` object or `null`. `auto_ref` is server-managed (not accepted in POST/PATCH bodies; editing an auto transport keeps its `auto_ref`).
|
||||
|
||||
### Route & summary (computed)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user