From 4a13004917b38828ef8ff59a961b3e7140f652ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Thu, 1 Oct 2026 09:44:29 +0200 Subject: [PATCH] fix(labs): fit the lab list, values list and add sheet to a phone The compact list was a five-column table at every width. Below the fixed columns (badge, 12rem range bar, 72px trend, chevron) the name column collapsed to nothing, the badge sat over the value and the row panned sideways off the card. Below lg the same cells now fold into two lines per row; the desktop subgrid is unchanged. The values table needed ~510px, so on a phone its edit and delete buttons sat in a sideways-scrolling strip and the range bar labels were clipped. Below lg each reading is a stacked row with its actions beside it. The add sheet's three footer buttons did not fit a phone row in German and pushed Cancel off the left edge; the secondary save takes a row of its own there, and the footer wraps instead of overflowing on desktop. The scan review's 'links to existing marker' chip truncates instead of running past its row. --- src/app/labs/page.tsx | 5 +- src/components/labs/lab-form.tsx | 4 + src/components/labs/lab-history-list.tsx | 233 ++++++++++++++--------- src/components/labs/lab-list.tsx | 72 +++++-- src/components/labs/ocr-row-editor.tsx | 12 +- 5 files changed, 214 insertions(+), 112 deletions(-) diff --git a/src/app/labs/page.tsx b/src/app/labs/page.tsx index 1899669fd..69ca8f41e 100644 --- a/src/app/labs/page.tsx +++ b/src/app/labs/page.tsx @@ -192,7 +192,10 @@ export default function LabsPage() { title={t("labs.addResult")} description={t("labs.addDescription")} footer={ -
+
} > {pendingAction === "saveAndAddAnother" ? ( diff --git a/src/components/labs/lab-history-list.tsx b/src/components/labs/lab-history-list.tsx index 55b397519..3dce4fa67 100644 --- a/src/components/labs/lab-history-list.tsx +++ b/src/components/labs/lab-history-list.tsx @@ -311,103 +311,148 @@ export function LabHistoryList({ readings }: { readings: LabResultDto[] }) { }); }, [readings, sortBy, sortDir]); + // One action pair per reading, shared by the phone list and the table. + const renderActions = (r: LabResultDto) => ( +
+ {canManageLabs && ( + + )} + deleteMutation.mutate(r.id)} + title={t("labs.deleteConfirmTitle")} + description={t("labs.deleteConfirmDescription")} + confirmLabel={t("labs.deleteReading")} + className="size-11 sm:size-9" + iconClassName="h-4 w-4" + /> +
+ ); + return ( <> - - - - - - - - - {t("labs.editReading")} - - - - - {ordered.map((r) => ( - - -
- {formatLabReading(r)} - -
-
- - {formatDateShort(r.takenAt, true)} - - -
- - -
-
- - {r.hasNote ? t("labs.hasNote") : ""} - - -
- {canManageLabs && ( - - )} - deleteMutation.mutate(r.id)} - title={t("labs.deleteConfirmTitle")} - description={t("labs.deleteConfirmDescription")} - confirmLabel={t("labs.deleteReading")} - className="size-11 sm:size-9" - iconClassName="h-4 w-4" - /> -
-
+ {/* Phones get one stacked row per reading. The table needs ~510 px for + its five columns, so on a phone it panned sideways inside its + container and parked the edit / delete buttons off-screen, while the + range bar's end labels were clipped by the same container. */} +
    + {ordered.map((r) => ( +
  • +
    +
    + {formatLabReading(r)} +
    + +
    + + {formatDateShort(r.takenAt, true)} + + + {r.hasNote ? ( + {t("labs.hasNote")} + ) : null} +
    + +
    + {renderActions(r)} +
  • + ))} +
+
+
+ + + + + + + + {t("labs.editReading")} + - ))} - -
+ + + {ordered.map((r) => ( + + +
+ {formatLabReading(r)} + +
+
+ + {formatDateShort(r.takenAt, true)} + + +
+ + +
+
+ + {r.hasNote ? t("labs.hasNote") : ""} + + + {renderActions(r)} + +
+ ))} +
+ +
+
void } = {}) { const { t } = useTranslations(); const { canWriteDomain } = useRecordCapabilities(); @@ -212,12 +241,15 @@ export function LabList({ onAddFirst }: { onAddFirst?: () => void } = {}) {

) : null} - + {groups.map((group) => { const inner = ( -
+
- + {group.analyte}
@@ -234,47 +266,57 @@ export function LabList({ onAddFirst }: { onAddFirst?: () => void } = {}) { {inner} -
+
reading.value)} referenceLow={group.latest.referenceLow} referenceHigh={group.latest.referenceHigh} />
- + ) : ( // Un-linked group (not backfilled to a catalog biomarker): // render inert but say so, so it doesn't read as a broken // link next to its clickable neighbours. -
+
{inner} -
+
reading.value)} referenceLow={group.latest.referenceLow} referenceHigh={group.latest.referenceHigh} />
- + {t("labs.notLinkedYet")}
diff --git a/src/components/labs/ocr-row-editor.tsx b/src/components/labs/ocr-row-editor.tsx index ea1a06281..e63388a55 100644 --- a/src/components/labs/ocr-row-editor.tsx +++ b/src/components/labs/ocr-row-editor.tsx @@ -129,9 +129,17 @@ export function OcrRowEditor({ {fieldError("analyte", "labs.ocr.errorAnalyte")}
{row.biomarkerMatch === "existing" ? ( - + // The chip repeats the analyte, so a long panel name ran past + // the row on a phone; it truncates inside the row instead. + - {t("labs.ocr.linksExisting", { name: row.analyte })} + + {t("labs.ocr.linksExisting", { name: row.analyte })} + ) : (