/** Admin claim workbench: list + status changes. */ import type { FastifyInstance } from "fastify"; import { z } from "zod"; import { envelope, notFound, parseBody, parseQuery } from "../../lib/http.js"; import { intParam, pageParam, strParam } from "../../lib/params.js"; import { listClaims, setClaimStatus } from "../../repositories/admin/claims.js"; import { invalidate } from "../../cache.js"; export async function claimAdminRoutes(app: FastifyInstance): Promise { app.get("/claims", { schema: { summary: "Claims (ClaimDTO[]) ?subject_type=&subject_id=&status=current|superseded|rejected|review|unscoped|all&predicate=&page=&per_page=" } }, async (req) => { const q = parseQuery(z.object({ subject_type: strParam, subject_id: strParam, status: strParam, predicate: strParam, page: pageParam, per_page: intParam }), req.query); const res = await listClaims({ subjectType: q.subject_type, subjectId: q.subject_id, status: q.status, predicate: q.predicate, page: q.page, perPage: q.per_page }); return envelope(res.items, { total: res.total, page: res.page, perPage: res.perPage }); }); app.post("/claims/:id/status", { schema: { summary: "Set a claim status {status: current|rejected|review, reason?}. Never rewrites the entity column: when a rejected claim was backing a displayed facility MW value a quality flag claim_rejected_backing_value is raised for the next reconciliation" } }, async (req) => { const { id } = req.params as { id: string }; const body = parseBody(z.object({ status: z.enum(["current", "rejected", "review"]), reason: z.string().max(500).optional() }), req.body); const res = await setClaimStatus(id, body.status, body.reason ?? null); if (!res) throw notFound("claim"); await Promise.all([invalidate("/datacenters"), invalidate("/projects")]); return envelope(res); }); }