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
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
# 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`.
|
||||
|
||||
```js
|
||||
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`
|
||||
Reference in New Issue
Block a user