04bcab2d55
Move stats computation and both panel HTML divs out of trip.html.twig
into dedicated macros/stats.html.twig and macros/cycling.html.twig.
trip.html.twig imports both at the top of {% block content %} and
calls them via stats_m.stats_panel() and cycling_m.cycling_panel().
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vgmzx8VTTTmCskSpQtsLTr
94 lines
4.2 KiB
Twig
94 lines
4.2 KiB
Twig
{% macro stats_panel(journal_entries, page, journal_count, has_gpx) %}
|
|
{% set days_on_road = 0 %}
|
|
{% if page.header.date_end is not empty %}
|
|
{% set start_ts = page.header.date_start|date('U') %}
|
|
{% set end_ts = page.header.date_end|date('U') %}
|
|
{% set days_on_road = ((end_ts - start_ts) / 86400)|round(0, 'ceil') %}
|
|
{% else %}
|
|
{% set first_ts = null %}
|
|
{% for entry in journal_entries %}
|
|
{% set ts = entry.date|date('U') %}
|
|
{% if first_ts is null or ts < first_ts %}{% set first_ts = ts %}{% endif %}
|
|
{% endfor %}
|
|
{% if first_ts is not null %}
|
|
{% set diff_seconds = "now"|date('U') - first_ts %}
|
|
{% set days_raw = (diff_seconds / 86400)|round(0, 'floor') %}
|
|
{% set days_on_road = days_raw < 1 ? 1 : days_raw %}
|
|
{% endif %}
|
|
{% endif %}
|
|
|
|
{% set seen_lower = [] %}
|
|
{% set country_display = [] %}
|
|
{% for entry in journal_entries %}
|
|
{% if entry.header.location_country is not empty %}
|
|
{% set lower = entry.header.location_country|trim|lower %}
|
|
{% if lower not in seen_lower %}
|
|
{% set seen_lower = seen_lower|merge([lower]) %}
|
|
{% set country_display = country_display|merge([entry.header.location_country|trim]) %}
|
|
{% endif %}
|
|
{% endif %}
|
|
{% endfor %}
|
|
|
|
{% set seen_city_lower = [] %}
|
|
{% set city_display = [] %}
|
|
{% for entry in journal_entries %}
|
|
{% if entry.header.location_city is not empty %}
|
|
{% set lower = entry.header.location_city|trim|lower %}
|
|
{% if lower not in seen_city_lower %}
|
|
{% set seen_city_lower = seen_city_lower|merge([lower]) %}
|
|
{% set city_display = city_display|merge([entry.header.location_city|trim]) %}
|
|
{% endif %}
|
|
{% endif %}
|
|
{% endfor %}
|
|
|
|
{% set temp_min = null %}
|
|
{% set temp_max = null %}
|
|
{% for entry in journal_entries %}
|
|
{% if entry.header.weather_temp_c is defined and entry.header.weather_temp_c is not empty %}
|
|
{% set t = entry.header.weather_temp_c %}
|
|
{% if temp_min is null or t < temp_min %}{% set temp_min = t %}{% endif %}
|
|
{% if temp_max is null or t > temp_max %}{% set temp_max = t %}{% endif %}
|
|
{% endif %}
|
|
{% endfor %}
|
|
|
|
<div id="trip-stats-block" class="trip-stats-block">
|
|
<div class="trip-panel-inner">
|
|
<div class="trip-stats-grid">
|
|
<div class="stat-block">
|
|
<span class="stat-value">{{ days_on_road }}</span>
|
|
<span class="stat-label">{{ days_on_road == 1 ? 'day' : 'days' }} on the road</span>
|
|
</div>
|
|
<div class="stat-block">
|
|
<span class="stat-value">{{ journal_count }}</span>
|
|
<span class="stat-label">{{ journal_count == 1 ? 'entry' : 'entries' }} posted</span>
|
|
</div>
|
|
<div class="stat-block">
|
|
<span class="stat-value">{{ country_display|length }}</span>
|
|
<span class="stat-label">{{ country_display|length == 1 ? 'country' : 'countries' }} visited</span>
|
|
</div>
|
|
<div class="stat-block">
|
|
<span class="stat-value">{{ city_display|length }}</span>
|
|
<span class="stat-label">{{ city_display|length == 1 ? 'city' : 'cities' }} visited</span>
|
|
</div>
|
|
<div class="stat-block">
|
|
<span class="stat-value" id="stat-distance">—</span>
|
|
<span class="stat-label">{{ has_gpx ? '🚴 km cycled' : '🧭 km roamed' }}</span>
|
|
</div>
|
|
<div class="stat-block">
|
|
{% if temp_min is not null %}
|
|
<span class="stat-value">{{ temp_min == temp_max ? temp_min : temp_min ~ ' → ' ~ temp_max }}</span>
|
|
{% else %}
|
|
<span class="stat-value">—</span>
|
|
{% endif %}
|
|
<span class="stat-label">°C range</span>
|
|
</div>
|
|
</div>
|
|
{% if country_display|length > 0 %}
|
|
<p class="trip-stats-countries">{{ country_display|join(' · ') }}</p>
|
|
{% endif %}
|
|
<p class="trip-stats-note">{{ has_gpx ? 'Distance based on GPS track data.' : 'Distance is approximate — straight lines between entry locations.' }}</p>
|
|
<button class="trip-panel-close" data-toggle="trip-stats-toggle">↑ Close stats</button>
|
|
</div>
|
|
</div>
|
|
{% endmacro %}
|