feat(ui,roundtrip): modern neutral “Apple Glass” UI, scenic round trips, and isochrone guidance
Some checks failed
build-and-push / docker (push) Failing after 10m2s

- UI (neutral minimal, glass-like)
  - Rewrote app shell and control panel in [app/web/static/index.html](cci:7://file:///home/pedan/freemoto/app/web/static/index.html:0:0-0:0)
  - Modernized responsive styles in [app/web/static/styles.css](cci:7://file:///home/pedan/freemoto/app/web/static/styles.css:0:0-0:0)
  - Proper internal scrolling for control panel, suggestions, waypoints, and directions sheet
  - Improved dark mode, spacing, and visual polish

- Round Trip generation
  - Direction preference (N/E/S/W) with biased waypoint bearings ([route.js](cci:7://file:///home/pedan/freemoto/app/web/static/route.js:0:0-0:0))
  - Ferry avoidance by default on round trips; added strong ferry cost
  - Coastal safety: reverse-geocode water check and automatic inland adjustment for generated waypoints
  - New “Scenic optimizer” option: generate candidate loops, route them, score twistiness, pick the best
    - Heading-change/turn-density based scoring using decoded polyline

- Isochrone-guided loops
  - Backend: added `/isochrone` proxy in [app/web/main.go](cci:7://file:///home/pedan/freemoto/app/web/main.go:0:0-0:0) (derives Valhalla base from `VALHALLA_URL`)
  - Frontend: optional “Use isochrone guidance” + time (minutes); sample preferred sector of polygon

- Settings persistence and live updates
  - Direction, scenic, isochrone, and distance persist via localStorage
  - Changing options regenerates round trips when enabled

- Docker/Build
  - Confirmed multi-arch build (amd64/arm64) and push to git.ztsw.de/pedan/freemoto/freemoto-web:latest

Refs: [index.html](cci:7://file:///home/pedan/freemoto/app/web/static/index.html:0:0-0:0), [styles.css](cci:7://file:///home/pedan/freemoto/app/web/static/styles.css:0:0-0:0), [route.js](cci:7://file:///home/pedan/freemoto/app/web/static/route.js:0:0-0:0), [main.js](cci:7://file:///home/pedan/freemoto/app/web/static/main.js:0:0-0:0), [app/web/main.go](cci:7://file:///home/pedan/freemoto/app/web/main.go:0:0-0:0), [Dockerfile](cci:7://file:///home/pedan/freemoto/Dockerfile:0:0-0:0)
This commit is contained in:
2025-09-19 14:45:21 +02:00
parent c8aa3fb3b2
commit c37ca4e8b4
5 changed files with 494 additions and 38 deletions

View File

@@ -9,7 +9,7 @@
<link href="/styles.css?v=20250918" rel="stylesheet" />
</head>
<body>
<!-- App Bar -->
<!-- App Bar (Neutral minimal, glassy) -->
<header class="appbar navbar px-3">
<div class="container-fluid d-flex align-items-center justify-content-between">
<div class="d-flex align-items-center gap-2">
@@ -20,11 +20,11 @@
<span id="summaryPill" class="badge rounded-pill text-bg-light d-none"></span>
</div>
<div class="d-flex align-items-center gap-2">
<button class="btn btn-outline-secondary btn-sm" id="clearRouteBtn" title="Clear route">Clear</button>
<div class="btn-group btn-group-sm" role="group" aria-label="Zoom controls">
<button class="btn btn-outline-secondary" id="zoomOutBtn" title="Zoom out"></button>
<button class="btn btn-outline-secondary" id="zoomInBtn" title="Zoom in">+</button>
</div>
<button class="btn btn-outline-secondary btn-sm" id="clearRouteBtn" title="Clear route">Clear</button>
<button class="btn btn-outline-secondary btn-sm" id="themeToggle" title="Toggle dark mode">🌙</button>
</div>
</div>
@@ -33,12 +33,11 @@
<!-- Map canvas -->
<div id="map" aria-label="Map"></div>
<!-- Control Drawer: overlay on mobile, sidebar on desktop -->
<!-- Control Panel (neutral, glassy, scrollable) -->
<aside class="nav-panel shadow">
<div class="d-flex align-items-center justify-content-between mb-2">
<div class="d-flex align-items-center gap-2">
<span class="badge text-bg-primary">Beta</span>
<span class="fw-semibold">Motorcycle Route Planner</span>
<span class="fw-semibold">Planner</span>
</div>
<button class="btn btn-sm btn-outline-secondary d-none d-md-inline" onclick="resetMarkers()">Reset</button>
</div>
@@ -46,14 +45,12 @@
<div id="routeInfoCard" class="alert alert-info d-none" role="alert"></div>
<div id="nextManeuverBanner" class="alert alert-secondary py-2 px-3 d-none" role="status"></div>
<!-- Route inputs -->
<!-- Route inputs (compact) -->
<div class="section-title mb-1">Route</div>
<div class="row g-2 align-items-stretch">
<div class="col-12">
<div class="input-group position-relative">
<span class="input-group-text" title="Start" aria-label="Start">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="#0d6efd" viewBox="0 0 16 16"><path d="M8 16s6-5.686 6-10A6 6 0 1 0 2 6c0 4.314 6 10 6 10zm0-7a3 3 0 1 1 0-6 3 3 0 0 1 0 6z"/></svg>
</span>
<span class="input-group-text" title="Start" aria-label="Start">A</span>
<input type="text" class="form-control" id="sourceInput" placeholder="Start address" autocomplete="off" />
<button class="btn btn-outline-secondary" type="button" id="useCurrentSource" title="Use current location">📍</button>
<div id="sourceSuggestions" class="list-group position-absolute w-100"></div>
@@ -64,9 +61,7 @@
</div>
<div class="col-12">
<div class="input-group position-relative">
<span class="input-group-text" title="Destination" aria-label="Destination">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="#dc3545" viewBox="0 0 16 16"><path d="M14.778 2.222a.5.5 0 0 1 0 .707l-2.5 2.5a.5.5 0 0 1-.707 0l-2.5-2.5a.5.5 0 0 1 .707-.707L12 3.793l2.071-2.071a.5.5 0 0 1 .707 0z"/><path d="M2.5 15a.5.5 0 0 1-.5-.5v-13a.5.5 0 0 1 1 0v13a.5.5 0 0 1-.5.5z"/></svg>
</span>
<span class="input-group-text" title="Destination" aria-label="Destination">B</span>
<input type="text" class="form-control" id="destInput" placeholder="Destination address" autocomplete="off" />
<button class="btn btn-outline-secondary" type="button" id="useCurrentDest" title="Use current location">📍</button>
<div id="destSuggestions" class="list-group position-absolute w-100"></div>
@@ -96,12 +91,12 @@
<button type="button" id="plotRouteBtn" class="btn btn-success">Plot Route</button>
</div>
<!-- Options accordion -->
<!-- Options (collapsible minimal) -->
<div class="accordion mb-2" id="optionsAccordion">
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#optionsCollapse" aria-expanded="false" aria-controls="optionsCollapse">
Route Options
Options
</button>
</h2>
<div id="optionsCollapse" class="accordion-collapse collapse" data-bs-parent="#optionsAccordion">
@@ -144,6 +139,43 @@
<button type="button" id="roundTripBtn" class="btn btn-sm btn-outline-primary">Create</button>
</div>
</div>
<div class="row g-2 align-items-center mb-2">
<div class="col-12 col-md-6">
<label for="roundTripDir" class="form-label mb-0 text-muted">Direction preference</label>
</div>
<div class="col-12 col-md-6">
<select id="roundTripDir" class="form-select form-select-sm">
<option value="any" selected>Any</option>
<option value="N">North</option>
<option value="E">East</option>
<option value="S">South</option>
<option value="W">West</option>
</select>
</div>
</div>
<div class="row g-2 align-items-center mb-2">
<div class="col-12 col-md-6">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="roundTripScenic">
<label class="form-check-label" for="roundTripScenic">Scenic optimizer (twistier)</label>
</div>
</div>
</div>
<div class="row g-2 align-items-center mb-2">
<div class="col-12 col-md-6">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="roundTripIsochrone">
<label class="form-check-label" for="roundTripIsochrone">Use isochrone guidance</label>
</div>
</div>
<div class="col-12 col-md-6">
<div class="input-group input-group-sm">
<span class="input-group-text">Time</span>
<input type="number" min="10" max="240" step="5" id="isochroneMinutes" class="form-control" placeholder="Minutes" value="60">
<span class="input-group-text">min</span>
</div>
</div>
</div>
<div class="row g-2 align-items-center mb-2">
<div class="col-12 col-md-6">
<div class="form-check">