cleanup
This commit is contained in:
parent
b7ec6a292f
commit
9297006ab3
58 changed files with 2032 additions and 2074 deletions
13
web/templates/index.html
Normal file
13
web/templates/index.html
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<h1>SEPTASTIC!</h1>
|
||||
<p>
|
||||
<i>A fantastic way to ride SEPTA</i>
|
||||
</p>
|
||||
<p style="margin-top: 25px;">
|
||||
SEPTASTIC is a website and (a soon to be) mobile app. Its purpose is to provide
|
||||
information about how to ride SEPTA (and connecting transit authorities) in a
|
||||
quick and information-rich manner.
|
||||
</p>
|
||||
<p style="margin-top: 25px; margin-bottom: 25px;">
|
||||
Currently, all this website has is <a href="/routes">timetables for every
|
||||
SEPTA route</a>. More to come soon!
|
||||
</p>
|
||||
103
web/templates/layout.html
Normal file
103
web/templates/layout.html
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
{% if let Some(title) = page_title %}
|
||||
<title>{{ title }} | SEPTASTIC</title>
|
||||
{% else %}
|
||||
<title>SEPTASTIC</title>
|
||||
{% endif %}
|
||||
{% if let Some(desc) = page_desc %}
|
||||
<meta name="{{ desc }}" />
|
||||
{% else %}
|
||||
<meta name="SEPTASTIC" />
|
||||
{% endif %}
|
||||
<link rel="stylesheet" href="/assets/style.css">
|
||||
<link rel="icon" type="image/x-icon" href="/assets/favicon.ico">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
</head>
|
||||
<script src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.8/dist/htmx.min.js"></script>
|
||||
<script>
|
||||
window.onload = function () {
|
||||
setTimeout(() => {
|
||||
const perfData = window.performance.timing;
|
||||
const pageLoadTime = perfData.loadEventEnd - perfData.navigationStart;
|
||||
|
||||
const loadTimeElement = document.getElementById('js_load_time');
|
||||
loadTimeElement.textContent += ` ${pageLoadTime}ms`;
|
||||
}, 0); // Minimal delay to wait for `loadEventEnd` to be populated
|
||||
};
|
||||
</script>
|
||||
<noscript>
|
||||
<style>
|
||||
.js-only {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
</noscript>
|
||||
<style>
|
||||
.silverliner-svg {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 200px; /* Fixed height matching the viewBox */
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
{% if widescreen %}
|
||||
<div class="body">
|
||||
{% else %}
|
||||
<div class="body body-small">
|
||||
{% endif %}
|
||||
<div style="background-color: #ff0000;
|
||||
color: #ffffff;
|
||||
font-size: .7em;
|
||||
padding: 5px;
|
||||
margin-bottom: 10px;
|
||||
margin-top: 10px">This website is not run by SEPTA. Data may be inaccurate.</div>
|
||||
<nav>
|
||||
<div style="display: flex; justify-content: space-between;">
|
||||
<div>
|
||||
<a class="nav-link" href="/">[ Home ]</a>
|
||||
<a class="nav-link" href="/routes">[ Routes ]</a>
|
||||
<a class="nav-link" href="/stops">[ Stops ]</a>
|
||||
</div>
|
||||
<div></div>
|
||||
</div>
|
||||
</nav>
|
||||
<hr />
|
||||
{{ content|safe }}
|
||||
<footer>
|
||||
<hr />
|
||||
<div style="display: flex; justify-content: space-between;">
|
||||
<div>
|
||||
<p style="margin-bottom: 0px; margin-top:0px;">
|
||||
<b>SEPTASTIC!</b>
|
||||
</p>
|
||||
<p style="margin-bottom: 0px;margin-top: 0px;">
|
||||
<small>Copyright © <a href="https://nickorlow.com">Nicholas Orlowsky</a> 2025</small>
|
||||
</p>
|
||||
{% if let Some(load_time) = load_time_ms %}
|
||||
<p style="marin-top: 5px; color: #555555;">
|
||||
<small><i>Data loaded in {{ *load_time | format_load_time }}</i></small>
|
||||
</p>
|
||||
{% endif %}
|
||||
<p class="js-only" style="marin-top: 5px; color: #555555;">
|
||||
<small><i id="js_load_time">Total load time</i></small>
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
{% if widescreen %}
|
||||
<a href="?widescreen=false"><small>[ disable widescreen ]</small></a>
|
||||
{% else %}
|
||||
<a href="?widescreen=true"><small>[ enable widescreen ]</small></a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<noscript>
|
||||
<p style="margin-top: 10px;">
|
||||
<small>[!] You do not have JavaScript enabled. Many features will be missing/broken!</small>
|
||||
</p>
|
||||
</noscript>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
138
web/templates/route.html
Normal file
138
web/templates/route.html
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
{%- import "route_symbol.html" as scope -%}
|
||||
<script>
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const scrollToNextColumn = (directionId) => {
|
||||
const target = document.getElementById("next-col-" + directionId);
|
||||
if (target) {
|
||||
const scrollContainer = target.closest(".tscroll");
|
||||
const firstCol = scrollContainer.querySelector("th:first-child");
|
||||
const firstColWidth = firstCol ? firstCol.offsetWidth : 0;
|
||||
|
||||
// Get the target's position relative to the scroll container
|
||||
const targetLeft = target.offsetLeft;
|
||||
|
||||
// Scroll so the target appears right after the sticky column
|
||||
scrollContainer.scrollLeft = targetLeft - firstColWidth;
|
||||
}
|
||||
};
|
||||
|
||||
document.querySelectorAll("details[data-direction-id]").forEach(details => {
|
||||
const directionId = details.getAttribute("data-direction-id");
|
||||
|
||||
// Scroll immediately if details is already open
|
||||
if (details.open) {
|
||||
setTimeout(() => scrollToNextColumn(directionId), 50);
|
||||
}
|
||||
|
||||
// Also scroll when details is opened
|
||||
details.addEventListener("toggle", () => {
|
||||
if (details.open) {
|
||||
setTimeout(() => scrollToNextColumn(directionId), 50);
|
||||
}
|
||||
});
|
||||
});
|
||||
document.querySelectorAll(".train-direction-table").forEach((table) => {
|
||||
table.addEventListener("click", (e) => {
|
||||
const cell = e.target.closest("td, th");
|
||||
if (!cell) return;
|
||||
|
||||
// Clear previous highlights
|
||||
table.querySelectorAll("tr").forEach(row => row.classList.remove("highlight-row"));
|
||||
table.querySelectorAll("td, th").forEach(c => c.classList.remove("highlight-col"));
|
||||
|
||||
const row = cell.parentNode;
|
||||
const colIndex = Array.from(cell.parentNode.children).indexOf(cell);
|
||||
|
||||
// If it's the first column (row header)
|
||||
if (cell.cellIndex === 0 && cell.tagName === "TD") {
|
||||
row.classList.add("highlight-row");
|
||||
}
|
||||
// If it's a column header
|
||||
else if (row.parentNode.tagName === "THEAD") {
|
||||
table.querySelectorAll("tr").forEach(r => {
|
||||
const cell = r.children[colIndex];
|
||||
if (cell) cell.classList.add("highlight-col");
|
||||
});
|
||||
}
|
||||
// If it's a center cell
|
||||
else {
|
||||
row.classList.add("highlight-row");
|
||||
table.querySelectorAll("tr").forEach(r => {
|
||||
const cell = r.children[colIndex];
|
||||
if (cell) cell.classList.add("highlight-col");
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<div style="display: flex; align-items: center;">
|
||||
{% call scope::route_symbol(route) %}
|
||||
{% endcall %}
|
||||
<h1 style="margin-left: 15px;">{{ route.name }}</h1>
|
||||
</div>
|
||||
{% for timetable in timetables %}
|
||||
<details style="margin-top: 15px"
|
||||
data-direction-id="{{ timetable.direction.direction }}">
|
||||
<summary>
|
||||
<div style="display: inline-block;">
|
||||
<h3>{{ timetable.direction.direction | capitalize }} to</h3>
|
||||
<h2>{{ timetable.direction.direction_destination }}</h2>
|
||||
</div>
|
||||
</summary>
|
||||
<div class="tscroll">
|
||||
<table class="train-direction-table" style="margin-top: 5px;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Stop</th>
|
||||
{% for trip_id in timetable.trip_ids %}
|
||||
{% if let Some(next_id_v) = timetable.next_id %}
|
||||
{% if next_id_v == trip_id %}
|
||||
<th class="next-col" id="next-col-{{ timetable.direction.direction }}">
|
||||
{% else %}
|
||||
<th>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<th>
|
||||
{% endif %}
|
||||
{{ trip_id }}
|
||||
</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for row in timetable.rows %}
|
||||
{% if let Some(filter_stop_v) = filter_stops %}
|
||||
{% if !filter_stop_v.contains(&row.stop_id) %}
|
||||
{% continue %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<tr>
|
||||
<td>{{ row.stop_name }}</td>
|
||||
{% for time in row.times %}
|
||||
{% if let Some(t) = time %}
|
||||
{% let live_o = timetable.tracking_data[loop.index0] %}
|
||||
{% if let Tracked(live) = live_o %}
|
||||
{% let time = (t + (live.delay * 60.0) as i64) %}
|
||||
<td style="background-color: #003300">
|
||||
<span style="color: #22bb22">{{ time | format_time }}</span>
|
||||
</td>
|
||||
{% elif let TripTracking::Cancelled = live_o %}
|
||||
<td style="color: #ff0000">
|
||||
<s>{{ t | format_time }}</s>
|
||||
</td>
|
||||
{% else %}
|
||||
<td>{{ t | format_time }}</td>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<td></td>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</details>
|
||||
{% endfor %}
|
||||
13
web/templates/route_symbol.html
Normal file
13
web/templates/route_symbol.html
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{% macro route_symbol(route) %}
|
||||
{% match route.route_type %}
|
||||
{% when libseptastic::route::RouteType::Trolley | libseptastic::route::RouteType::SubwayElevated %}
|
||||
<div class="metro-container bg-{{ route.short_name }}">{{ route.short_name }}</div>
|
||||
{% endwhen %}
|
||||
{% when libseptastic::route::RouteType::RegionalRail %}
|
||||
<div class="rr-container">{{ route.short_name }}</div>
|
||||
{% endwhen %}
|
||||
{% when libseptastic::route::RouteType::Bus | libseptastic::route::RouteType::TracklessTrolley %}
|
||||
<div class="bus-container">{{ route.short_name }}</div>
|
||||
{% endwhen %}
|
||||
{% endmatch %}
|
||||
{% endmacro %}
|
||||
93
web/templates/routes.html
Normal file
93
web/templates/routes.html
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
<h1>Routes</h1>
|
||||
<p>Click on a route to see details and a schedule. Schedules in prevailing local time.</p>
|
||||
<fieldset>
|
||||
<legend>
|
||||
<h2>Regional Rail</h2>
|
||||
</legend>
|
||||
<p style="margin-top: 10px; margin-bottom: 10px;">For infrequent rail service to suburban locations</p>
|
||||
{% for route in rr_routes %}
|
||||
<a href="/route/{{ route.id }}"
|
||||
style="display: flex;
|
||||
justify-content: space-between">
|
||||
<p class="line-link">
|
||||
[ <b>{{ format!("{:7}", route.short_name) }}:</b> {{ route.name }}
|
||||
</p>
|
||||
<p>]</p>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>
|
||||
<h2>Metro</h2>
|
||||
</legend>
|
||||
<p style="margin-top: 10px; margin-bottom: 10px;">
|
||||
For frequent rail service within Philadelphia and suburban locations
|
||||
</p>
|
||||
<div class="lines-label"
|
||||
style="font-weight: bold;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between">
|
||||
<p>[ Subway/Elevated</p>
|
||||
<p>]</p>
|
||||
</div>
|
||||
{% for route in subway_routes %}
|
||||
<a href="/route/{{ route.id }}"
|
||||
style="display: flex;
|
||||
justify-content: space-between">
|
||||
<p class="line-link">
|
||||
[ <b>{{ format!("{:7}", route.short_name) }}:</b> {{ route.name }}
|
||||
</p>
|
||||
<p>]</p>
|
||||
</a>
|
||||
{% endfor %}
|
||||
<div class="lines-label"
|
||||
style="font-weight: bold;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between">
|
||||
<p>[ Trolleys</p>
|
||||
<p>]</p>
|
||||
</div>
|
||||
{% for route in trolley_routes %}
|
||||
<a href="/route/{{ route.id }}"
|
||||
style="display: flex;
|
||||
justify-content: space-between">
|
||||
<p class="line-link">
|
||||
[ <b>{{ format!("{:7}", route.short_name) }}:</b> {{ route.name }}
|
||||
</p>
|
||||
<p>]</p>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>
|
||||
<h2>Bus</h2>
|
||||
</legend>
|
||||
<p style="margin-top: 10px; margin-bottom: 10px;">
|
||||
For service of varying frequency within SEPTA's entire service area
|
||||
</p>
|
||||
{% for route in bus_routes %}
|
||||
<a href="/route/{{ route.id }}"
|
||||
style="display: flex;
|
||||
justify-content: space-between">
|
||||
<p class="line-link">
|
||||
[ <b>{{ format!("{:7}", route.short_name) }}:</b> {{ route.name }}
|
||||
</p>
|
||||
<p>]</p>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</fieldset>
|
||||
<style>
|
||||
.line-link, .lines-label {
|
||||
white-space: pre;
|
||||
margin-top: 3px;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.lines-label {
|
||||
color: #ffffff;
|
||||
background-color: #000000;
|
||||
width: max-content;
|
||||
}
|
||||
</style>
|
||||
139
web/templates/stop.html
Normal file
139
web/templates/stop.html
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
{%- import "route_symbol.html" as scope -%}
|
||||
{%- import "stop_table.html" as stop_table -%}
|
||||
<div style="display: flex; align-items: center;">
|
||||
<h1>{{ stop.name }}</h1>
|
||||
</div>
|
||||
<p>With service available on:</p>
|
||||
<div style="display: flex;
|
||||
justify-content: start;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
flex-wrap: wrap;
|
||||
gap: 5px">
|
||||
{% for route in routes %}
|
||||
<div style="margin-right: 5px">
|
||||
{% call scope::route_symbol(route) %}
|
||||
{% endcall %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<details>
|
||||
<summary>
|
||||
<p style="font-weight: bold; font-size: large;">Filters</p>
|
||||
</summary>
|
||||
<form hx-trigger="submit"
|
||||
hx-get="/stop/{{ stop.id }}/table"
|
||||
hx-target="#nta-table"
|
||||
hx-swap="outerHTML"
|
||||
hx-push-url="/stop/{{ stop.id }}">
|
||||
<div style="margin: 5px; padding: 10px; background-color: #eee;">
|
||||
<div style="display: flex; flex-wrap: wrap;">
|
||||
<fieldset style="flex-grow: 1;">
|
||||
<legend>Route</legend>
|
||||
{% for route in routes %}
|
||||
{% for dir in route.directions %}
|
||||
{% if let Some(fil) = filters && let Some(rts) = fil.routes %}
|
||||
{% let route_filter_id = format!("{},{}", route.id, dir.direction) %}
|
||||
<input type="checkbox"
|
||||
class="route-checkbox"
|
||||
name="routes"
|
||||
id="{{ route.id }},{{ dir.direction }}"
|
||||
value="{{ route.id }},{{ dir.direction }}"
|
||||
checked="{{ rts.contains(&*route_filter_id) }}">
|
||||
{% else %}
|
||||
<input type="checkbox"
|
||||
class="route-checkbox"
|
||||
name="routes"
|
||||
id="{{ route.id }},{{ dir.direction }}"
|
||||
value="{{ route.id }},{{ dir.direction }}"
|
||||
checked="true">
|
||||
{% endif %}
|
||||
<label for="{{ route.id }},{{ dir.direction }}">
|
||||
<b>{{ route.short_name }}</b>: {{ dir.direction_destination }}
|
||||
</label>
|
||||
<br>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
<input type="checkbox"
|
||||
id="master"
|
||||
hx-on:click="document.querySelectorAll('.route-checkbox').forEach(c => c.checked = this.checked)">
|
||||
<label for="master">Select/Deselect All</label>
|
||||
</fieldset>
|
||||
<div style="flex-grow: 1;">
|
||||
<fieldset>
|
||||
<legend>Ride Options</legend>
|
||||
{% if let Some(fil) = filters && let Some(lt) = fil.live_tracked %}
|
||||
<input type="checkbox"
|
||||
name="live_tracked"
|
||||
id="live_tracked"
|
||||
value="true"
|
||||
checked="{{ lt }}">
|
||||
{% else %}
|
||||
<input type="checkbox"
|
||||
name="live_tracked"
|
||||
id="live_tracked"
|
||||
value="true"
|
||||
checked="true">
|
||||
{% endif %}
|
||||
<label for="live-tracked">Live Tracked</label>
|
||||
<br>
|
||||
{% if let Some(fil) = filters && let Some(sc) = fil.scheduled %}
|
||||
<input type="checkbox"
|
||||
name="scheduled"
|
||||
id="scheduled"
|
||||
value="true"
|
||||
checked="{{ sc }}">
|
||||
{% else %}
|
||||
<input type="checkbox"
|
||||
name="scheduled"
|
||||
id="scheduled"
|
||||
value="true"
|
||||
checked="true">
|
||||
{% endif %}
|
||||
<label for="scheduled">Scheduled</label>
|
||||
<br>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>Crowding</legend>
|
||||
{% for avail in SeatAvailability::iter() %}
|
||||
{% if let Some(fil) = filters && let Some(crd) = fil.crowding %}
|
||||
<input type="checkbox"
|
||||
name="crowding"
|
||||
id="{{ avail.to_string() }}"
|
||||
value="{{ avail.to_string() }}"
|
||||
checked="{{ crd.contains(&avail) }}">
|
||||
{% else %}
|
||||
<input type="checkbox"
|
||||
name="crowding"
|
||||
id="{{ avail.to_string() }}"
|
||||
value="{{ avail.to_string() }}"
|
||||
checked="true">
|
||||
{% endif %}
|
||||
<label for="{{ avail.to_string() }}">{{ avail.to_human_string() }}</label>
|
||||
<br>
|
||||
{% endfor %}
|
||||
{% if let Some(fil) = filters && let Some(uc) = fil.unknown_crowding %}
|
||||
<input type="checkbox"
|
||||
name="unknown_crowding"
|
||||
id="unknown_crowding"
|
||||
value="true"
|
||||
checked="{{ uc }}">
|
||||
{% else %}
|
||||
<input type="checkbox"
|
||||
name="unknown_crowding"
|
||||
id="unknown_crowding"
|
||||
value="true"
|
||||
checked="true">
|
||||
{% endif %}
|
||||
<label for="scheduled">Unknown</label>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
<input type="submit" value="Apply">
|
||||
</div>
|
||||
</form>
|
||||
</details>
|
||||
<div style="overflow-x: scroll; max-width: 100%;">
|
||||
{% call stop_table::stop_table(trips, current_time, stop.id, query_str) %}
|
||||
{% endcall %}
|
||||
</div>
|
||||
69
web/templates/stop_table.html
Normal file
69
web/templates/stop_table.html
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
{%- import "route_symbol.html" as scope -%}
|
||||
{% macro stop_table(trips, current_time, stop_id, query_str) %}
|
||||
<div id="nta-table"
|
||||
hx-get="/stop/{{ stop_id }}/table?{{ query_str }}"
|
||||
hx-trigger="every 5s"
|
||||
hx-swap="outer-html">
|
||||
<table class="train-direction-table">
|
||||
<tr>
|
||||
<th>ROUTE</th>
|
||||
<th>DESTINATION</th>
|
||||
<th>BOARDING AREA</th>
|
||||
<th>TIME</th>
|
||||
<th>VEHICLE</th>
|
||||
<th>TRIP</th>
|
||||
<th>CROWDING</th>
|
||||
</tr>
|
||||
{% for trip in trips %}
|
||||
<tr>
|
||||
<td>
|
||||
{% call scope::route_symbol(trip.trip.route) %}
|
||||
{% endcall %}
|
||||
</td>
|
||||
<td>
|
||||
<p>{{ trip.trip.direction.direction_destination }}</p>
|
||||
</td>
|
||||
<td>
|
||||
<p>{{ trip.perspective_stop.platform.name }}</p>
|
||||
</td>
|
||||
{% if let Tracked(tracked_trip) = trip.trip.tracking_data %}
|
||||
<td style="color: #008800">
|
||||
<p style="font-size: small;">{{ &trip.perspective_stop.get_arrival_time(&tracked_trip) | format_time }}</p>
|
||||
<p style="font-size: x-small; font-style: italic;">
|
||||
{{ ( trip.perspective_stop.get_arrival_time(&tracked_trip) - current_time) / 60 }} mins
|
||||
</p>
|
||||
<p style="font-size: x-small; font-style: italic;">{{ tracked_trip.delay.round() }} late</p>
|
||||
</td>
|
||||
{% else %}
|
||||
<td>
|
||||
<p style="font-size: small;">{{ trip.perspective_stop.arrival_time | format_time }}</p>
|
||||
<p style="font-size: x-small; font-style: italic;">
|
||||
{{ (trip.perspective_stop.arrival_time - current_time) / 60 }} mins
|
||||
</p>
|
||||
</td>
|
||||
{% endif %}
|
||||
{% if let Tracked(tracked_trip) = trip.trip.tracking_data %}
|
||||
<td>{{ tracked_trip.vehicle_ids.join(", ") }}</td>
|
||||
{% else %}
|
||||
<td>-</td>
|
||||
{% endif %}
|
||||
<td>{{ trip.trip.trip_id }}</td>
|
||||
{% if let Tracked(tracked_trip) = trip.trip.tracking_data %}
|
||||
{% if let Some(seat_avail) = tracked_trip.seat_availability %}
|
||||
<td>{{ seat_avail.to_human_string() }}</td>
|
||||
{% else %}
|
||||
<td>N/A</td>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<td>-</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tr>
|
||||
<td colspan="7">
|
||||
<p>Updated at: {{ current_time | format_time_with_seconds }}</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
3
web/templates/stop_table_impl.html
Normal file
3
web/templates/stop_table_impl.html
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{%- import "stop_table.html" as stop_table -%}
|
||||
{% call stop_table::stop_table(trips, current_time, stop_id, query_str) %}
|
||||
{% endcall %}
|
||||
29
web/templates/stops.html
Normal file
29
web/templates/stops.html
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<h1>Stops</h1>
|
||||
<p>Click on a route to see details and a schedule. Schedules in prevailing local time.</p>
|
||||
<fieldset>
|
||||
<legend>
|
||||
<h2>Transit Centers</h2>
|
||||
</legend>
|
||||
<p style="margin-top: 10px; margin-bottom: 10px;">Hubs to connect between different modes of transit</p>
|
||||
{% for stop in tc_stops %}
|
||||
<a href="/stop/{{ stop.id }}"
|
||||
style="display: flex;
|
||||
justify-content: space-between">
|
||||
<p class="line-link">[ {{ stop.name }}</p>
|
||||
<p>]</p>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</fieldset>
|
||||
<style>
|
||||
.line-link, .lines-label {
|
||||
white-space: pre;
|
||||
margin-top: 3px;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.lines-label {
|
||||
color: #ffffff;
|
||||
background-color: #000000;
|
||||
width: max-content;
|
||||
}
|
||||
</style>
|
||||
Loading…
Add table
Add a link
Reference in a new issue