From 326aee771400072ca1772f743a574675ec8a53ab Mon Sep 17 00:00:00 2001 From: justin Date: Tue, 16 Jun 2026 02:57:24 -0500 Subject: [PATCH] admin: inline filing screenshots + atomic approve transaction - Documents now flag is_image and the drawer renders screenshots / confirmation images as inline clickable thumbnails (click to open full size); PDFs keep the View link. Evidence keys are labeled (Filing confirmation screenshot, etc.), the worker-temp screenshot_path (not a MinIO key) is dropped in favor of the durable evidence copy, and non-file evidence (fax_log_id) is skipped. - Wrap approve's status-update + audit-insert in a transaction so a failure can no longer leave an order out of ready_to_file without dispatching (the earlier audit CHECK violation did exactly that to Paul's UCR; it has been reset). --- api/src/routes/admin.ts | 62 +++++++++++++------ .../public/admin/compliance-orders/index.html | 14 +++++ 2 files changed, 58 insertions(+), 18 deletions(-) diff --git a/api/src/routes/admin.ts b/api/src/routes/admin.ts index d26bcac..6ee911e 100644 --- a/api/src/routes/admin.ts +++ b/api/src/routes/admin.ts @@ -499,21 +499,36 @@ router.post("/api/v1/admin/compliance-orders/:order_number/approve", requireAdmi return; } - await pool.query( - `UPDATE compliance_orders - SET fulfillment_status = 'authorization_signed', fulfillment_status_at = now(), updated_at = now() - WHERE order_number = $1`, - [id], - ); - await pool.query( - `INSERT INTO order_audit_log (order_type, order_id, order_number, action, from_status, to_status, actor_type, actor_id, actor_name, note) - VALUES ('compliance', 0, $1, 'approved_for_submission', 'ready_to_file', 'authorization_signed', 'admin', $2, $3, $4)`, - [id, req.admin!.id, req.admin!.username, - (req.body?.note as string) - || (order.intake_data_validated - ? "Approved + dispatched for government submission" - : "Approved + dispatched (intake-incomplete override)")], - ); + // Flip status + write the audit row atomically. Previously these were two + // separate pool.query calls, so a failure on the audit insert (e.g. an + // order_type CHECK violation) left the status changed but un-dispatched and + // returned a 500 -- the order silently fell out of ready_to_file without + // being filed. A transaction keeps them all-or-nothing. + const client = await pool.connect(); + try { + await client.query("BEGIN"); + await client.query( + `UPDATE compliance_orders + SET fulfillment_status = 'authorization_signed', fulfillment_status_at = now(), updated_at = now() + WHERE order_number = $1`, + [id], + ); + await client.query( + `INSERT INTO order_audit_log (order_type, order_id, order_number, action, from_status, to_status, actor_type, actor_id, actor_name, note) + VALUES ('compliance', 0, $1, 'approved_for_submission', 'ready_to_file', 'authorization_signed', 'admin', $2, $3, $4)`, + [id, req.admin!.id, req.admin!.username, + (req.body?.note as string) + || (order.intake_data_validated + ? "Approved + dispatched for government submission" + : "Approved + dispatched (intake-incomplete override)")], + ); + await client.query("COMMIT"); + } catch (txErr) { + await client.query("ROLLBACK").catch(() => {}); + throw txErr; + } finally { + client.release(); + } const workerUrl = process.env.WORKER_URL || "http://workers:8090"; let dispatched = false; @@ -671,10 +686,19 @@ async function collectOrderDocuments(orderNumber: string): Promise = { + confirmation_screenshot: "Filing confirmation screenshot", + pre_submit_screenshot: "Pre-submission screenshot", + attested_pdf_minio: "Attested filing PDF", + }; for (const [k, v] of Object.entries(fs.evidence as Record)) { - add("Evidence: " + k.replace(/_/g, " "), v); + if (k === "fax_log_id") continue; // an ID, not a file + add(EVIDENCE_LABELS[k] || ("Evidence: " + k.replace(/_/g, " ")), v); } } add("Engagement letter", o.engagement_letter_minio_key); @@ -721,7 +745,9 @@ router.get("/api/v1/admin/compliance-orders/:order_number/documents", requireAdm present = probe.ok; // 200 or 206 } } catch { /* treat as missing */ } - return { ...d, exists: present }; + const lower = d.key.toLowerCase(); + const isImage = lower.endsWith(".png") || lower.endsWith(".jpg") || lower.endsWith(".jpeg") || lower.endsWith(".gif") || lower.endsWith(".webp"); + return { ...d, exists: present, is_image: isImage }; })); const available = checked.filter((d) => d.exists); res.json({ order_number: req.params.order_number, documents: available }); diff --git a/site/public/admin/compliance-orders/index.html b/site/public/admin/compliance-orders/index.html index bd70c5e..487c06d 100644 --- a/site/public/admin/compliance-orders/index.html +++ b/site/public/admin/compliance-orders/index.html @@ -401,6 +401,20 @@ box.innerHTML = documents.map((d) => { const u = API + "/api/v1/admin/compliance-orders/" + encodeURIComponent(orderNumber) + "/document?key=" + encodeURIComponent(d.key) + "&token=" + tok; + // Render screenshots/images as an inline clickable thumbnail so you can + // see the filing confirmation at a glance; PDFs stay as a View link. + if (d.is_image) { + return `
+
+ ${esc(d.label)} + ${esc(d.key.split("/").pop())} +
+ + ${esc(d.label)} + +
`; + } return `
${esc(d.label)} ${esc(d.key.split("/").pop())}