# GPX Manager Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Build a protected admin page at `/gpx-manager` that lists all trip GPX files and supports upload and deletion via the Grav API. **Architecture:** A Grav page (`user/pages/03.gpx-manager/`) with a custom Twig template. Access is enforced by the Login plugin via `access.admin.login: true` in page frontmatter. The template renders a section per trip using the Grav page tree, then vanilla JavaScript calls the existing Grav API (`/api/v1/pages{route}/media`) using the browser's live session cookie — no JWT or separate login needed. **Tech Stack:** Grav 2.0 Twig, Vanilla JS (fetch API), Grav API plugin v1, Grav Login plugin (page access control) ## Global Constraints - Grav 2.0.0-rc.9 + Admin2 v2.0.0-rc.15; theme `intotheeast` at `user/themes/intotheeast/` - API base URL: `/api/v1` (`route: /api`, `version_prefix: v1` in `user/plugins/api/api.yaml`) - Session auth: all fetch calls use `credentials: 'include'` — no JWT handling (`session_enabled: true` in api.yaml) - API media routes (confirmed from `user/plugins/api/classes/Api/ApiRouter.php:333`): - `GET /api/v1/pages{route}/media` — list; response `{ data: [{ filename, size, modified, type }] }` - `POST /api/v1/pages{route}/media` — multipart file upload - `DELETE /api/v1/pages{route}/media/{filename}` — delete single file - `{route}` is the full Grav route including leading slash, e.g. `/trips/italy-2025` - Style: teal `#1F6B5A`, warm border `#e0ddd6`, font-family `'DM Sans', sans-serif` — match existing theme tokens - No new plugins, no npm, no build step. All changes inside `user/` only. - The page must be `visible: false` — must not appear in site navigation. - Trip pages live at `user/pages/01.trips//`; retrieved via `grav.pages.find('/trips').children.published()` --- ### Task 1: Page definition **Files:** - Create: `user/pages/03.gpx-manager/gpx-manager.md` **Interfaces:** - Produces: Grav page routed at `/gpx-manager`, protected by Login plugin, hidden from nav, using template `gpx-manager` - [ ] **Step 1: Create the page file** Create `user/pages/03.gpx-manager/gpx-manager.md` with this exact content: ``` --- title: 'GPX Manager' template: gpx-manager visible: false routable: true access: admin.login: true --- ``` - [ ] **Step 2: Verify protection (no template yet)** With the dev server running, open `http://localhost:8081/gpx-manager` while **logged out** of admin. You should be redirected to the login page. While **logged in**, you'll see a blank page or a Twig error (template missing) — that's fine at this stage. - [ ] **Step 3: Commit** ```bash git -C user add pages/03.gpx-manager/gpx-manager.md git -C user commit -m "feat: add gpx-manager page definition (access-protected)" ``` --- ### Task 2: Template — layout and trip sections **Files:** - Create: `user/themes/intotheeast/templates/gpx-manager.html.twig` **Interfaces:** - Consumes: `grav.pages.find('/trips').children.published()` — each trip object exposes `.route` (string, e.g. `/trips/italy-2025`), `.title` (string), `.slug` (string, e.g. `italy-2025`) - Produces: one `.gpx-trip[data-route]` section per trip; `data-route` = full route string (e.g. `/trips/italy-2025`); `data-trip-route` on upload form = same value - [ ] **Step 1: Create the template** Create `user/themes/intotheeast/templates/gpx-manager.html.twig`: ```twig {% extends 'partials/base.html.twig' %} {% block content %} {% set trips_page = grav.pages.find('/trips') %} {% set trips = trips_page ? trips_page.children.published() : [] %}

GPX Files

{% if trips is empty %}

No trips found.

{% else %} {% for trip in trips %}

{{ trip.title }}

Loading…

{% endfor %} {% endif %}
{% endblock %} ``` - [ ] **Step 2: Verify trip sections render** Open `http://localhost:8081/gpx-manager` while logged in. You should see: - Heading "GPX Files" - One card per trip (Italy 2025, Japan-Korea 2026) each showing "Loading…" and an upload form with a file picker and Upload button. - The page header/nav from `base.html.twig` is present. - [ ] **Step 3: Commit** ```bash git -C user add themes/intotheeast/templates/gpx-manager.html.twig git -C user commit -m "feat: gpx-manager template layout with trip sections" ``` --- ### Task 3: JavaScript — list, upload, delete **Files:** - Modify: `user/themes/intotheeast/templates/gpx-manager.html.twig` — replace `/* GPX manager JS — added in Task 3 */` inside the existing `