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:
Vendored
+50
-17
@@ -46,10 +46,29 @@ export function makeBandDraggable(node, entry) {
|
||||
node.addEventListener('dragend', () => node.classList.remove('dragging'));
|
||||
}
|
||||
|
||||
// Wire an in-range day cell as a drop target. On drop the dragged entry moves to
|
||||
// `targetDate` with sort_order = `targetCount` (appended after that day's
|
||||
// entries); an entry with an end_date has it shifted by the same day-delta in
|
||||
// the SAME patch (the backend rejects date > stored end_date otherwise).
|
||||
// Shared move-commit: PATCHes the dragged entry to `targetDate` with the given
|
||||
// `sortOrder`, shifting `end_date` by the same day-delta in the SAME patch (the
|
||||
// backend rejects date > stored end_date otherwise). No-ops when dropped back
|
||||
// on its own day. Used by both the day-cell and week-bands drop targets below.
|
||||
async function commitEntryMove(id, targetDate, sortOrder, entriesById, tctx) {
|
||||
const entry = entriesById.get(id);
|
||||
if (!entry || entry.date === targetDate) return; // dropped on its own day
|
||||
const patch = { date: targetDate, sort_order: sortOrder };
|
||||
if (entry.end_date) {
|
||||
const delta = dayDelta(entry.date, targetDate);
|
||||
patch.end_date = ymd(addDays(parseYMD(entry.end_date), delta));
|
||||
}
|
||||
try {
|
||||
await api.entries.update(id, patch);
|
||||
await tctx.refreshTrip();
|
||||
} catch (err) {
|
||||
toast(err.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Wire an in-range day cell as a drop target. On drop the dragged entry moves
|
||||
// to `targetDate` with sort_order = `targetCount` (appended after that day's
|
||||
// entries).
|
||||
export function makeDayDropTarget(cell, targetDate, targetCount, entriesById, tctx) {
|
||||
cell.addEventListener('dragover', (e) => {
|
||||
e.preventDefault();
|
||||
@@ -64,19 +83,33 @@ export function makeDayDropTarget(cell, targetDate, targetCount, entriesById, tc
|
||||
cell.classList.remove('cal-day-drop');
|
||||
const id = Number(e.dataTransfer.getData(DND_MIME));
|
||||
if (!id) return;
|
||||
const entry = entriesById.get(id);
|
||||
if (!entry || entry.date === targetDate) return; // dropped on its own day
|
||||
const patch = { date: targetDate, sort_order: targetCount };
|
||||
if (entry.end_date) {
|
||||
const delta = dayDelta(entry.date, targetDate);
|
||||
patch.end_date = ymd(addDays(parseYMD(entry.end_date), delta));
|
||||
}
|
||||
try {
|
||||
await api.entries.update(id, patch);
|
||||
await tctx.refreshTrip();
|
||||
} catch (err) {
|
||||
toast(err.message);
|
||||
}
|
||||
await commitEntryMove(id, targetDate, targetCount, entriesById, tctx);
|
||||
});
|
||||
}
|
||||
|
||||
// Wire a week's stay-bands strip as a drop target, so dragging a chip/band
|
||||
// onto a spot already covered by another stay's band still moves the entry
|
||||
// (day cells sit below the bands strip and are otherwise unreachable there).
|
||||
// The target day is derived from the pointer's x-position across the 7-column
|
||||
// strip; the moved entry is appended (sort_order 0 — bands don't track a
|
||||
// day's chip order).
|
||||
export function makeWeekBandsDropTarget(strip, weekYmd, entriesById, tctx) {
|
||||
strip.addEventListener('dragover', (e) => {
|
||||
e.preventDefault();
|
||||
e.dataTransfer.dropEffect = 'move';
|
||||
strip.classList.add('cal-bands-drop');
|
||||
});
|
||||
strip.addEventListener('dragleave', (e) => {
|
||||
if (!strip.contains(e.relatedTarget)) strip.classList.remove('cal-bands-drop');
|
||||
});
|
||||
strip.addEventListener('drop', async (e) => {
|
||||
e.preventDefault();
|
||||
strip.classList.remove('cal-bands-drop');
|
||||
const id = Number(e.dataTransfer.getData(DND_MIME));
|
||||
if (!id) return;
|
||||
const rect = strip.getBoundingClientRect();
|
||||
const idx = Math.min(6, Math.max(0, Math.floor((e.clientX - rect.left) / (rect.width / 7))));
|
||||
await commitEntryMove(id, weekYmd[idx], 0, entriesById, tctx);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user