feat(ui): mobile card layout for the remaining item tables
OrderConfirmationModal (the order→confirmation editable items dialog) and InvoiceDetail's read-only paid-invoice items table now render stacked label:value cards on phones (< sm) instead of a wide horizontally-scrolling table; desktop keeps the table. Same pattern as the Offer/Invoice editors. Gates: tsc=0, build=0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,8 @@ import { useState, useCallback } from "react";
|
|||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
import IconButton from "@mui/material/IconButton";
|
import IconButton from "@mui/material/IconButton";
|
||||||
|
import useMediaQuery from "@mui/material/useMediaQuery";
|
||||||
|
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";
|
||||||
|
|
||||||
@@ -38,6 +40,8 @@ export default function OrderConfirmationModal({
|
|||||||
applyVat,
|
applyVat,
|
||||||
}: OrderConfirmationModalProps) {
|
}: OrderConfirmationModalProps) {
|
||||||
const alert = useAlert();
|
const alert = useAlert();
|
||||||
|
const theme = useTheme();
|
||||||
|
const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
|
||||||
const [step, setStep] = useState<"choose" | "edit">("choose");
|
const [step, setStep] = useState<"choose" | "edit">("choose");
|
||||||
const [lang, setLang] = useState<string>("cs");
|
const [lang, setLang] = useState<string>("cs");
|
||||||
const [applyVatState, setApplyVatState] = useState(applyVat);
|
const [applyVatState, setApplyVatState] = useState(applyVat);
|
||||||
@@ -193,116 +197,221 @@ export default function OrderConfirmationModal({
|
|||||||
Zpět
|
Zpět
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{ overflowX: "auto" }}>
|
{isMobile ? (
|
||||||
<Box
|
<Box sx={{ display: "flex", flexDirection: "column", gap: 1.5 }}>
|
||||||
component="table"
|
{items.map((item, i) => (
|
||||||
sx={{
|
<Box
|
||||||
width: "100%",
|
key={i}
|
||||||
borderCollapse: "collapse",
|
sx={{
|
||||||
"& th": {
|
border: 1,
|
||||||
textAlign: "left",
|
borderColor: "divider",
|
||||||
fontSize: ".7rem",
|
borderRadius: 2,
|
||||||
textTransform: "uppercase",
|
p: 1.5,
|
||||||
letterSpacing: ".05em",
|
display: "flex",
|
||||||
fontWeight: 700,
|
flexDirection: "column",
|
||||||
color: "text.secondary",
|
gap: 1,
|
||||||
pb: 1,
|
}}
|
||||||
},
|
>
|
||||||
"& td": { py: 0.5, pr: 1, verticalAlign: "middle" },
|
<Box sx={{ display: "flex", alignItems: "center", gap: 1 }}>
|
||||||
}}
|
<Typography
|
||||||
>
|
variant="caption"
|
||||||
<thead>
|
sx={{ color: "text.secondary", fontWeight: 700 }}
|
||||||
<tr>
|
>
|
||||||
<th>Popis</th>
|
Položka {i + 1}
|
||||||
<th>Mn.</th>
|
</Typography>
|
||||||
<th>Jedn.</th>
|
<Box sx={{ flex: 1 }} />
|
||||||
<th>Cena</th>
|
<IconButton
|
||||||
<th>%DPH</th>
|
size="small"
|
||||||
<th style={{ width: "40px" }} />
|
color="error"
|
||||||
</tr>
|
onClick={() => removeItem(i)}
|
||||||
</thead>
|
title="Odstranit"
|
||||||
<tbody>
|
aria-label="Odstranit"
|
||||||
{items.map((item, i) => (
|
>
|
||||||
<tr key={i}>
|
<svg
|
||||||
<td>
|
width="16"
|
||||||
<TextField
|
height="16"
|
||||||
value={item.description}
|
viewBox="0 0 24 24"
|
||||||
onChange={(e) =>
|
fill="none"
|
||||||
updateItem(i, "description", e.target.value)
|
stroke="currentColor"
|
||||||
}
|
strokeWidth="2"
|
||||||
sx={{ minWidth: 200 }}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<TextField
|
|
||||||
type="number"
|
|
||||||
value={item.quantity}
|
|
||||||
onChange={(e) =>
|
|
||||||
updateItem(i, "quantity", Number(e.target.value) || 0)
|
|
||||||
}
|
|
||||||
sx={{ width: 90 }}
|
|
||||||
slotProps={{ htmlInput: { step: "0.001" } }}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<TextField
|
|
||||||
value={item.unit}
|
|
||||||
onChange={(e) => updateItem(i, "unit", e.target.value)}
|
|
||||||
sx={{ width: 70 }}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<TextField
|
|
||||||
type="number"
|
|
||||||
value={item.unit_price}
|
|
||||||
onChange={(e) =>
|
|
||||||
updateItem(
|
|
||||||
i,
|
|
||||||
"unit_price",
|
|
||||||
Number(e.target.value) || 0,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
sx={{ width: 110 }}
|
|
||||||
slotProps={{ htmlInput: { step: "0.01" } }}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<TextField
|
|
||||||
type="number"
|
|
||||||
value={item.vat_rate}
|
|
||||||
onChange={(e) =>
|
|
||||||
updateItem(i, "vat_rate", Number(e.target.value) || 0)
|
|
||||||
}
|
|
||||||
sx={{ width: 80 }}
|
|
||||||
slotProps={{ htmlInput: { step: "1" } }}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<IconButton
|
|
||||||
size="small"
|
|
||||||
color="error"
|
|
||||||
onClick={() => removeItem(i)}
|
|
||||||
title="Odstranit"
|
|
||||||
aria-label="Odstranit"
|
|
||||||
>
|
>
|
||||||
<svg
|
<polyline points="3 6 5 6 21 6" />
|
||||||
width="16"
|
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
|
||||||
height="16"
|
</svg>
|
||||||
viewBox="0 0 24 24"
|
</IconButton>
|
||||||
fill="none"
|
</Box>
|
||||||
stroke="currentColor"
|
<TextField
|
||||||
strokeWidth="2"
|
label="Popis"
|
||||||
>
|
value={item.description}
|
||||||
<polyline points="3 6 5 6 21 6" />
|
onChange={(e) =>
|
||||||
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
|
updateItem(i, "description", e.target.value)
|
||||||
</svg>
|
}
|
||||||
</IconButton>
|
/>
|
||||||
</td>
|
<Box
|
||||||
</tr>
|
sx={{
|
||||||
))}
|
display: "grid",
|
||||||
</tbody>
|
gridTemplateColumns: "repeat(2, minmax(0, 1fr))",
|
||||||
|
gap: 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<TextField
|
||||||
|
label="Množství"
|
||||||
|
type="number"
|
||||||
|
value={item.quantity}
|
||||||
|
onChange={(e) =>
|
||||||
|
updateItem(i, "quantity", Number(e.target.value) || 0)
|
||||||
|
}
|
||||||
|
slotProps={{ htmlInput: { step: "0.001" } }}
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
label="Jednotka"
|
||||||
|
value={item.unit}
|
||||||
|
onChange={(e) => updateItem(i, "unit", e.target.value)}
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
label="Cena"
|
||||||
|
type="number"
|
||||||
|
value={item.unit_price}
|
||||||
|
onChange={(e) =>
|
||||||
|
updateItem(i, "unit_price", Number(e.target.value) || 0)
|
||||||
|
}
|
||||||
|
slotProps={{ htmlInput: { step: "0.01" } }}
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
label="%DPH"
|
||||||
|
type="number"
|
||||||
|
value={item.vat_rate}
|
||||||
|
onChange={(e) =>
|
||||||
|
updateItem(i, "vat_rate", Number(e.target.value) || 0)
|
||||||
|
}
|
||||||
|
slotProps={{ htmlInput: { step: "1" } }}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
) : (
|
||||||
|
<Box sx={{ overflowX: "auto" }}>
|
||||||
|
<Box
|
||||||
|
component="table"
|
||||||
|
sx={{
|
||||||
|
width: "100%",
|
||||||
|
borderCollapse: "collapse",
|
||||||
|
"& th": {
|
||||||
|
textAlign: "left",
|
||||||
|
fontSize: ".7rem",
|
||||||
|
textTransform: "uppercase",
|
||||||
|
letterSpacing: ".05em",
|
||||||
|
fontWeight: 700,
|
||||||
|
color: "text.secondary",
|
||||||
|
pb: 1,
|
||||||
|
},
|
||||||
|
"& td": { py: 0.5, pr: 1, verticalAlign: "middle" },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Popis</th>
|
||||||
|
<th>Mn.</th>
|
||||||
|
<th>Jedn.</th>
|
||||||
|
<th>Cena</th>
|
||||||
|
<th>%DPH</th>
|
||||||
|
<th style={{ width: "40px" }} />
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{items.map((item, i) => (
|
||||||
|
<tr key={i}>
|
||||||
|
<td>
|
||||||
|
<TextField
|
||||||
|
value={item.description}
|
||||||
|
onChange={(e) =>
|
||||||
|
updateItem(i, "description", e.target.value)
|
||||||
|
}
|
||||||
|
sx={{ minWidth: 200 }}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<TextField
|
||||||
|
type="number"
|
||||||
|
value={item.quantity}
|
||||||
|
onChange={(e) =>
|
||||||
|
updateItem(
|
||||||
|
i,
|
||||||
|
"quantity",
|
||||||
|
Number(e.target.value) || 0,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
sx={{ width: 90 }}
|
||||||
|
slotProps={{ htmlInput: { step: "0.001" } }}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<TextField
|
||||||
|
value={item.unit}
|
||||||
|
onChange={(e) =>
|
||||||
|
updateItem(i, "unit", e.target.value)
|
||||||
|
}
|
||||||
|
sx={{ width: 70 }}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<TextField
|
||||||
|
type="number"
|
||||||
|
value={item.unit_price}
|
||||||
|
onChange={(e) =>
|
||||||
|
updateItem(
|
||||||
|
i,
|
||||||
|
"unit_price",
|
||||||
|
Number(e.target.value) || 0,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
sx={{ width: 110 }}
|
||||||
|
slotProps={{ htmlInput: { step: "0.01" } }}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<TextField
|
||||||
|
type="number"
|
||||||
|
value={item.vat_rate}
|
||||||
|
onChange={(e) =>
|
||||||
|
updateItem(
|
||||||
|
i,
|
||||||
|
"vat_rate",
|
||||||
|
Number(e.target.value) || 0,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
sx={{ width: 80 }}
|
||||||
|
slotProps={{ htmlInput: { step: "1" } }}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<IconButton
|
||||||
|
size="small"
|
||||||
|
color="error"
|
||||||
|
onClick={() => removeItem(i)}
|
||||||
|
title="Odstranit"
|
||||||
|
aria-label="Odstranit"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
width="16"
|
||||||
|
height="16"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
>
|
||||||
|
<polyline points="3 6 5 6 21 6" />
|
||||||
|
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
|
||||||
|
</svg>
|
||||||
|
</IconButton>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
<Box sx={{ mt: 1.5 }}>
|
<Box sx={{ mt: 1.5 }}>
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
size="small"
|
||||||
|
|||||||
@@ -1263,93 +1263,106 @@ export default function InvoiceDetail() {
|
|||||||
Položky
|
Položky
|
||||||
</Typography>
|
</Typography>
|
||||||
{invoice.items && invoice.items.length > 0 ? (
|
{invoice.items && invoice.items.length > 0 ? (
|
||||||
<TableContainer sx={{ overflowX: "auto" }}>
|
isMobile ? (
|
||||||
<Table
|
<Box sx={{ display: "flex", flexDirection: "column", gap: 1.5 }}>
|
||||||
size="small"
|
{invoice.items.map((item, index) => {
|
||||||
sx={{ "& td, & th": { borderColor: "divider" } }}
|
const lineSubtotal =
|
||||||
>
|
(Number(item.quantity) || 0) *
|
||||||
<TableHead>
|
(Number(item.unit_price) || 0);
|
||||||
<TableRow>
|
const lineVat = Number(invoice.apply_vat)
|
||||||
<TableCell sx={{ width: "2.5rem" }} align="center">
|
? (lineSubtotal * (Number(item.vat_rate) || 0)) / 100
|
||||||
#
|
: 0;
|
||||||
</TableCell>
|
return (
|
||||||
<TableCell>Popis</TableCell>
|
<Box
|
||||||
<TableCell sx={{ width: "5.5rem" }} align="center">
|
key={item.id || index}
|
||||||
Množství
|
sx={{
|
||||||
</TableCell>
|
border: 1,
|
||||||
<TableCell sx={{ width: "5rem" }} align="center">
|
borderColor: "divider",
|
||||||
Jednotka
|
borderRadius: 2,
|
||||||
</TableCell>
|
p: 1.5,
|
||||||
<TableCell sx={{ width: "8rem" }} align="right">
|
display: "flex",
|
||||||
Jedn. cena
|
flexDirection: "column",
|
||||||
</TableCell>
|
gap: 0.75,
|
||||||
<TableCell sx={{ width: "4rem" }} align="center">
|
}}
|
||||||
%DPH
|
>
|
||||||
</TableCell>
|
<Box sx={{ fontWeight: 600 }}>
|
||||||
<TableCell sx={{ width: "9rem" }} align="right">
|
{item.description || "—"}
|
||||||
Celkem
|
{item.item_description && (
|
||||||
</TableCell>
|
<Typography
|
||||||
</TableRow>
|
variant="caption"
|
||||||
</TableHead>
|
sx={{
|
||||||
<TableBody>
|
display: "block",
|
||||||
{invoice.items.map((item, index) => {
|
opacity: 0.8,
|
||||||
const lineSubtotal =
|
fontWeight: 400,
|
||||||
(Number(item.quantity) || 0) *
|
}}
|
||||||
(Number(item.unit_price) || 0);
|
>
|
||||||
const lineVat = Number(invoice.apply_vat)
|
{item.item_description}
|
||||||
? (lineSubtotal * (Number(item.vat_rate) || 0)) / 100
|
</Typography>
|
||||||
: 0;
|
)}
|
||||||
return (
|
</Box>
|
||||||
<TableRow key={item.id || index}>
|
<Box
|
||||||
<TableCell
|
sx={{
|
||||||
align="center"
|
display: "flex",
|
||||||
sx={{ color: "text.secondary", fontWeight: 500 }}
|
justifyContent: "space-between",
|
||||||
>
|
fontSize: ".85rem",
|
||||||
{index + 1}
|
}}
|
||||||
</TableCell>
|
>
|
||||||
<TableCell sx={{ fontWeight: 500 }}>
|
<Typography variant="caption" color="text.secondary">
|
||||||
{item.description || "—"}
|
Množství
|
||||||
{item.item_description && (
|
</Typography>
|
||||||
<Typography
|
<span>
|
||||||
variant="caption"
|
{item.quantity} {item.unit}
|
||||||
sx={{
|
</span>
|
||||||
display: "block",
|
</Box>
|
||||||
opacity: 0.8,
|
<Box
|
||||||
mt: "2px",
|
sx={{
|
||||||
}}
|
display: "flex",
|
||||||
>
|
justifyContent: "space-between",
|
||||||
{item.item_description}
|
fontSize: ".85rem",
|
||||||
</Typography>
|
}}
|
||||||
)}
|
>
|
||||||
</TableCell>
|
<Typography variant="caption" color="text.secondary">
|
||||||
<TableCell align="center">
|
Jedn. cena
|
||||||
{item.quantity}{" "}
|
</Typography>
|
||||||
{item.unit && (
|
<Box
|
||||||
<Box
|
component="span"
|
||||||
component="span"
|
sx={{ fontFamily: "'DM Mono', Menlo, monospace" }}
|
||||||
sx={{ color: "text.secondary" }}
|
|
||||||
>
|
|
||||||
{item.unit}
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</TableCell>
|
|
||||||
<TableCell align="center">{item.unit || "—"}</TableCell>
|
|
||||||
<TableCell
|
|
||||||
align="right"
|
|
||||||
sx={{
|
|
||||||
fontFamily: "'DM Mono', Menlo, monospace",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{formatCurrency(item.unit_price, invoice.currency)}
|
{formatCurrency(item.unit_price, invoice.currency)}
|
||||||
</TableCell>
|
</Box>
|
||||||
<TableCell align="center">
|
</Box>
|
||||||
{Number(invoice.apply_vat)
|
{Number(invoice.apply_vat) ? (
|
||||||
? Number(item.vat_rate)
|
<Box
|
||||||
: 0}
|
sx={{
|
||||||
%
|
display: "flex",
|
||||||
</TableCell>
|
justifyContent: "space-between",
|
||||||
<TableCell
|
fontSize: ".85rem",
|
||||||
align="right"
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="caption" color="text.secondary">
|
||||||
|
%DPH
|
||||||
|
</Typography>
|
||||||
|
<span>{Number(item.vat_rate)}%</span>
|
||||||
|
</Box>
|
||||||
|
) : null}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "baseline",
|
||||||
|
pt: 0.5,
|
||||||
|
borderTop: 1,
|
||||||
|
borderColor: "divider",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
variant="caption"
|
||||||
|
sx={{ color: "text.secondary", fontWeight: 700 }}
|
||||||
|
>
|
||||||
|
Celkem
|
||||||
|
</Typography>
|
||||||
|
<Box
|
||||||
|
component="span"
|
||||||
sx={{
|
sx={{
|
||||||
fontFamily: "'DM Mono', Menlo, monospace",
|
fontFamily: "'DM Mono', Menlo, monospace",
|
||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
@@ -1359,13 +1372,119 @@ export default function InvoiceDetail() {
|
|||||||
lineSubtotal + lineVat,
|
lineSubtotal + lineVat,
|
||||||
invoice.currency,
|
invoice.currency,
|
||||||
)}
|
)}
|
||||||
</TableCell>
|
</Box>
|
||||||
</TableRow>
|
</Box>
|
||||||
);
|
</Box>
|
||||||
})}
|
);
|
||||||
</TableBody>
|
})}
|
||||||
</Table>
|
</Box>
|
||||||
</TableContainer>
|
) : (
|
||||||
|
<TableContainer sx={{ overflowX: "auto" }}>
|
||||||
|
<Table
|
||||||
|
size="small"
|
||||||
|
sx={{ "& td, & th": { borderColor: "divider" } }}
|
||||||
|
>
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell sx={{ width: "2.5rem" }} align="center">
|
||||||
|
#
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>Popis</TableCell>
|
||||||
|
<TableCell sx={{ width: "5.5rem" }} align="center">
|
||||||
|
Množství
|
||||||
|
</TableCell>
|
||||||
|
<TableCell sx={{ width: "5rem" }} align="center">
|
||||||
|
Jednotka
|
||||||
|
</TableCell>
|
||||||
|
<TableCell sx={{ width: "8rem" }} align="right">
|
||||||
|
Jedn. cena
|
||||||
|
</TableCell>
|
||||||
|
<TableCell sx={{ width: "4rem" }} align="center">
|
||||||
|
%DPH
|
||||||
|
</TableCell>
|
||||||
|
<TableCell sx={{ width: "9rem" }} align="right">
|
||||||
|
Celkem
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>
|
||||||
|
{invoice.items.map((item, index) => {
|
||||||
|
const lineSubtotal =
|
||||||
|
(Number(item.quantity) || 0) *
|
||||||
|
(Number(item.unit_price) || 0);
|
||||||
|
const lineVat = Number(invoice.apply_vat)
|
||||||
|
? (lineSubtotal * (Number(item.vat_rate) || 0)) / 100
|
||||||
|
: 0;
|
||||||
|
return (
|
||||||
|
<TableRow key={item.id || index}>
|
||||||
|
<TableCell
|
||||||
|
align="center"
|
||||||
|
sx={{ color: "text.secondary", fontWeight: 500 }}
|
||||||
|
>
|
||||||
|
{index + 1}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell sx={{ fontWeight: 500 }}>
|
||||||
|
{item.description || "—"}
|
||||||
|
{item.item_description && (
|
||||||
|
<Typography
|
||||||
|
variant="caption"
|
||||||
|
sx={{
|
||||||
|
display: "block",
|
||||||
|
opacity: 0.8,
|
||||||
|
mt: "2px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{item.item_description}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell align="center">
|
||||||
|
{item.quantity}{" "}
|
||||||
|
{item.unit && (
|
||||||
|
<Box
|
||||||
|
component="span"
|
||||||
|
sx={{ color: "text.secondary" }}
|
||||||
|
>
|
||||||
|
{item.unit}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell align="center">
|
||||||
|
{item.unit || "—"}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell
|
||||||
|
align="right"
|
||||||
|
sx={{
|
||||||
|
fontFamily: "'DM Mono', Menlo, monospace",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{formatCurrency(item.unit_price, invoice.currency)}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell align="center">
|
||||||
|
{Number(invoice.apply_vat)
|
||||||
|
? Number(item.vat_rate)
|
||||||
|
: 0}
|
||||||
|
%
|
||||||
|
</TableCell>
|
||||||
|
<TableCell
|
||||||
|
align="right"
|
||||||
|
sx={{
|
||||||
|
fontFamily: "'DM Mono', Menlo, monospace",
|
||||||
|
fontWeight: 600,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{formatCurrency(
|
||||||
|
lineSubtotal + lineVat,
|
||||||
|
invoice.currency,
|
||||||
|
)}
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
|
)
|
||||||
) : (
|
) : (
|
||||||
<EmptyState title="Žádné položky." />
|
<EmptyState title="Žádné položky." />
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user