Tidy results table layout and centralise connection defaults

- Redesign results table from 8 columns to 4 (National Rail, Transfer,
  Eurostar, Total), making GWR and Eurostar legs consistent with each other
- Move CET label next to Paris arrival time; show duration · train number
  on one line below
- Move "Too early" label into the National Rail column for unreachable rows
- Remove horizontal scrollbar (drop card-scroll / overflow-x: auto)
- Add DEFAULT_MIN_CONNECTION / DEFAULT_MAX_CONNECTION to config/default.py
  (70 / 150 min); remove all hardcoded fallback values from app.py and
  templates
- Redirect to clean URL when both connection params equal their defaults;
  omit params from all generated links when at default values

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Edward Betts 2026-04-10 13:11:26 +01:00
parent 6ab4534051
commit 8433252cae
5 changed files with 109 additions and 87 deletions

View file

@ -15,10 +15,10 @@
{{ departure_station_name }} &rarr; {{ destination }}
</h2>
<div class="date-nav">
<a href="{{ url_for('results', station_crs=station_crs, slug=slug, travel_date=prev_date, min_connection=min_connection, max_connection=max_connection) }}"
<a href="{{ url_for('results', station_crs=station_crs, slug=slug, travel_date=prev_date, min_connection=url_min_connection, max_connection=url_max_connection) }}"
class="btn-nav">&larr; Prev</a>
<strong>{{ travel_date_display }}</strong>
<a href="{{ url_for('results', station_crs=station_crs, slug=slug, travel_date=next_date, min_connection=min_connection, max_connection=max_connection) }}"
<a href="{{ url_for('results', station_crs=station_crs, slug=slug, travel_date=next_date, min_connection=url_min_connection, max_connection=url_max_connection) }}"
class="btn-nav">Next &rarr;</a>
</div>
<div class="switcher-section">
@ -30,7 +30,7 @@
{% else %}
<a
class="chip-link"
href="{{ url_for('results', station_crs=station_crs, slug=destination_slug, travel_date=travel_date, min_connection=min_connection, max_connection=max_connection) }}"
href="{{ url_for('results', station_crs=station_crs, slug=destination_slug, travel_date=travel_date, min_connection=url_min_connection, max_connection=url_max_connection) }}"
>{{ destination_name }}</a>
{% endif %}
{% endfor %}
@ -64,9 +64,13 @@
</div>
<script>
function applyConnectionFilter() {
var min = document.getElementById('min_conn_select').value;
var max = document.getElementById('max_conn_select').value;
window.location = '{{ url_for('results', station_crs=station_crs, slug=slug, travel_date=travel_date) }}?min_connection=' + min + '&max_connection=' + max;
var min = parseInt(document.getElementById('min_conn_select').value);
var max = parseInt(document.getElementById('max_conn_select').value);
var base = '{{ url_for('results', station_crs=station_crs, slug=slug, travel_date=travel_date) }}';
var params = [];
if (min !== {{ default_min_connection }}) params.push('min_connection=' + min);
if (max !== {{ default_max_connection }}) params.push('max_connection=' + max);
window.location = params.length ? base + '?' + params.join('&') : base;
}
</script>
<p class="card-meta">
@ -90,17 +94,13 @@
</div>
{% if trips or unreachable_morning_services %}
<div class="card card-scroll">
<div class="card">
<table class="results-table">
<thead>
<tr>
<th class="nowrap">{{ departure_station_name }}</th>
<th class="nowrap">Paddington</th>
<th class="nowrap">GWR Fare</th>
<th class="nowrap">National Rail</th>
<th class="col-transfer nowrap">Transfer</th>
<th class="nowrap">Depart STP</th>
<th>{{ destination }}</th>
<th class="nowrap">ES Std</th>
<th class="nowrap">Eurostar</th>
<th class="nowrap">Total</th>
</tr>
</thead>
@ -128,51 +128,50 @@
{% endif %}
<tr class="{{ row_class }}">
{% if row.row_type == 'trip' %}
<td class="font-bold">
{{ row.depart_bristol }}
{% if row.headcode %}<br><span class="text-xs font-normal text-muted">{{ row.headcode }}</span>{% endif %}
</td>
<td>
{{ row.arrive_paddington }}
<span class="font-bold nowrap">{{ row.depart_bristol }} &rarr; {{ row.arrive_paddington }}</span>
<span class="text-sm text-muted nowrap">({{ row.gwr_duration }})</span>
{% if row.arrive_platform %}<br><span class="text-xs text-muted">Plat {{ row.arrive_platform }}</span>{% endif %}
</td>
<td class="nowrap">
{% if row.headcode or row.arrive_platform %}
<br><span class="text-xs text-muted">
{%- if row.headcode %}{{ row.headcode }}{% endif %}
{%- if row.headcode and row.arrive_platform %} &middot; {% endif %}
{%- if row.arrive_platform %}Plat {{ row.arrive_platform }}{% endif %}
</span>
{% endif %}
{% if row.ticket_price is not none %}
£{{ "%.2f"|format(row.ticket_price) }}
<br><span class="text-xs text-muted">{{ row.ticket_name }}</span>
<br><span class="text-sm font-bold">£{{ "%.2f"|format(row.ticket_price) }}</span>
<span class="text-xs text-muted">{{ row.ticket_name }}</span>
{% else %}
<span class="text-muted">&ndash;</span>
<br><span class="text-sm text-muted">&ndash;</span>
{% endif %}
</td>
<td class="col-transfer nowrap" style="color:#4a5568">
{{ row.connection_duration }}{% if row.connection_minutes < 80 %} <span title="Tight connection">⚠️</span>{% endif %}
<td class="col-transfer" style="color:#4a5568">
<span class="nowrap">{{ row.connection_duration }}{% if row.connection_minutes < 80 %} <span title="Tight connection">⚠️</span>{% endif %}</span>
{% if row.circle_services %}
{% set c = row.circle_services[0] %}
<br><span class="text-xs text-muted">Circle {{ c.depart }} → KX {{ c.arrive_kx }}</span>
<br><span class="text-xs text-muted nowrap">Circle {{ c.depart }} → KX {{ c.arrive_kx }}</span>
{% if row.circle_services | length > 1 %}
{% set c2 = row.circle_services[1] %}
<br><span class="text-xs text-muted" style="opacity:0.7">next {{ c2.depart }} → KX {{ c2.arrive_kx }}</span>
<br><span class="text-xs text-muted nowrap" style="opacity:0.7">next {{ c2.depart }} → KX {{ c2.arrive_kx }}</span>
{% endif %}
{% endif %}
</td>
<td class="font-bold">
{{ row.depart_st_pancras }}
{% if row.train_number %}<br><span class="text-xs font-normal text-muted">{% for part in row.train_number.split(' + ') %}<span class="nowrap">{{ part }}</span>{% if not loop.last %} + {% endif %}{% endfor %}</span>{% endif %}
</td>
<td>
{{ row.arrive_destination }}
<span class="font-normal text-muted" style="font-size:0.85em">(CET)</span>
{% if row.eurostar_duration %}<br><span class="text-sm text-muted nowrap">({{ row.eurostar_duration }})</span>{% endif %}
</td>
<td class="nowrap">
<span class="font-bold nowrap">{{ row.depart_st_pancras }} &rarr; {{ row.arrive_destination }} <span class="font-normal text-muted" style="font-size:0.85em">(CET)</span></span>
{% if row.eurostar_duration or row.train_number %}
<br><span class="text-xs text-muted">
{%- if row.eurostar_duration %}<span class="nowrap">({{ row.eurostar_duration }})</span>{% endif %}
{%- if row.eurostar_duration and row.train_number %} &middot; {% endif %}
{%- if row.train_number %}{% for part in row.train_number.split(' + ') %}<span class="nowrap">{{ part }}</span>{% if not loop.last %} + {% endif %}{% endfor %}{% endif %}
</span>
{% endif %}
{% if row.eurostar_price is not none %}
£{{ "%.2f"|format(row.eurostar_price) }}
<br><span class="text-sm font-bold">£{{ "%.2f"|format(row.eurostar_price) }}</span>
{% if row.eurostar_seats is not none %}
<br><span class="text-xs text-muted">{{ row.eurostar_seats }} at this price</span>
<span class="text-xs text-muted">{{ row.eurostar_seats }} at this price</span>
{% endif %}
{% else %}
<span class="text-muted">&ndash;</span>
<br><span class="text-sm text-muted">&ndash;</span>
{% endif %}
</td>
<td class="font-bold nowrap">
@ -188,32 +187,29 @@
{% endif %}
</td>
{% else %}
<td class="font-bold">&mdash;</td>
<td>&mdash;</td>
<td>&mdash;</td>
<td class="col-transfer">&mdash;</td>
<td class="font-bold">
{{ row.depart_st_pancras }}
{% if row.train_number %}<br><span class="text-xs font-normal text-dimmed">{% for part in row.train_number.split(' + ') %}<span class="nowrap">{{ part }}</span>{% if not loop.last %} + {% endif %}{% endfor %}</span>{% endif %}
</td>
<td>
{{ row.arrive_destination }}
<span class="font-normal text-dimmed" style="font-size:0.85em">(CET)</span>
{% if row.eurostar_duration %}<br><span class="text-sm text-dimmed nowrap">({{ row.eurostar_duration }})</span>{% endif %}
<span class="text-dimmed text-sm" title="Too early to reach from {{ departure_station_name }}">Too early</span>
</td>
<td class="nowrap">
<td class="col-transfer text-dimmed">&mdash;</td>
<td>
<span class="font-bold nowrap text-dimmed">{{ row.depart_st_pancras }} &rarr; {{ row.arrive_destination }} <span class="font-normal" style="font-size:0.85em">(CET)</span></span>
{% if row.eurostar_duration or row.train_number %}
<br><span class="text-xs text-dimmed">
{%- if row.eurostar_duration %}<span class="nowrap">({{ row.eurostar_duration }})</span>{% endif %}
{%- if row.eurostar_duration and row.train_number %} &middot; {% endif %}
{%- if row.train_number %}{% for part in row.train_number.split(' + ') %}<span class="nowrap">{{ part }}</span>{% if not loop.last %} + {% endif %}{% endfor %}{% endif %}
</span>
{% endif %}
{% if row.eurostar_price is not none %}
<span class="text-dimmed">£{{ "%.2f"|format(row.eurostar_price) }}</span>
<br><span class="text-sm text-dimmed">£{{ "%.2f"|format(row.eurostar_price) }}</span>
{% if row.eurostar_seats is not none %}
<br><span class="text-xs text-dimmed">{{ row.eurostar_seats }} at this price</span>
<span class="text-xs text-dimmed">{{ row.eurostar_seats }} at this price</span>
{% endif %}
{% else %}
<span class="text-dimmed">&ndash;</span>
<br><span class="text-sm text-dimmed">&ndash;</span>
{% endif %}
</td>
<td class="font-bold">
<span title="Too early to reach from {{ departure_station_name }}" class="text-dimmed nowrap">Too early</span>
</td>
<td class="text-dimmed">&mdash;</td>
{% endif %}
</tr>
{% endfor %}