From bddab60686459f30075852f03e817d4607424f9b Mon Sep 17 00:00:00 2001 From: vikingowl Date: Sun, 19 Apr 2026 00:46:05 +0200 Subject: [PATCH] fix(admin): queue response uses meta envelope; UI reads total from meta MR 6 backend returned {data, total, limit, offset} as siblings but the shared ApiResponse envelope only types the data field. The UI's load function treated queueRes.data as a wrapper and read body.data (undefined) as the row list. Result: empty queue in UI despite 1384 pending rows in the DB. Fix: backend moves total/limit/offset into meta (matches PaginationMeta convention from web/src/lib/api/types.ts). UI casts to read the meta slot alongside typed data. --- backend/internal/domain/discovery/handler.go | 12 +++++--- .../routes/admin/discovery/+page.server.ts | 28 +++++++++++++------ 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/backend/internal/domain/discovery/handler.go b/backend/internal/domain/discovery/handler.go index 8c95212..0f7141a 100644 --- a/backend/internal/domain/discovery/handler.go +++ b/backend/internal/domain/discovery/handler.go @@ -180,11 +180,15 @@ func (h *Handler) ListQueue(c *gin.Context) { c.JSON(apiErr.Status, apierror.NewResponse(apiErr)) return } + // Envelope matches ApiResponse{data, meta} convention used by the rest + // of the API (see web/src/lib/api/types.ts PaginationMeta). c.JSON(http.StatusOK, gin.H{ - "data": rows, - "total": total, - "limit": limit, - "offset": offset, + "data": rows, + "meta": gin.H{ + "total": total, + "limit": limit, + "offset": offset, + }, }) } diff --git a/web/src/routes/admin/discovery/+page.server.ts b/web/src/routes/admin/discovery/+page.server.ts index 063facb..aa2c831 100644 --- a/web/src/routes/admin/discovery/+page.server.ts +++ b/web/src/routes/admin/discovery/+page.server.ts @@ -62,8 +62,7 @@ function parseLimit(raw: string | null): ValidLimit { return (VALID_LIMITS as readonly number[]).includes(n) ? (n as ValidLimit) : 50; } -type QueueListBody = { - data: DiscoveredMarket[]; +type QueueMeta = { total: number; limit: number; offset: number; @@ -75,14 +74,27 @@ export const load: PageServerLoad = async ({ cookies, url }) => { const offset = (page - 1) * limit; const [queueRes, statsRes] = await Promise.all([ - serverFetch(`/admin/discovery/queue?limit=${limit}&offset=${offset}`, cookies), + serverFetch( + `/admin/discovery/queue?limit=${limit}&offset=${offset}`, + cookies + ), serverFetch(`/admin/discovery/stats`, cookies) ]); - // serverFetch casts the full JSON body as ApiResponse; the actual response - // shape is { data: [...], total: N, limit: N, offset: N } so queueRes.data - // is the QueueListBody with all fields. - const body = queueRes.data; - return { queue: body.data, total: body.total, stats: statsRes.data, limit, offset, page }; + // Pagination info lives on the envelope's `meta` field alongside `data`. + // apiFetch/serverFetch only types the `data` slot, so cast for meta access. + const meta = (queueRes as unknown as { meta?: QueueMeta }).meta ?? { + total: 0, + limit, + offset + }; + return { + queue: queueRes.data, + total: meta.total, + stats: statsRes.data, + limit, + offset, + page + }; }; export const actions: Actions = {