fix: restore list map previews (batch geojson query)

Map previews were missing on every list view (activities, routes, home,
profile, feed): the batch geojson helpers used `WHERE id = ANY(${ids}::text[])`,
but drizzle expands a JS array in a sql template to `($1,$2,...)`, producing the
invalid `ANY((...)::text[])`. That throws, and the helpers' `try/catch` swallowed
it — so every card fell back to "No map preview".

Rewrite getSimplifiedActivityGeojsonBatch (activities) and getSimplifiedGeojsonBatch
(routes) to use the query builder: `inArray(table.id, ids)` for the id list +
a raw `ST_AsGeoJSON(ST_Simplify(geom, 0.001))` select column. Same result, valid
SQL.

E2E: list-map-previews.test.ts creates an activity with geometry and asserts a
Leaflet preview renders on /activities (no "No map preview"). Registered in
playwright.config. typecheck + lint + unit (journal 315) green; verified in the
browser.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Ullrich Schäfer 2026-06-12 22:53:14 +02:00
parent 8cacc7d91e
commit 0ee4daaed1
No known key found for this signature in database
GPG key ID: A32FF691A0F752D9
4 changed files with 62 additions and 14 deletions

View file

@ -0,0 +1,30 @@
import { test, expect, gotoHydrated } from "./fixtures/test";
import { setupVirtualAuthenticator, registerUser } from "./helpers/auth";
// A GPX fixture with geometry → the activity gets a PostGIS geom, so its list
// card should render a Leaflet map preview. Guards the batch-geojson query
// (a malformed `ANY(${ids}::text[])` previously threw and was swallowed,
// silently dropping every list preview).
const GPX = "packages/fit/__fixtures__/alpine.gpx";
test.describe("List map previews", () => {
test("activities list renders a map preview for an activity with geometry", async ({ page }) => {
const cdp = await page.context().newCDPSession(page);
await setupVirtualAuthenticator(cdp);
const stamp = Date.now();
await registerUser(page, `lmp-${stamp}@example.com`, `lmp${stamp}`);
await gotoHydrated(page, "/activities/new");
await page.getByLabel("Name").fill("Preview check");
await page.locator('input[name="gpx"]').setInputFiles(GPX);
await page.getByRole("button", { name: "Create Activity" }).click();
await expect(page).toHaveURL(/\/activities\/[0-9a-f-]+$/, { timeout: 10000 });
await gotoHydrated(page, "/activities");
// The Leaflet container mounts after the client-only dynamic import; no
// "No map preview" fallback should remain.
await expect(page.locator(".leaflet-container").first()).toBeVisible({ timeout: 15000 });
await expect(page.getByText("No map preview")).toHaveCount(0);
});
});