- Add /docs route serving web/templates/api.html: full Bootstrap 5 documentation page covering all three API endpoints with parameter tables, example requests, and example responses - Add 'API docs' link to the navbar on the main map page - Update README.md: add web frontend section with feature list, dev server instructions, and API endpoint summary table - Update AGENTS.md: add web/ layout, API endpoint table, Flask run instructions, and route_master example relation IDs Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
127 lines
4.9 KiB
HTML
127 lines
4.9 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>OSM Public Transport → GeoJSON</title>
|
|
<link rel="icon" type="image/svg+xml" href="{{ url_for('static', filename='favicon.svg') }}">
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
|
|
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
|
|
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
|
</head>
|
|
<body>
|
|
|
|
<nav class="navbar navbar-dark bg-dark px-3" style="height:56px">
|
|
<a class="navbar-brand" href="/">OSM Public Transport → GeoJSON</a>
|
|
<a class="nav-link text-white" href="/docs">API docs</a>
|
|
</nav>
|
|
|
|
<div class="container-fluid h-100 p-0">
|
|
<div class="row g-0 h-100" id="main-row">
|
|
|
|
<!-- Sidebar -->
|
|
<div class="col-3 border-end" id="sidebar">
|
|
|
|
<!-- Load form -->
|
|
<form method="post" action="/load" class="mb-3">
|
|
<label class="form-label fw-semibold small">Relation ID or OSM URL</label>
|
|
<div class="input-group input-group-sm">
|
|
<input type="text" name="relation" class="form-control"
|
|
placeholder="e.g. 15083963"
|
|
value="{{ relation_id or '' }}">
|
|
<button class="btn btn-primary" type="submit">Load</button>
|
|
</div>
|
|
</form>
|
|
|
|
<!-- Error alert -->
|
|
{% if error %}
|
|
<div class="alert alert-danger alert-dismissible py-2 small" role="alert">
|
|
{{ error }}
|
|
<button type="button" class="btn-close btn-sm" data-bs-dismiss="alert"></button>
|
|
</div>
|
|
{% endif %}
|
|
<div id="js-alert" class="alert alert-danger alert-dismissible py-2 small d-none" role="alert">
|
|
<span id="js-alert-msg"></span>
|
|
<button type="button" class="btn-close btn-sm" data-bs-dismiss="alert"></button>
|
|
</div>
|
|
|
|
<!-- Route master panel (shown when a route_master relation is loaded) -->
|
|
<div id="route-master-panel" class="d-none">
|
|
<div class="mb-3">
|
|
<div class="fw-semibold" id="route-master-name"></div>
|
|
</div>
|
|
<div class="fw-semibold small mb-1">Routes</div>
|
|
<div id="route-master-list"></div>
|
|
</div>
|
|
|
|
<!-- Route info (hidden until loaded) -->
|
|
<div id="route-panel" class="d-none">
|
|
<div class="mb-3">
|
|
<div class="fw-semibold" id="route-name"></div>
|
|
</div>
|
|
|
|
<!-- From / To slots -->
|
|
<div class="mb-1">
|
|
<label class="form-label fw-semibold small mb-1">From</label>
|
|
<div class="slot-box active" id="slot-from" title="Click to make active, then click a stop">
|
|
<span class="placeholder" id="slot-from-text">Click a stop…</span>
|
|
<span class="pencil" id="slot-from-pencil">✎</span>
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label fw-semibold small mb-1">To</label>
|
|
<div class="slot-box" id="slot-to" title="Click to make active, then click a stop">
|
|
<span class="placeholder" id="slot-to-text">Click a stop…</span>
|
|
<span class="pencil d-none" id="slot-to-pencil">✎</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Clear button -->
|
|
<button class="btn btn-outline-secondary btn-sm w-100 mb-3" id="btn-clear" type="button">
|
|
✕ Clear selection
|
|
</button>
|
|
|
|
<!-- Include stops toggle -->
|
|
<div class="form-check mb-3">
|
|
<input class="form-check-input" type="checkbox" id="include-stops" checked>
|
|
<label class="form-check-label small" for="include-stops">Include stops in GeoJSON</label>
|
|
</div>
|
|
|
|
<!-- Download buttons -->
|
|
<div class="d-grid gap-2 mb-3">
|
|
<button class="btn btn-outline-secondary btn-sm disabled" id="btn-download-segment">
|
|
↓ Download segment
|
|
</button>
|
|
<button class="btn btn-outline-secondary btn-sm" id="btn-download-full">
|
|
↓ Download full route
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Other directions -->
|
|
<div id="other-directions-panel" class="d-none mb-3">
|
|
<div class="fw-semibold small mb-1">Other directions</div>
|
|
<div id="other-directions-list"></div>
|
|
</div>
|
|
|
|
<!-- Stop list -->
|
|
<div class="fw-semibold small mb-1">Stops <span id="stop-count" class="text-muted"></span></div>
|
|
<div id="stop-list"></div>
|
|
</div>
|
|
|
|
</div><!-- /sidebar -->
|
|
|
|
<!-- Map -->
|
|
<div class="col-9" id="map"></div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
|
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
|
<script>
|
|
// Relation ID injected by Flask; null if none.
|
|
const RELATION_ID = {{ relation_id | tojson }};
|
|
</script>
|
|
<script src="{{ url_for('static', filename='app.js') }}"></script>
|
|
</body>
|
|
</html>
|