Files
intotheeast-com/docs/working/specs/2026-06-23-template-refactor-design.md
T
m038 aab783384f docs: add template refactor design spec (Milestone 2)
Three Twig macros (stats panel, cycling panel, date range), five template
modifications, and two latent bug fixes in inactive templates.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vgmzx8VTTTmCskSpQtsLTr
2026-06-23 23:38:54 +02:00

116 lines
6.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Template Refactor Design
**Date:** 2026-06-23
**Status:** Approved for implementation
## Problem
`trip.html.twig` (386 lines) mixes three concerns: 50 lines of stats computation, 80 lines of panel HTML, and 130 lines of map/JS logic. Changes to stats logic or panel structure require reading through all three to understand what's where.
Four templates also duplicate the `map_entries` collection loop, and date range formatting is independently reimplemented in `story.html.twig` and `stories.html.twig`. Two inactive templates (`map.html.twig`, `feed-map.html.twig`) have bugs that would surface as soon as they're activated.
## Goals
- Reduce `trip.html.twig` from 386 to ~260 lines by extracting stats computation and panel HTML to macros
- Extract date range formatting to one macro used by both story templates
- Fix two latent bugs in inactive templates (wrong DOMContentLoaded timing, CSS not making it into `<head>`)
- Zero visual change — this is structural only
## Non-goals
- Moving stats computation to PHP (performance difference at 60-80 entries is ~5-20ms, only on uncached loads; not worth a plugin)
- Extracting `map_entries` loops to a macro (Twig macros output HTML, not data; they can't return arrays)
- Changing any JS logic — DOMContentLoaded wrappers are structural fixes only (no logic changes)
- Fixing `dailies.html.twig`'s CDN PhotoSwipe — Milestone 1 leftover, inactive page, separate concern
- Any visual layout changes
## Key constraint: Twig macros vs data
Twig macros produce rendered HTML output, not return values. This means:
- **Stats + cycling panels** → good macro candidates (compute internally, render HTML)
- **Date range string** → good macro candidate (outputs text)
- **`map_entries` array** → cannot be a macro; each template's loop stays inline
## Architecture
### New macros
**`templates/macros/stats.html.twig`**
Signature: `stats_panel(journal_entries, page, journal_count, has_gpx)`
Computes internally: `days_on_road`, `country_display[]`, `city_display[]`, `temp_min`, `temp_max`. Outputs the full `<div id="trip-stats-block">` HTML with Twig-computed values baked in and JS placeholder IDs (`id="stat-distance"`) for the distance stat filled by JS after page load.
**`templates/macros/cycling.html.twig`**
Signature: `cycling_panel()`
No computation needed — all values are JS placeholders (`id="cyc-distance"` etc.). Outputs `<div id="trip-cycling-block">` HTML.
**`templates/macros/date-range.html.twig`**
Signature: `format_date_range(start_date, end_date)`
Condensed smart formatting extracted from `story.html.twig`: same month → `1215 Jun 2026`; same year → `12 Jun 3 Jul 2026`; different years → `28 Dec 2025 3 Jan 2026`. If `end_date` is empty or equals `start_date`, outputs a single date. Used by both `story.html.twig` and `stories.html.twig` (unifies the two currently divergent implementations).
### Data flow in trip.html.twig
```twig
{% import 'macros/stats.html.twig' as m %}
{% import 'macros/cycling.html.twig' as mc %}
{# Data building stays inline (feeds both HTML and JS) #}
{% set journal_entries = ... %}
{% set gpx_urls = [...] %}
{% set gps_points = [...] %} {# for STATS_GPS in <script> #}
{% set map_entries = [...] %} {# for TRIP_ENTRIES in <script> #}
{# HTML: macro calls replace 130 lines of computation + panel HTML #}
{{ m.stats_panel(journal_entries, page, journal_count, has_gpx) }}
{% if has_gpx %}{{ mc.cycling_panel() }}{% endif %}
{# JS block: unchanged, uses Twig vars already in scope #}
<script>
var TRIP_ENTRIES = {{ map_entries|json_encode|raw }};
var STATS_GPS = {{ gps_points|json_encode|raw }};
var GPX_URLS = {{ gpx_urls|json_encode|raw }};
...
</script>
```
### Latent bug fixes
**Bug 1 — CSS not reaching `<head>` in feed-map consumers**
`feed-map.html.twig` calls `{% do assets.addCss('map.css') %}` inside `{% block content %}`, but `base.html.twig` renders `{{ assets.css() }}` in `<head>` before content. The CSS is added too late and never appears in `<head>`.
Fix: remove `assets.addCss/addJs` from `feed-map.html.twig`. Add `{% block map_assets %}` to `dailies.html.twig` and `stories.html.twig` — this block is declared at line 11 of `base.html.twig`, before `{{ assets.css() }}`, so it registers correctly.
**Bug 2 — `maplibregl is not defined` in `map.html.twig` and `feed-map.html.twig`**
These templates call `new maplibregl.Map()` in inline `<script>` blocks inside `{% block content %}`. But `map.js` is in the `bottom` group, output by `{{ assets.js('bottom') }}` at the end of `<body>` — after the inline scripts have already run. MapLibre is not defined yet when the inline script executes.
Fix: wrap map init in `document.addEventListener('DOMContentLoaded', function() { ... })`. This is the same pattern `trip.html.twig` already uses correctly; DOMContentLoaded fires after all synchronous scripts (including bottom-group scripts) have run.
### File map
| Action | File | Change |
|---|---|---|
| Create | `templates/macros/stats.html.twig` | New macro: stats computation + panel HTML |
| Create | `templates/macros/cycling.html.twig` | New macro: cycling panel HTML |
| Create | `templates/macros/date-range.html.twig` | New macro: smart date range string |
| Modify | `templates/trip.html.twig` | Import + call macros; remove stats loops + panel HTML; ~130 lines shorter |
| Modify | `templates/story.html.twig` | Replace 15-line date logic with macro call |
| Modify | `templates/stories.html.twig` | Add `{% block map_assets %}`; replace date logic with macro call |
| Modify | `templates/map.html.twig` | Add `{% block map_assets %}` at top level; add DOMContentLoaded wrapper |
| Modify | `templates/partials/feed-map.html.twig` | Remove asset registration; wrap map init in DOMContentLoaded |
| Modify | `templates/dailies.html.twig` | Add `{% block map_assets %}` override |
## Testing
- `trip.html.twig`: stats panel renders correctly (days, countries, cities, temp range); cycling panel appears when GPX present; map initialises; distance stat fills via JS
- `story.html.twig`: date range displays correctly for single-day, same-month, same-year, cross-year entries
- `stories.html.twig`: same date range validation; map loads without console errors
- `map.html.twig`: no console errors on page load (was: `maplibregl is not defined`)
- All inactive templates: no regressions on the active trip page