From 11f39253c2e5b7bf5e4307b709b3a2570a6bb025 Mon Sep 17 00:00:00 2001 From: cproudlock Date: Thu, 27 Aug 2026 10:20:38 -0400 Subject: [PATCH] A plugin report renders as a report, not as a JSON dump The reports page ran a hardcoded switch over five core report ids and fell through to `default: console.error('Unknown report')`, and its results panel was a matching chain of per-id templates ending in a `JSON.stringify`. So a plugin could register a report, watch it appear in the catalogue, and get a wall of JSON when it was opened - which is where the three printedparts reports have been since they were added. Any report that declares an endpoint is now fetched from it, and any report returning {columns, rows} renders as a table. That shape is already the convention on the server side, so a plugin needs no core code at all to have a working report. The per-id templates stay for the core reports whose payloads are not tabular. Export CSV goes through the api client rather than window.open. A new window carries no Authorization header, so every report behind a real jwt_required - which is every plugin one - answered the export button with a 401. The core reports are jwt-optional, which is the only reason the old way ever appeared to work. It also followed /api/reports/, which does not exist for a plugin report, instead of the endpoint the report itself came from. A report that fails now says so instead of leaving an empty panel, and the page offers the time window the endpoints already accepted - printedparts-consumption was permanently stuck on its 30-day default because nothing could send one. Verified in a browser against both plugin report families and the core reports. --- CHANGELOG.md | 20 +++ frontend/src/views/reports/ReportsIndex.vue | 163 ++++++++++++++++---- 2 files changed, 154 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c274b47..9c895081 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,26 @@ ADR-007 and ADR-002. ### Fixed +- **Plugin reports rendered a raw JSON dump.** The reports page ran a hardcoded + switch over five core report ids and fell through to + `console.error('Unknown report')`, and its results panel was a matching chain + of per-id templates ending in a `JSON.stringify`. So a plugin could register a + report, watch it appear in the catalogue, and get a wall of JSON when it was + opened - which is where the three printedparts reports have been since they + were added. Any report that declares an endpoint is now fetched from it, and + any report returning `{columns, rows}` renders as a table. A plugin needs no + core code to have a working report. CSV export follows the same endpoint + rather than assuming `/api/reports/`, which does not exist for a plugin + report, and a report that fails now says so instead of showing an empty panel. + Export CSV is fetched through the api client rather than `window.open`: a new + window carries no Authorization header, so every report behind a real + `jwt_required` answered the export button with a 401. The core reports are + jwt-optional, which is the only reason the old way ever appeared to work. + +- **Reports had no way to choose a time period.** Endpoints accepted a window; + the page offered no control for it, so `printedparts-consumption` was + permanently stuck on its 30-day default. + - **USB kiosk: check a drive out and back in without a login.** The people who actually take USB drives are standing at the cabinet, and most of them have no ShopDB account - so check-out and check-in were gated behind a login that was diff --git a/frontend/src/views/reports/ReportsIndex.vue b/frontend/src/views/reports/ReportsIndex.vue index 34457c3d..89c62c32 100644 --- a/frontend/src/views/reports/ReportsIndex.vue +++ b/frontend/src/views/reports/ReportsIndex.vue @@ -47,8 +47,20 @@ +
{{ reportError }}
+
+