Add scenic waypoints for drive legs (OSRM via-routing)

- 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)
This commit is contained in:
2026-07-20 14:57:26 +07:00
parent 9369c82e56
commit e2c3089c25
15 changed files with 594 additions and 16 deletions
+15
View File
@@ -284,6 +284,8 @@ textarea.input { resize: vertical; }
.map-pin-wrap { background: none; border: none; }
.map-pin { display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px; border-radius: 50%; color: #fff; font-weight: 700; font-size: 0.8rem; box-shadow: 0 2px 6px rgba(0,0,0,0.35); border: 2px solid #fff; }
.km-label { background: rgba(37, 99, 235, 0.92); color: #fff; font-size: 0.7rem; font-weight: 700; padding: 0.1rem 0.4rem 0.1rem 0.55rem; border-radius: 999px; white-space: nowrap; box-shadow: 0 1px 3px rgba(0,0,0,0.3); border-left: 4px solid var(--leg-accent, transparent); }
.wp-dot-wrap { background: none; border: none; }
.wp-dot { display: block; width: 10px; height: 10px; border-radius: 50%; background: var(--wp-color, var(--brand)); border: 2px solid #fff; box-shadow: 0 1px 3px rgba(0,0,0,0.35); }
.map-popup strong { font-size: 0.95rem; }
.map-popup-sub { color: #64748b; font-size: 0.8rem; margin-top: 0.15rem; }
.map-popup-loc { font-size: 0.82rem; margin-top: 0.15rem; }
@@ -326,6 +328,8 @@ textarea.input { resize: vertical; }
.icon-btn { border: none; background: var(--surface-2); border-radius: 8px; width: 2rem; height: 2rem; cursor: pointer; font-size: 1rem; display: inline-flex; align-items: center; justify-content: center; color: var(--text-muted); }
.icon-btn:hover { background: var(--border); color: var(--text); }
.icon-btn.danger:hover { background: var(--danger-soft); color: var(--danger); }
.icon-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.icon-btn:disabled:hover { background: var(--surface-2); color: var(--text-muted); }
.entry-list { display: flex; flex-direction: column; gap: 0.5rem; }
.entry-empty { padding: 0.6rem 0; }
@@ -434,6 +438,17 @@ textarea.input { resize: vertical; }
.entry-seg { display: flex; gap: 0.5rem; font-size: 0.8rem; font-variant-numeric: tabular-nums; }
.seg-flight { font-weight: 700; color: var(--brand-dark); }
/* ---------- Scenic waypoints (day editor) ---------- */
.wp-section { display: flex; flex-direction: column; gap: 0.5rem; }
.wp-list { display: flex; flex-direction: column; gap: 0.4rem; }
.wp-row { display: flex; align-items: center; gap: 0.5rem; border: 1px solid var(--border); border-left: 3px solid var(--brand); border-radius: var(--radius-sm); padding: 0.45rem 0.6rem; background: var(--surface-2); }
.wp-num { flex-shrink: 0; width: 1.4rem; height: 1.4rem; display: inline-flex; align-items: center; justify-content: center; background: var(--brand-soft); color: var(--brand-dark); border-radius: 50%; font-weight: 700; font-size: 0.75rem; }
.wp-name { flex: 1; font-size: 0.85rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wp-row-actions { display: flex; gap: 0.2rem; flex-shrink: 0; }
.wp-move { font-size: 0.8rem; }
.wp-search { position: relative; }
.wp-max-hint { margin: 0; }
.stat-sub { font-size: 0.66rem; color: var(--brand-dark); font-weight: 700; }
/* ---------- Rental details (day editor) ---------- */