feat(ocr): discard abandoned capture batches
A bad scan, the wrong PDFs or a duplicate upload used to leave a batch sitting in READY_FOR_REVIEW forever, because the only exits were confirm (posts to the books) or rejecting every page one at a time. Add a DISCARDED terminal status to both OCR domains and a single endpoint per domain that rejects every page still pending in one shot. Discarding is refused once anything has landed: statements once a page is POSTED, policies once a page is APPLIED. Those batches did real work and have to be settled page by page. - POST /statements/batches/:id/discard - POST /policy-ocr/batches/:id/discard - shared DiscardBatchCard on both review screens, gated the same way
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
|
||||
/**
|
||||
* "Throw this batch away" control, shared by both OCR review queues
|
||||
* (recibos and pólizas).
|
||||
*
|
||||
* Confirmation is a two-step inline swap rather than `window.confirm`: the
|
||||
* dialog would block the page, and an accidental discard is not undoable from
|
||||
* the UI — the reviewer should read what they are about to lose, not dismiss
|
||||
* a modal reflexively.
|
||||
*
|
||||
* The card is only rendered when the batch is still discardable; the API
|
||||
* refuses again on its own (a page posted between render and click).
|
||||
*/
|
||||
export function DiscardBatchCard({
|
||||
busy,
|
||||
onDiscard,
|
||||
pageCount,
|
||||
what,
|
||||
}: {
|
||||
busy: boolean;
|
||||
onDiscard: () => void;
|
||||
pageCount: number;
|
||||
/** Singular noun for what a page becomes — "recibo" / "póliza". */
|
||||
what: string;
|
||||
}) {
|
||||
const [armed, setArmed] = useState(false);
|
||||
|
||||
return (
|
||||
<section className="card" style={{ padding: 16 }}>
|
||||
<h2 className="section-title" style={{ marginTop: 0 }}>
|
||||
Descartar lote
|
||||
</h2>
|
||||
{armed ? (
|
||||
<>
|
||||
<p className="page-sub" style={{ marginBottom: 12 }}>
|
||||
Se descartarán las {pageCount} página(s) de este lote y no se
|
||||
creará ninguna {what}. Esto no se puede deshacer desde aquí; para
|
||||
volver a intentarlo hay que subir los PDFs otra vez.
|
||||
</p>
|
||||
<div className="inline-form" style={{ gap: 8 }}>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-danger"
|
||||
disabled={busy}
|
||||
onClick={onDiscard}
|
||||
>
|
||||
{busy ? "Descartando…" : "Sí, descartar el lote"}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-ghost"
|
||||
disabled={busy}
|
||||
onClick={() => setArmed(false)}
|
||||
>
|
||||
Cancelar
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<p className="page-sub" style={{ marginBottom: 12 }}>
|
||||
Si el lote quedó mal (escaneo ilegible, PDFs equivocados, subida
|
||||
duplicada), descártelo para sacarlo de la cola de revisión.
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-ghost"
|
||||
disabled={busy}
|
||||
onClick={() => setArmed(true)}
|
||||
>
|
||||
Descartar lote
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user