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:
@@ -7,7 +7,22 @@ import { useTheme } from "@mui/material/styles";
|
|||||||
import { Modal, Button, TextField, Field } from "../ui";
|
import { Modal, Button, TextField, Field } from "../ui";
|
||||||
import { useAlert } from "../context/AlertContext";
|
import { useAlert } from "../context/AlertContext";
|
||||||
|
|
||||||
|
// Editable line-item. quantity/unit_price/vat_rate are held as the raw typed
|
||||||
|
// string while editing (so a field can be cleared — empty renders fine in a
|
||||||
|
// type="number" input) and are coerced to numbers in handleEditGenerate before
|
||||||
|
// being handed to onGenerate (the parent posts them verbatim).
|
||||||
interface ConfirmationItem {
|
interface ConfirmationItem {
|
||||||
|
description: string;
|
||||||
|
quantity: string | number;
|
||||||
|
unit: string;
|
||||||
|
unit_price: string | number;
|
||||||
|
is_included_in_total: boolean;
|
||||||
|
vat_rate: string | number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Numeric shape handed to the parent (the raw editing strings are coerced in
|
||||||
|
// handleEditGenerate). Distinct from the editable ConfirmationItem.
|
||||||
|
interface GeneratedItem {
|
||||||
description: string;
|
description: string;
|
||||||
quantity: number;
|
quantity: number;
|
||||||
unit: string;
|
unit: string;
|
||||||
@@ -22,7 +37,7 @@ interface OrderConfirmationModalProps {
|
|||||||
onGenerate: (
|
onGenerate: (
|
||||||
lang: string,
|
lang: string,
|
||||||
applyVat: boolean,
|
applyVat: boolean,
|
||||||
items?: ConfirmationItem[],
|
items?: GeneratedItem[],
|
||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
initialItems: ConfirmationItem[];
|
initialItems: ConfirmationItem[];
|
||||||
orderNumber: string;
|
orderNumber: string;
|
||||||
@@ -81,7 +96,17 @@ export default function OrderConfirmationModal({
|
|||||||
const handleEditGenerate = async () => {
|
const handleEditGenerate = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
await onGenerate(lang, applyVatState, items);
|
// Coerce the raw typed strings back to numbers so an empty/partial field
|
||||||
|
// never reaches the server as NaN (the parent posts these verbatim).
|
||||||
|
const coercedItems: GeneratedItem[] = items.map((it) => ({
|
||||||
|
description: it.description,
|
||||||
|
quantity: Number(it.quantity) || 0,
|
||||||
|
unit: it.unit,
|
||||||
|
unit_price: Number(it.unit_price) || 0,
|
||||||
|
is_included_in_total: it.is_included_in_total,
|
||||||
|
vat_rate: Number(it.vat_rate) || 0,
|
||||||
|
}));
|
||||||
|
await onGenerate(lang, applyVatState, coercedItems);
|
||||||
// Only close on success — on error keep the user's edited items intact.
|
// Only close on success — on error keep the user's edited items intact.
|
||||||
onClose();
|
onClose();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -273,9 +298,9 @@ export default function OrderConfirmationModal({
|
|||||||
<TextField
|
<TextField
|
||||||
label="Množství"
|
label="Množství"
|
||||||
type="number"
|
type="number"
|
||||||
value={item.quantity}
|
value={item.quantity ?? ""}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
updateItem(i, "quantity", Number(e.target.value) || 0)
|
updateItem(i, "quantity", e.target.value)
|
||||||
}
|
}
|
||||||
slotProps={{ htmlInput: { step: "0.001" } }}
|
slotProps={{ htmlInput: { step: "0.001" } }}
|
||||||
/>
|
/>
|
||||||
@@ -287,18 +312,18 @@ export default function OrderConfirmationModal({
|
|||||||
<TextField
|
<TextField
|
||||||
label="Cena"
|
label="Cena"
|
||||||
type="number"
|
type="number"
|
||||||
value={item.unit_price}
|
value={item.unit_price ?? ""}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
updateItem(i, "unit_price", Number(e.target.value) || 0)
|
updateItem(i, "unit_price", e.target.value)
|
||||||
}
|
}
|
||||||
slotProps={{ htmlInput: { step: "0.01" } }}
|
slotProps={{ htmlInput: { step: "0.01" } }}
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
label="%DPH"
|
label="%DPH"
|
||||||
type="number"
|
type="number"
|
||||||
value={item.vat_rate}
|
value={item.vat_rate ?? ""}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
updateItem(i, "vat_rate", Number(e.target.value) || 0)
|
updateItem(i, "vat_rate", e.target.value)
|
||||||
}
|
}
|
||||||
slotProps={{ htmlInput: { step: "1" } }}
|
slotProps={{ htmlInput: { step: "1" } }}
|
||||||
/>
|
/>
|
||||||
@@ -350,15 +375,11 @@ export default function OrderConfirmationModal({
|
|||||||
<td>
|
<td>
|
||||||
<TextField
|
<TextField
|
||||||
type="number"
|
type="number"
|
||||||
value={item.quantity}
|
value={item.quantity ?? ""}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
updateItem(
|
updateItem(i, "quantity", e.target.value)
|
||||||
i,
|
|
||||||
"quantity",
|
|
||||||
Number(e.target.value) || 0,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
sx={{ width: 90 }}
|
sx={{ width: 112 }}
|
||||||
slotProps={{ htmlInput: { step: "0.001" } }}
|
slotProps={{ htmlInput: { step: "0.001" } }}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
@@ -374,13 +395,9 @@ export default function OrderConfirmationModal({
|
|||||||
<td>
|
<td>
|
||||||
<TextField
|
<TextField
|
||||||
type="number"
|
type="number"
|
||||||
value={item.unit_price}
|
value={item.unit_price ?? ""}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
updateItem(
|
updateItem(i, "unit_price", e.target.value)
|
||||||
i,
|
|
||||||
"unit_price",
|
|
||||||
Number(e.target.value) || 0,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
sx={{ width: 110 }}
|
sx={{ width: 110 }}
|
||||||
slotProps={{ htmlInput: { step: "0.01" } }}
|
slotProps={{ htmlInput: { step: "0.01" } }}
|
||||||
@@ -389,13 +406,9 @@ export default function OrderConfirmationModal({
|
|||||||
<td>
|
<td>
|
||||||
<TextField
|
<TextField
|
||||||
type="number"
|
type="number"
|
||||||
value={item.vat_rate}
|
value={item.vat_rate ?? ""}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
updateItem(
|
updateItem(i, "vat_rate", e.target.value)
|
||||||
i,
|
|
||||||
"vat_rate",
|
|
||||||
Number(e.target.value) || 0,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
sx={{ width: 80 }}
|
sx={{ width: 80 }}
|
||||||
slotProps={{ htmlInput: { step: "1" } }}
|
slotProps={{ htmlInput: { step: "1" } }}
|
||||||
|
|||||||
@@ -166,9 +166,13 @@ interface InvoiceItem {
|
|||||||
_key: string;
|
_key: string;
|
||||||
description: string;
|
description: string;
|
||||||
item_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: string;
|
||||||
unit_price: number;
|
unit_price: string | number;
|
||||||
vat_rate: number;
|
vat_rate: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,10 +336,8 @@ function SortableInvoiceRow({
|
|||||||
<TextField
|
<TextField
|
||||||
label="Množství"
|
label="Množství"
|
||||||
type="number"
|
type="number"
|
||||||
value={item.quantity}
|
value={item.quantity ?? ""}
|
||||||
onChange={(e) =>
|
onChange={(e) => onUpdate(index, "quantity", e.target.value)}
|
||||||
onUpdate(index, "quantity", Number(e.target.value))
|
|
||||||
}
|
|
||||||
slotProps={{ htmlInput: { min: "0", step: "any" } }}
|
slotProps={{ htmlInput: { min: "0", step: "any" } }}
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
@@ -347,10 +349,8 @@ function SortableInvoiceRow({
|
|||||||
<TextField
|
<TextField
|
||||||
label="Jedn. cena"
|
label="Jedn. cena"
|
||||||
type="number"
|
type="number"
|
||||||
value={item.unit_price}
|
value={item.unit_price ?? ""}
|
||||||
onChange={(e) =>
|
onChange={(e) => onUpdate(index, "unit_price", e.target.value)}
|
||||||
onUpdate(index, "unit_price", Number(e.target.value))
|
|
||||||
}
|
|
||||||
slotProps={{ htmlInput: { step: "any" } }}
|
slotProps={{ htmlInput: { step: "any" } }}
|
||||||
/>
|
/>
|
||||||
{apply_vat && (
|
{apply_vat && (
|
||||||
@@ -438,8 +438,8 @@ function SortableInvoiceRow({
|
|||||||
<TableCell>
|
<TableCell>
|
||||||
<TextField
|
<TextField
|
||||||
type="number"
|
type="number"
|
||||||
value={item.quantity}
|
value={item.quantity ?? ""}
|
||||||
onChange={(e) => onUpdate(index, "quantity", Number(e.target.value))}
|
onChange={(e) => onUpdate(index, "quantity", e.target.value)}
|
||||||
slotProps={{ htmlInput: { min: "0", step: "any" } }}
|
slotProps={{ htmlInput: { min: "0", step: "any" } }}
|
||||||
sx={{ "& input": { textAlign: "center" } }}
|
sx={{ "& input": { textAlign: "center" } }}
|
||||||
/>
|
/>
|
||||||
@@ -455,10 +455,8 @@ function SortableInvoiceRow({
|
|||||||
<TableCell>
|
<TableCell>
|
||||||
<TextField
|
<TextField
|
||||||
type="number"
|
type="number"
|
||||||
value={item.unit_price}
|
value={item.unit_price ?? ""}
|
||||||
onChange={(e) =>
|
onChange={(e) => onUpdate(index, "unit_price", e.target.value)}
|
||||||
onUpdate(index, "unit_price", Number(e.target.value))
|
|
||||||
}
|
|
||||||
slotProps={{ htmlInput: { step: "any" } }}
|
slotProps={{ htmlInput: { step: "any" } }}
|
||||||
sx={{ "& input": { textAlign: "right" } }}
|
sx={{ "& input": { textAlign: "right" } }}
|
||||||
/>
|
/>
|
||||||
@@ -1036,6 +1034,10 @@ export default function InvoiceDetail() {
|
|||||||
.filter((i) => i.description.trim())
|
.filter((i) => i.description.trim())
|
||||||
.map((item, i) => ({
|
.map((item, i) => ({
|
||||||
...item,
|
...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,
|
position: i,
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
@@ -2065,7 +2067,7 @@ export default function InvoiceDetail() {
|
|||||||
#
|
#
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>Popis</TableCell>
|
<TableCell>Popis</TableCell>
|
||||||
<TableCell sx={{ width: "5.5rem" }} align="center">
|
<TableCell sx={{ width: "7rem" }} align="center">
|
||||||
Množství
|
Množství
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell sx={{ width: "5.5rem" }} align="center">
|
<TableCell sx={{ width: "5.5rem" }} align="center">
|
||||||
|
|||||||
@@ -141,9 +141,13 @@ interface OrderItem {
|
|||||||
_key: string;
|
_key: string;
|
||||||
description: string;
|
description: string;
|
||||||
item_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: string;
|
||||||
unit_price: number;
|
unit_price: string | number;
|
||||||
vat_rate: number;
|
vat_rate: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,10 +288,8 @@ function SortableOrderRow({
|
|||||||
<TextField
|
<TextField
|
||||||
label="Množství"
|
label="Množství"
|
||||||
type="number"
|
type="number"
|
||||||
value={item.quantity}
|
value={item.quantity ?? ""}
|
||||||
onChange={(e) =>
|
onChange={(e) => onUpdate(index, "quantity", e.target.value)}
|
||||||
onUpdate(index, "quantity", Number(e.target.value))
|
|
||||||
}
|
|
||||||
slotProps={{ htmlInput: { min: "0", step: "any" } }}
|
slotProps={{ htmlInput: { min: "0", step: "any" } }}
|
||||||
InputProps={{ readOnly }}
|
InputProps={{ readOnly }}
|
||||||
/>
|
/>
|
||||||
@@ -301,10 +303,8 @@ function SortableOrderRow({
|
|||||||
<TextField
|
<TextField
|
||||||
label="Jedn. cena"
|
label="Jedn. cena"
|
||||||
type="number"
|
type="number"
|
||||||
value={item.unit_price}
|
value={item.unit_price ?? ""}
|
||||||
onChange={(e) =>
|
onChange={(e) => onUpdate(index, "unit_price", e.target.value)}
|
||||||
onUpdate(index, "unit_price", Number(e.target.value))
|
|
||||||
}
|
|
||||||
slotProps={{ htmlInput: { step: "any" } }}
|
slotProps={{ htmlInput: { step: "any" } }}
|
||||||
InputProps={{ readOnly }}
|
InputProps={{ readOnly }}
|
||||||
/>
|
/>
|
||||||
@@ -401,8 +401,8 @@ function SortableOrderRow({
|
|||||||
<TableCell>
|
<TableCell>
|
||||||
<TextField
|
<TextField
|
||||||
type="number"
|
type="number"
|
||||||
value={item.quantity}
|
value={item.quantity ?? ""}
|
||||||
onChange={(e) => onUpdate(index, "quantity", Number(e.target.value))}
|
onChange={(e) => onUpdate(index, "quantity", e.target.value)}
|
||||||
slotProps={{ htmlInput: { min: "0", step: "any" } }}
|
slotProps={{ htmlInput: { min: "0", step: "any" } }}
|
||||||
InputProps={{ readOnly }}
|
InputProps={{ readOnly }}
|
||||||
sx={{ "& input": { textAlign: "center" } }}
|
sx={{ "& input": { textAlign: "center" } }}
|
||||||
@@ -420,10 +420,8 @@ function SortableOrderRow({
|
|||||||
<TableCell>
|
<TableCell>
|
||||||
<TextField
|
<TextField
|
||||||
type="number"
|
type="number"
|
||||||
value={item.unit_price}
|
value={item.unit_price ?? ""}
|
||||||
onChange={(e) =>
|
onChange={(e) => onUpdate(index, "unit_price", e.target.value)}
|
||||||
onUpdate(index, "unit_price", Number(e.target.value))
|
|
||||||
}
|
|
||||||
slotProps={{ htmlInput: { step: "any" } }}
|
slotProps={{ htmlInput: { step: "any" } }}
|
||||||
InputProps={{ readOnly }}
|
InputProps={{ readOnly }}
|
||||||
sx={{ "& input": { textAlign: "right" } }}
|
sx={{ "& input": { textAlign: "right" } }}
|
||||||
@@ -740,9 +738,11 @@ export default function IssuedOrderDetail() {
|
|||||||
.map((it, i) => ({
|
.map((it, i) => ({
|
||||||
description: it.description,
|
description: it.description,
|
||||||
item_description: it.item_description,
|
item_description: it.item_description,
|
||||||
quantity: it.quantity,
|
// Coerce the raw typed strings back to numbers so an empty/partial
|
||||||
|
// field never reaches the server as NaN (mirrors the totals math).
|
||||||
|
quantity: Number(it.quantity) || 0,
|
||||||
unit: it.unit,
|
unit: it.unit,
|
||||||
unit_price: it.unit_price,
|
unit_price: Number(it.unit_price) || 0,
|
||||||
vat_rate: it.vat_rate,
|
vat_rate: it.vat_rate,
|
||||||
position: i,
|
position: i,
|
||||||
})),
|
})),
|
||||||
@@ -1135,7 +1135,7 @@ export default function IssuedOrderDetail() {
|
|||||||
#
|
#
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>Popis</TableCell>
|
<TableCell>Popis</TableCell>
|
||||||
<TableCell sx={{ width: "5.5rem" }} align="center">
|
<TableCell sx={{ width: "7rem" }} align="center">
|
||||||
Množství
|
Množství
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell sx={{ width: "5.5rem" }} align="center">
|
<TableCell sx={{ width: "5.5rem" }} align="center">
|
||||||
|
|||||||
@@ -87,9 +87,12 @@ interface OfferItem {
|
|||||||
id?: number;
|
id?: number;
|
||||||
description: string;
|
description: string;
|
||||||
item_description: string;
|
item_description: string;
|
||||||
quantity: number;
|
// Held as the raw typed string while editing so the field can be cleared
|
||||||
|
// (an empty string renders fine in a type="number" input). Coerced via
|
||||||
|
// Number(x) || 0 only where used (live totals + save payload).
|
||||||
|
quantity: string | number;
|
||||||
unit: string;
|
unit: string;
|
||||||
unit_price: number;
|
unit_price: string | number;
|
||||||
is_included_in_total: boolean;
|
is_included_in_total: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,10 +326,11 @@ function SortableItemRow({
|
|||||||
<TextField
|
<TextField
|
||||||
label="Množství"
|
label="Množství"
|
||||||
type="number"
|
type="number"
|
||||||
value={item.quantity}
|
value={item.quantity ?? ""}
|
||||||
onChange={(e) => onUpdate("quantity", parseInt(e.target.value, 10))}
|
onChange={(e) => onUpdate("quantity", e.target.value)}
|
||||||
slotProps={{ htmlInput: { step: "1" } }}
|
slotProps={{ htmlInput: { step: "1" } }}
|
||||||
InputProps={{ readOnly }}
|
InputProps={{ readOnly }}
|
||||||
|
sx={{ "& input": { textAlign: "center" } }}
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
label="Jednotka"
|
label="Jednotka"
|
||||||
@@ -337,10 +341,11 @@ function SortableItemRow({
|
|||||||
<TextField
|
<TextField
|
||||||
label="Jedn. cena"
|
label="Jedn. cena"
|
||||||
type="number"
|
type="number"
|
||||||
value={item.unit_price}
|
value={item.unit_price ?? ""}
|
||||||
onChange={(e) => onUpdate("unit_price", parseFloat(e.target.value))}
|
onChange={(e) => onUpdate("unit_price", e.target.value)}
|
||||||
slotProps={{ htmlInput: { step: "0.01" } }}
|
slotProps={{ htmlInput: { step: "0.01" } }}
|
||||||
InputProps={{ readOnly }}
|
InputProps={{ readOnly }}
|
||||||
|
sx={{ "& input": { textAlign: "right" } }}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
<Box
|
||||||
@@ -437,10 +442,11 @@ function SortableItemRow({
|
|||||||
<TableCell>
|
<TableCell>
|
||||||
<TextField
|
<TextField
|
||||||
type="number"
|
type="number"
|
||||||
value={item.quantity}
|
value={item.quantity ?? ""}
|
||||||
onChange={(e) => onUpdate("quantity", parseInt(e.target.value, 10))}
|
onChange={(e) => onUpdate("quantity", e.target.value)}
|
||||||
slotProps={{ htmlInput: { step: "1" } }}
|
slotProps={{ htmlInput: { step: "1" } }}
|
||||||
InputProps={{ readOnly }}
|
InputProps={{ readOnly }}
|
||||||
|
sx={{ "& input": { textAlign: "center" } }}
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
@@ -453,10 +459,11 @@ function SortableItemRow({
|
|||||||
<TableCell>
|
<TableCell>
|
||||||
<TextField
|
<TextField
|
||||||
type="number"
|
type="number"
|
||||||
value={item.unit_price}
|
value={item.unit_price ?? ""}
|
||||||
onChange={(e) => onUpdate("unit_price", parseFloat(e.target.value))}
|
onChange={(e) => onUpdate("unit_price", e.target.value)}
|
||||||
slotProps={{ htmlInput: { step: "0.01" } }}
|
slotProps={{ htmlInput: { step: "0.01" } }}
|
||||||
InputProps={{ readOnly }}
|
InputProps={{ readOnly }}
|
||||||
|
sx={{ "& input": { textAlign: "right" } }}
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell align="center">
|
<TableCell align="center">
|
||||||
@@ -882,7 +889,14 @@ export default function OfferDetail() {
|
|||||||
const url = isEdit ? `${API_BASE}/offers/${id}` : `${API_BASE}/offers`;
|
const url = isEdit ? `${API_BASE}/offers/${id}` : `${API_BASE}/offers`;
|
||||||
const payload: any = {
|
const payload: any = {
|
||||||
...form,
|
...form,
|
||||||
items: items.map((item, i) => ({ ...item, position: i })),
|
items: items.map((item, i) => ({
|
||||||
|
...item,
|
||||||
|
// Coerce the raw typed strings back to numbers so an empty/partial
|
||||||
|
// field never reaches the server as NaN (mirrors the totals math).
|
||||||
|
quantity: Number(item.quantity) || 0,
|
||||||
|
unit_price: Number(item.unit_price) || 0,
|
||||||
|
position: i,
|
||||||
|
})),
|
||||||
sections: sections.map((s, i) => ({ ...s, position: i })),
|
sections: sections.map((s, i) => ({ ...s, position: i })),
|
||||||
};
|
};
|
||||||
if (!isEdit) delete payload.quotation_number;
|
if (!isEdit) delete payload.quotation_number;
|
||||||
@@ -1524,7 +1538,7 @@ export default function OfferDetail() {
|
|||||||
#
|
#
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>Popis</TableCell>
|
<TableCell>Popis</TableCell>
|
||||||
<TableCell sx={{ width: "5.5rem" }} align="center">
|
<TableCell sx={{ width: "7rem" }} align="center">
|
||||||
Množství
|
Množství
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell sx={{ width: "5rem" }}>Jednotka</TableCell>
|
<TableCell sx={{ width: "5rem" }}>Jednotka</TableCell>
|
||||||
|
|||||||
Reference in New Issue
Block a user