"use client"; /** * v1.18.9 — a single proposed Lab-OCR row on the review screen. * * Mandatory human review: each row is shown for per-row confirm/edit/discard. * The checkbox confirms; the inline fields edit analyte / value-or-valueText / * unit / reference range / date. Server-computed hints surface as calm badges: * a new-vs-existing-biomarker hint, a duplicate warning (the row defaults to * unchecked when flagged), and a low-confidence flag per the model's self-score. * * The no-alarming-colour ethos holds: an out-of-range or duplicate row is not * painted red — these are informative `secondary`/`outline` badges, same weight * as the in-range state. */ import { useId } from "react"; import { AlertCircle, FilePlus2, Link2, Ruler, TriangleAlert, } from "lucide-react"; import { Badge } from "@/components/ui/badge"; import { Checkbox } from "@/components/ui/checkbox"; import { DateField } from "@/components/ui/date-field"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { useTranslations } from "@/lib/i18n/context"; import type { OcrReviewRow } from "./ocr-review-types"; /** Below this per-field confidence the field is flagged for a second look. */ const CONFIDENCE_THRESHOLD = 0.6; export function OcrRowEditor({ row, onChange, }: { row: OcrReviewRow; onChange: (next: OcrReviewRow) => void; }) { const { t } = useTranslations(); const fieldId = useId(); const isQualitative = row.valueText !== null && row.valueText !== undefined; const lowValueConfidence = !isQualitative && row.confidence.value < CONFIDENCE_THRESHOLD; const valueUnreadable = !isQualitative && row.value === null; // The model self-scores each field it transcribed. Surface a single calm // per-row flag when ANY tracked field came back below the threshold so a // shaky read is visible at a glance — including on qualitative rows, which // have no value-confidence footnote of their own. Informative, not alarming. const lowRowConfidence = row.confidence.analyte < CONFIDENCE_THRESHOLD || row.confidence.unit < CONFIDENCE_THRESHOLD || (isQualitative ? row.confidence.value < CONFIDENCE_THRESHOLD : lowValueConfidence); // v1.18.10 (#5) — a NEW numeric biomarker mints its catalog reference range // from THIS extracted row. The range is therefore load-bearing and worth a // second look. Surface the range confidence prominently for new numeric // markers, and call it out explicitly when the model was unsure (or set no // bounds at all) so the user verifies the range it will be judged against. const isNewNumericMarker = !isQualitative && row.biomarkerMatch === "new"; const lowRangeConfidence = row.confidence.range < CONFIDENCE_THRESHOLD; const hasBounds = row.referenceLow !== null || row.referenceHigh !== null; const flagRange = isNewNumericMarker && (lowRangeConfidence || !hasBounds); return (