Files
intotheeast-com-content/themes/intotheeast/templates/trips.html.twig
T
m038andClaude Opus 4.8 37b669424b fix(trips): remaining publish-toggle review findings
- resolveTripChild now asserts the resolved page uses the trip template, so a
  non-trip direct child of /trips could never be toggled through this endpoint
  (P3 adversarial).
- apiSend gains an optional timeoutMs (AbortController); trip-publish passes 10s
  so a hung toggle can't leave the switch stuck aria-busy. post-form omits it,
  keeping media uploads unbounded (P2 reliability).
- trips.html.twig reuses trip.html.twig's one-line active-trip slug match
  instead of a bespoke 3-branch OR (P2 maintainability).
- Draft-badge amber is now a --color-draft-accent token shared by the trip and
  journal badges instead of a twice-hardcoded #E0A458 literal (P3).

Rebuilt js/trip-publish.js and js/post/post-form.js (shared api-utils change).
PHP lint clean; trip-publish suite 10/10; post suite unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mpdu3Dt1iVoozHwAMyjrbn
2026-07-08 17:31:40 +02:00

58 lines
3.0 KiB
Twig

{% extends 'partials/base.html.twig' %}
{% block content %}
{% import 'macros/cover.html.twig' as cover %}
<h1 class="trips-heading">Past Trips</h1>
{# Owner gate (R1): broader than trip.html.twig's owner_can_edit — publishing
works on ANY trip, not just the active one, so it's just owner identity. #}
{% set is_owner = grav.user.authenticated
and grav.user.username == grav.config.site.owner_username %}
{# Owner sees drafts (R9); everyone else published only. #}
{% set trips = (is_owner ? page.children : page.children.published())|sort((a, b) => a.date < b.date ? 1 : -1) %}
{% if is_owner %}{% do assets.addJs('theme://js/trip-publish.js', {group: 'bottom'}) %}{% endif %}
{% if trips|length == 0 %}
<p class="feed-empty">No trips yet.</p>
{% else %}
<div class="trips-list">
{% for trip in trips %}
{% set dailies_page = grav.pages.find(trip.route ~ '/dailies') %}
{% set stories_page = grav.pages.find(trip.route ~ '/stories') %}
{% set journal_count = dailies_page ? dailies_page.children.published()|length : 0 %}
{% set story_count = stories_page ? stories_page.children.published()|length : 0 %}
{# Active-trip match: site.active_trip may be a full route (/trips/x) or a
bare slug — normalise to its last segment and compare to the trip slug,
the same one-liner trip.html.twig uses, so the R12 confirm never silently
drops. #}
{% set active_trip_slug = (grav.config.site.active_trip|default(''))|split('/')|last %}
{% set is_active = active_trip_slug != '' and trip.slug == active_trip_slug %}
{# The wrapper is a positioned container so the owner toggle can overlay the
cover as a SIBLING of the navigating <a> (KTD6) — and it exists even for a
coverless draft, giving the toggle an anchor whether or not the cover macro
emits an image. #}
<div class="trip-card-wrap">
<a class="trip-card" href="{{ trip.url }}">
{{ cover.render(trip, trip.title, 720, 240, 'trip-card-cover', '(max-width: 700px) 100vw, 360px') }}
<div class="trip-card-title">{{ trip.title }}</div>
{% if trip.header.tagline %}
<div class="trip-card-tagline">{{ trip.header.tagline }}</div>
{% endif %}
<div class="trip-card-meta">
{% if trip.header.date_start %}
<span class="trip-card-dates">
{{ trip.header.date_start|date('M Y') }}
{% if trip.header.date_end %}{{ trip.header.date_end|date('M Y') }}{% else %} — Ongoing{% endif %}
</span>
{% endif %}
<span class="trip-card-counts">
{{ journal_count }} journal {{ journal_count == 1 ? 'entry' : 'entries' }}
{% if story_count > 0 %} · {{ story_count }} {{ story_count == 1 ? 'story' : 'stories' }}{% endif %}
</span>
</div>
</a>
{% if is_owner %}{% include 'partials/trip-publish-toggle.html.twig' with { trip: trip, is_active: is_active } only %}{% endif %}
</div>
{% endfor %}
</div>
{% endif %}
{% endblock %}