feat(medications): carry manufacturer and printed strength on an inventory container

The native pen list renders these two as its headline and subhead. The
server held neither, so a container registered on the web could not be
shown there without fabricating an empty card, and the client kept its
own copy locally with no back-fill possible.

Both columns are additive and nullable (migration 0255). Every existing
row is truthfully NULL — the values were never collected, and a plain
supply row such as a blister pack or a bottle has no use for either.

Strength stays free text rather than a parsed number + unit: pens state
it per dose, per ml, or per cartridge depending on the product, and
forcing a numeric split would lose which of the three was printed.

On PATCH an omitted field leaves the stored value untouched, the same
rule notes already followed, so a client that does not know the fields
cannot blank what another populated; an explicit null still clears. The
web add-container form collects both for a PEN, so the columns do not
ship empty forever.
This commit is contained in:
Marc-André Bombeck committed 2026-07-19 12:28:48 +02:00
1 parent 4ddc08f784
commit 4c3cc84daf
18 files changed
+391 -28

No files matched your search

@@ -492,6 +492,11 @@ export function AddInventoryDialog({
const [containerType, setContainerType] =
useState<ContainerType>(initialContainerType);
const [expiry, setExpiry] = useState("");
// Carton labelling. Only offered for a PEN: the native pen list is the
// one surface that renders them, and asking for a maker + strength on a
// blister pack would be noise on the far commoner path.
const [manufacturer, setManufacturer] = useState("");
const [doseStrength, setDoseStrength] = useState("");
const [busy, setBusy] = useState(false);
const formId = useId();
@@ -513,6 +518,10 @@ export function AddInventoryDialog({
printedExpiry: expiry
? new Date(`${expiry}T00:00:00`).toISOString()
: null,
// Trimmed to null so a field the user opened and left blank stores
// absence rather than an empty string.
manufacturer: manufacturer.trim() || null,
doseStrength: doseStrength.trim() || null,
});
await invalidateSupplyQueries(queryClient, medicationId);
toast.success(t("medications.detail.bestand.addSuccess"));
@@ -664,6 +673,41 @@ export function AddInventoryDialog({
onChange={setExpiry}
/>
</div>
{containerType === "PEN" && (
<>
<div className="space-y-2">
<label
htmlFor="inventory-add-manufacturer"
className="text-sm font-medium"
>
{t("medications.detail.bestand.addManufacturerLabel")}
</label>
<Input
id="inventory-add-manufacturer"
value={manufacturer}
maxLength={120}
onChange={(e) => setManufacturer(e.target.value)}
/>
</div>
<div className="space-y-2">
<label
htmlFor="inventory-add-dose-strength"
className="text-sm font-medium"
>
{t("medications.detail.bestand.addDoseStrengthLabel")}
</label>
<Input
id="inventory-add-dose-strength"
value={doseStrength}
maxLength={60}
onChange={(e) => setDoseStrength(e.target.value)}
/>
<p className="text-muted-foreground text-sm">
{t("medications.detail.bestand.addDoseStrengthHint")}
</p>
</div>
</>
)}
</form>
</ResponsiveSheet>
);