Files
intotheeast-com/docs/working/specs/2026-06-27-home-trip-view-convergence-design.md
T
m038andClaude Opus 4.8 301de51add docs: add home/trip view convergence design spec
Converge home-active feed-col chrome (date range, filter bar, stats/
cycling panels) with the trip page via a shared partial + shared
initTripStats() glue. Map convergence deferred to a later spec.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BftDn9vu9SonFAY4vxu4uk
2026-06-27 19:37:11 +02:00

8.6 KiB

Home / Trip View Convergence Design

Date: 2026-06-27 Status: Approved for implementation

Problem

The home page in active-trip mode (home.html.twig, config.site.travelling branch) and the trip page (trip.html.twig) are meant to present the same experience — the same content, behaving near-identically. Today their feeds already match (both render journal + story entries via the shared entry-journal/entry-story partials), but the feed-col chrome diverges:

Feature Trip page Home-active Converge?
Feed lists journal + stories already matches
Date-range header yes
Filter bar (All / Journal / Stories) yes
Stats panel yes
Cycling panel (if GPX) yes
Sort toggle button no — intended difference
Default feed order oldest→newest (sort flag 4) its own (sort flag 3) no — intended difference

The chrome markup is the divergence. The supporting behavior is already global: js/main.js (loaded for every page via base.html.twig:10) runs initFilterBar(), initPanelToggles(), and initSortButton('trip-sort-toggle', …), each a silent no-op when its markup is absent. The entry partials already emit data-type, which the filter relies on. So rendering the same markup on home is enough for the filter bar, panel toggles, and (where present) the sort button to work with zero new JS.

The single exception is the stats/cycling computation glue (writing distance/elevation values into #stat-distance, #cyc-*). That code is currently inline in trip.html.twig and not global, so the stats/cycling panels cannot function on home until it is shared.

Goals

  • Home-active gains the date-range header, filter bar, and stats/cycling panels — matching the trip page.
  • The shared feed-col chrome lives in one place (a partial), so future header/chrome changes apply to both pages.
  • Home-active keeps its own default feed order and has no sort button (the two intended differences).
  • Stats/cycling computation works on both pages from a single shared JS function.
  • No change to the trip page's rendered output (structural refactor only on that side).

Non-goals

  • Map convergence is out of scope. The home-active map omitting story markers, lacking a fullscreen button, and its hash-only click behavior are all deferred to a later map-init spec (see project-map-init-refactor memory). Both inline map scripts and both map-col markup blocks stay exactly as they are.
  • Extracting the all_items / map_entries build loops to a macro — Twig macros output HTML, not arrays (established constraint; see 2026-06-23-template-refactor-design.md). Each page keeps its own data-build loops.
  • Any visual restyling of the chrome — home reuses the trip's existing CSS classes unchanged.
  • Adding a sort button to home, or changing home's default order.

Architecture

1. New shared partial: templates/partials/trip-feed-col.html.twig

Holds the entire .home-feed-col content currently inline in trip.html.twig:70-120:

  • header (.home-trip-header): title, date range (when trip_page.header.date_start set), counts
  • filter bar (.trip-filter-bar): All / Journal / Stories buttons
  • the sort button (#trip-sort-toggle) — rendered only when show_sort is true
  • panel toggles (.trip-panel-toggles): Stats, and Cycling (when has_gpx)
  • stats_panel(...) and (when has_gpx) cycling_panel(...) macro calls
  • the feed loop over all_items with the #feed-filter-empty sentinel

Interface (called via {% include 'partials/trip-feed-col.html.twig' with {…} only %}):

Param Type Trip passes Home-active passes
trip_page Page page trip
all_items array sorted by date, flag 4 sorted by date, flag 3
journal_entries array dailies children dailies children
journal_count int count count
story_count int count count
has_gpx bool gpx_urls|length > 0 home_gpx_urls|length > 0
show_sort bool true false

Because the partial is called with only, it must {% import 'macros/stats.html.twig' %} and {% import 'macros/cycling.html.twig' %} itself.

Both pages already build all_items, the counts, and has_gpx for their existing map data, so these are passed in rather than rebuilt — no new duplication is introduced.

2. Shared stats glue: initTripStats(config) in js/src/main.js

Extract the inline stats/cycling computation from trip.html.twig:213-249 into a config-driven function. Current inline logic: if GPX present, MapUtils.parseGpxFiles(urls, …) fills #stat-distance and all #cyc-* fields; otherwise sum haversineKm over gps_points and write the ~-prefixed estimate to #stat-distance.

function initTripStats(config) {
    // config: { gpxUrls: [], gpsPoints: [[lat,lng],...], hasGpx: bool }
    // No-op if #stat-distance is absent (page has no stats panel).
}

Called from the boot block alongside the other inits. Each page provides the config via a small inline <script> that defines the data (the *_GPX_URLS / gps_points arrays are page-specific Twig output), then calls initTripStats(...) — or the boot reads globals the page sets. Implementation detail for the plan; the contract is: the function is selector-guarded and runs on any page that rendered a stats panel.

js/main.js is the built artifact; the asset pipeline rebuilds it from js/src/main.js (see 2026-06-22-asset-pipeline-design.md).

3. Home-active data additions

Home-active currently builds map_entries (journal-only) and home_gpx_urls. For the stats panel's no-GPX fallback it must also build gps_points (journal entries with lat/lng), mirroring trip.html.twig:27-32.

4. Template wiring

trip.html.twig: replace the inline .home-feed-col block (:70-120) with the partial include; remove the inline stats script (:213-249) in favor of initTripStats(...). Map script, fullscreen wiring, and map data-build stay untouched.

home.html.twig (active branch): replace the bespoke feed-col (:60-83) with the same partial include (show_sort: false); add gps_points build; wire initTripStats(...). Map script and map-col stay untouched.

Data / behavior flow after change

base.html.twig  ──loads──>  js/main.js (global)
                              ├─ initFilterBar()      ← works on both via .trip-filter-btn + [data-type]
                              ├─ initPanelToggles()   ← works on both via .trip-panel-toggle
                              ├─ initSortButton('trip-sort-toggle', …)  ← trip only (home omits button → no-op)
                              └─ initTripStats(cfg)   ← works on both via #stat-distance guard

trip.html.twig  ─include─>  partials/trip-feed-col.html.twig  (show_sort: true)
home.html.twig  ─include─>  partials/trip-feed-col.html.twig  (show_sort: false)
                              └─ stats_panel(), cycling_panel(), feed loop

Testing

No JS test harness exists in this project; verification is manual browser smoke testing at http://localhost:8081, consistent with prior template work.

Trip page (regression — must be unchanged):

  1. Load the trip page. Confirm header, date range, filter bar, sort button, stats/cycling panels, and feed render identically to before.
  2. Filter bar All/Journal/Stories filters the feed; sort button flips order; Stats/Cycling panels toggle open/closed.
  3. Stats panel distance and cycling figures populate (GPX present) or show the ~ estimate (no GPX).

Home page, active-trip mode (new behavior): 4. With config.site.travelling: true, load /. Confirm date range, counts, filter bar (no sort button), and Stats panel (+ Cycling if the trip has GPX) now appear. 5. Filter bar filters the feed; panel toggles work; stats figures populate. 6. Confirm the feed default order is home's own order (unchanged from today) and that no sort button is present.

Home page, between-trips mode (regression): 7. With config.site.travelling: false, load /. Confirm the highlights layout is unaffected (this branch does not use the partial).

Files touched

  • New: user/themes/intotheeast/templates/partials/trip-feed-col.html.twig
  • Edit: user/themes/intotheeast/templates/trip.html.twig (feed-col → include; remove inline stats script)
  • Edit: user/themes/intotheeast/templates/home.html.twig (active branch feed-col → include; add gps_points; wire stats)
  • Edit: user/themes/intotheeast/js/src/main.js (+initTripStats); rebuild js/main.js