feat(recibos): live OCR progress bar on review page
Build and Push Images / Build jorgecuadros-web (push) Successful in 1m51s
Build and Push Images / Build jorgecuadros-api (push) Successful in 2m35s

Backend already returns per-status counts via byStatus; render a real
progress bar (X% / N de M / en cola) while PENDING_OCR pages remain,
using the existing progress-track CSS. Falls back to indeterminate
when no docs have been reported yet.
This commit is contained in:
2026-08-01 02:43:19 -07:00
parent e589bda28b
commit 216309190c
+50 -3
View File
@@ -132,9 +132,7 @@ function BatchReview({ id }: { id: string }) {
{error && <div className="state-box state-error">{error}</div>}
{processing && (
<div className="state-box">
Leyendo los recibos esta pantalla se actualiza sola.
</div>
<ProcessingBanner docsLength={docs.length} pendingOcr={batch.byStatus.PENDING_OCR ?? 0} />
)}
<SummaryCard batch={batch} readyCount={readyCount} />
@@ -170,6 +168,55 @@ const STATUS_LABEL_BATCH: Record<string, string> = {
FAILED: "Falló",
};
/**
* Live readout while OCR is running. The backend tells us how many pages are
* still PENDING_OCR, so we can show real progress instead of "loading…". When
* the docs list hasn't caught up to the upload yet (total === 0) we fall back
* to the indeterminate bar.
*/
function ProcessingBanner({
docsLength,
pendingOcr,
}: {
docsLength: number;
pendingOcr: number;
}) {
const done = Math.max(docsLength - pendingOcr, 0);
const pct =
docsLength > 0 ? Math.min(100, Math.round((done / docsLength) * 100)) : null;
return (
<div className="card" style={{ padding: 16 }}>
<div className="upload-progress" style={{ padding: 0 }}>
<div
className={`progress-track${pct === null ? " progress-indeterminate" : ""}`}
role="progressbar"
aria-valuemin={0}
aria-valuemax={100}
aria-valuenow={pct ?? undefined}
>
<div className="progress-fill" style={{ width: `${pct ?? 100}%` }} />
</div>
<div className="upload-progress-stats">
{pct === null ? (
<span>Leyendo los recibos</span>
) : (
<>
<strong>{pct}%</strong>
<span>
{done} de {docsLength} página(s) leídas
</span>
{pendingOcr > 0 && <span>{pendingOcr} en cola</span>}
</>
)}
<span style={{ marginLeft: "auto" }}>
Esta pantalla se actualiza sola.
</span>
</div>
</div>
</div>
);
}
function SummaryCard({
batch,
readyCount,