fix(ui): line-item number inputs — allow clearing the 0 (store raw string, coerce at totals/save), unify value alignment, widen Množství column

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
BOHA
2026-06-09 17:38:28 +02:00
parent a3948c7da1
commit 28186397a0
4 changed files with 104 additions and 75 deletions

View File

@@ -166,9 +166,13 @@ interface InvoiceItem {
_key: string;
description: string;
item_description: string;
quantity: number;
// Held as the raw typed string while editing so the field can be cleared
// (empty renders fine in a type="number" input). Coerced via Number(x) || 0
// only where used (live totals + save payload). vat_rate stays numeric: it
// is edited via a Select, never a free-text number input.
quantity: string | number;
unit: string;
unit_price: number;
unit_price: string | number;
vat_rate: number;
}
@@ -332,10 +336,8 @@ function SortableInvoiceRow({
<TextField
label="Množství"
type="number"
value={item.quantity}
onChange={(e) =>
onUpdate(index, "quantity", Number(e.target.value))
}
value={item.quantity ?? ""}
onChange={(e) => onUpdate(index, "quantity", e.target.value)}
slotProps={{ htmlInput: { min: "0", step: "any" } }}
/>
<TextField
@@ -347,10 +349,8 @@ function SortableInvoiceRow({
<TextField
label="Jedn. cena"
type="number"
value={item.unit_price}
onChange={(e) =>
onUpdate(index, "unit_price", Number(e.target.value))
}
value={item.unit_price ?? ""}
onChange={(e) => onUpdate(index, "unit_price", e.target.value)}
slotProps={{ htmlInput: { step: "any" } }}
/>
{apply_vat && (
@@ -438,8 +438,8 @@ function SortableInvoiceRow({
<TableCell>
<TextField
type="number"
value={item.quantity}
onChange={(e) => onUpdate(index, "quantity", Number(e.target.value))}
value={item.quantity ?? ""}
onChange={(e) => onUpdate(index, "quantity", e.target.value)}
slotProps={{ htmlInput: { min: "0", step: "any" } }}
sx={{ "& input": { textAlign: "center" } }}
/>
@@ -455,10 +455,8 @@ function SortableInvoiceRow({
<TableCell>
<TextField
type="number"
value={item.unit_price}
onChange={(e) =>
onUpdate(index, "unit_price", Number(e.target.value))
}
value={item.unit_price ?? ""}
onChange={(e) => onUpdate(index, "unit_price", e.target.value)}
slotProps={{ htmlInput: { step: "any" } }}
sx={{ "& input": { textAlign: "right" } }}
/>
@@ -1036,6 +1034,10 @@ export default function InvoiceDetail() {
.filter((i) => i.description.trim())
.map((item, i) => ({
...item,
// Coerce the raw typed strings back to numbers so an empty/partial
// field never reaches the server as NaN (mirrors createTotals).
quantity: Number(item.quantity) || 0,
unit_price: Number(item.unit_price) || 0,
position: i,
})),
};
@@ -2065,7 +2067,7 @@ export default function InvoiceDetail() {
#
</TableCell>
<TableCell>Popis</TableCell>
<TableCell sx={{ width: "5.5rem" }} align="center">
<TableCell sx={{ width: "7rem" }} align="center">
Množství
</TableCell>
<TableCell sx={{ width: "5.5rem" }} align="center">