fix(ui): cross-module mechanical unifications (line-item labels/align, back-tab targets, status-button colors, apply_vat checkbox, currency-from-settings, read-only issued_by, Celkem header, centered offer tabs)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
BOHA
2026-06-09 16:49:31 +02:00
parent e868ecf769
commit 9fe5113c5b
6 changed files with 69 additions and 62 deletions

View File

@@ -62,6 +62,7 @@ import {
DateField, DateField,
Field, Field,
StatusChip, StatusChip,
CheckboxField,
ConfirmDialog, ConfirmDialog,
EmptyState, EmptyState,
LoadingState, LoadingState,
@@ -1146,7 +1147,7 @@ export default function InvoiceDetail() {
<Box sx={{ display: "flex", alignItems: "center", gap: 2 }}> <Box sx={{ display: "flex", alignItems: "center", gap: 2 }}>
<Button <Button
component={RouterLink} component={RouterLink}
to="/invoices" to="/invoices?tab=issued"
variant="outlined" variant="outlined"
color="inherit" color="inherit"
startIcon={BackIcon} startIcon={BackIcon}
@@ -1454,11 +1455,11 @@ export default function InvoiceDetail() {
<TableCell sx={{ width: "5rem" }} align="center"> <TableCell sx={{ width: "5rem" }} align="center">
Jednotka Jednotka
</TableCell> </TableCell>
<TableCell sx={{ width: "8rem" }} align="right"> <TableCell sx={{ width: "8rem" }} align="center">
Jedn. cena Jedn. cena
</TableCell> </TableCell>
<TableCell sx={{ width: "4rem" }} align="center"> <TableCell sx={{ width: "4rem" }} align="center">
%DPH DPH
</TableCell> </TableCell>
<TableCell sx={{ width: "9rem" }} align="right"> <TableCell sx={{ width: "9rem" }} align="right">
Celkem Celkem
@@ -1665,7 +1666,7 @@ export default function InvoiceDetail() {
<Box sx={{ display: "flex", alignItems: "center", gap: 2 }}> <Box sx={{ display: "flex", alignItems: "center", gap: 2 }}>
<Button <Button
component={RouterLink} component={RouterLink}
to="/invoices" to="/invoices?tab=issued"
variant="outlined" variant="outlined"
color="inherit" color="inherit"
startIcon={BackIcon} startIcon={BackIcon}
@@ -1748,8 +1749,16 @@ export default function InvoiceDetail() {
<Button <Button
key={status} key={status}
onClick={() => setStatusConfirm({ show: true, status })} onClick={() => setStatusConfirm({ show: true, status })}
variant={status === "paid" ? "contained" : "outlined"} variant={
color={status === "paid" ? "primary" : "inherit"} status === "cancelled" || status === "stornovana"
? "outlined"
: "contained"
}
color={
status === "cancelled" || status === "stornovana"
? "error"
: "primary"
}
disabled={statusChanging === status} disabled={statusChanging === status}
> >
{statusChanging === status ? ( {statusChanging === status ? (
@@ -1925,28 +1934,17 @@ export default function InvoiceDetail() {
/> />
</Field> </Field>
<Field label="DPH"> <Field label="DPH">
<Box <Box sx={{ display: "flex", alignItems: "center", height: 40 }}>
component="label" <CheckboxField
sx={{ label="Uplatnit DPH"
display: "flex",
alignItems: "center",
gap: 0.75,
whiteSpace: "nowrap",
cursor: "pointer",
height: 40,
}}
>
<input
type="checkbox"
checked={!!form.apply_vat} checked={!!form.apply_vat}
onChange={(e) => onChange={(v) =>
setForm((prev) => ({ setForm((prev) => ({
...prev, ...prev,
apply_vat: e.target.checked ? 1 : 0, apply_vat: v ? 1 : 0,
})) }))
} }
/> />
<Box component="span">Uplatnit DPH</Box>
</Box> </Box>
</Field> </Field>
</Box> </Box>

View File

@@ -43,6 +43,7 @@ import apiFetch from "../utils/api";
import { jsonQuery } from "../lib/apiAdapter"; import { jsonQuery } from "../lib/apiAdapter";
import { offerCustomersOptions, type Customer } from "../lib/queries/offers"; import { offerCustomersOptions, type Customer } from "../lib/queries/offers";
import { issuedOrderDetailOptions } from "../lib/queries/issued-orders"; import { issuedOrderDetailOptions } from "../lib/queries/issued-orders";
import { companySettingsOptions } from "../lib/queries/settings";
import { formatCurrency, todayLocalStr } from "../utils/formatters"; import { formatCurrency, todayLocalStr } from "../utils/formatters";
import { normalizeDateStr } from "../utils/attendanceHelpers"; import { normalizeDateStr } from "../utils/attendanceHelpers";
import { import {
@@ -53,6 +54,7 @@ import {
DateField, DateField,
Field, Field,
StatusChip, StatusChip,
CheckboxField,
ConfirmDialog, ConfirmDialog,
LoadingState, LoadingState,
PageEnter, PageEnter,
@@ -80,10 +82,7 @@ const VAT_OPTIONS = [0, 10, 12, 15, 21].map((v) => ({
label: `${v}%`, label: `${v}%`,
})); }));
const CURRENCY_OPTIONS = ["CZK", "EUR", "USD", "GBP"].map((c) => ({ const CURRENCY_FALLBACK = ["CZK", "EUR", "USD", "GBP"];
value: c,
label: c,
}));
const BackIcon = ( const BackIcon = (
<svg <svg
@@ -548,6 +547,13 @@ export default function IssuedOrderDetail() {
const customersQuery = useQuery(offerCustomersOptions()); const customersQuery = useQuery(offerCustomersOptions());
const customers = customersQuery.data ?? []; const customers = customersQuery.data ?? [];
const companySettings = useQuery(companySettingsOptions()).data;
// Configurable currency list from company settings (falls back to the
// built-in list when settings are empty) — matches Offers/Invoices.
const currencyOptions = (
companySettings?.available_currencies || CURRENCY_FALLBACK
).map((c) => ({ value: c, label: c }));
const detailQuery = useQuery(issuedOrderDetailOptions(id)); const detailQuery = useQuery(issuedOrderDetailOptions(id));
const detail = detailQuery.data ?? null; const detail = detailQuery.data ?? null;
@@ -1000,7 +1006,7 @@ export default function IssuedOrderDetail() {
onChange={(val) => onChange={(val) =>
setForm((prev) => ({ ...prev, currency: val })) setForm((prev) => ({ ...prev, currency: val }))
} }
options={CURRENCY_OPTIONS} options={currencyOptions}
/> />
</Field> </Field>
<Field label="Jazyk"> <Field label="Jazyk">
@@ -1027,29 +1033,18 @@ export default function IssuedOrderDetail() {
/> />
</Field> </Field>
<Field label="DPH"> <Field label="DPH">
<Box <Box sx={{ display: "flex", alignItems: "center", height: 40 }}>
component="label" <CheckboxField
sx={{ label="Uplatnit DPH"
display: "flex",
alignItems: "center",
gap: 0.75,
whiteSpace: "nowrap",
cursor: editable ? "pointer" : "default",
height: 40,
}}
>
<input
type="checkbox"
checked={form.apply_vat} checked={form.apply_vat}
disabled={!editable} disabled={!editable}
onChange={(e) => onChange={(v) =>
setForm((prev) => ({ setForm((prev) => ({
...prev, ...prev,
apply_vat: e.target.checked, apply_vat: v,
})) }))
} }
/> />
<Box component="span">Uplatnit DPH</Box>
</Box> </Box>
</Field> </Field>
</Box> </Box>
@@ -1057,11 +1052,8 @@ export default function IssuedOrderDetail() {
<Field label="Vystavil"> <Field label="Vystavil">
<TextField <TextField
value={form.issued_by} value={form.issued_by}
disabled={!editable} InputProps={{ readOnly: true }}
onChange={(e) => sx={{ "& .MuiInputBase-root": { bgcolor: "action.hover" } }}
setForm((prev) => ({ ...prev, issued_by: e.target.value }))
}
placeholder="Jméno"
/> />
</Field> </Field>
</Card> </Card>

View File

@@ -335,7 +335,7 @@ function SortableItemRow({
InputProps={{ readOnly }} InputProps={{ readOnly }}
/> />
<TextField <TextField
label="Cena/ks" 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", parseFloat(e.target.value))}
@@ -1167,14 +1167,25 @@ export default function OfferDetail() {
<Button <Button
onClick={() => setInvalidateConfirm(true)} onClick={() => setInvalidateConfirm(true)}
variant="outlined" variant="outlined"
color="inherit" color="error"
> >
Zneplatnit Zneplatnit
</Button> </Button>
)} )}
{!readOnly && ( {!readOnly && (
<Button onClick={handleSave} disabled={saving}> <Button onClick={handleSave} disabled={saving}>
{saving ? "Ukládání..." : "Uložit"} {saving ? (
<>
<CircularProgress
size={16}
color="inherit"
sx={{ mr: 1 }}
/>
Ukládání...
</>
) : (
"Uložit"
)}
</Button> </Button>
)} )}
{canDelete && hasPermission("offers.delete") && ( {canDelete && hasPermission("offers.delete") && (
@@ -1513,16 +1524,20 @@ export default function OfferDetail() {
# #
</TableCell> </TableCell>
<TableCell>Popis</TableCell> <TableCell>Popis</TableCell>
<TableCell sx={{ width: "5rem" }}>Množství</TableCell> <TableCell sx={{ width: "5.5rem" }} align="center">
Množství
</TableCell>
<TableCell sx={{ width: "5rem" }}>Jednotka</TableCell> <TableCell sx={{ width: "5rem" }}>Jednotka</TableCell>
<TableCell sx={{ width: "8rem" }}>Cena/ks</TableCell> <TableCell sx={{ width: "8rem" }} align="center">
Jedn. cena
</TableCell>
<TableCell sx={{ width: "4rem" }} align="center"> <TableCell sx={{ width: "4rem" }} align="center">
V ceně V ceně
</TableCell> </TableCell>
<TableCell sx={{ width: "8rem" }} align="right"> <TableCell sx={{ width: "8rem" }} align="right">
Celkem Celkem
</TableCell> </TableCell>
{!readOnly && <TableCell sx={{ width: "3rem" }} />} {!readOnly && <TableCell sx={{ width: "2.5rem" }} />}
</TableRow> </TableRow>
</TableHead> </TableHead>
<TableBody> <TableBody>

View File

@@ -741,7 +741,7 @@ export default function Offers() {
} }
/> />
<Box sx={{ mb: 2.5 }}> <Box sx={{ display: "flex", justifyContent: "center", mb: 2.5 }}>
<Tabs <Tabs
value={statusFilter} value={statusFilter}
onChange={(v) => { onChange={(v) => {

View File

@@ -16,6 +16,7 @@ import {
orderNextNumberOptions, orderNextNumberOptions,
} from "../lib/queries/orders"; } from "../lib/queries/orders";
import { offerCustomersOptions } from "../lib/queries/offers"; import { offerCustomersOptions } from "../lib/queries/offers";
import { companySettingsOptions } from "../lib/queries/settings";
import { useApiMutation } from "../lib/queries/mutations"; import { useApiMutation } from "../lib/queries/mutations";
import { import {
Card, Card,
@@ -186,6 +187,12 @@ export default function OrdersReceived({
...orderNextNumberOptions(), ...orderNextNumberOptions(),
enabled: createOpen, enabled: createOpen,
}); });
const companySettings = useQuery(companySettingsOptions()).data;
// Configurable currency list from company settings (falls back to the
// built-in list when settings are empty) — matches Offers/Invoices.
const currencyOptions = (
companySettings?.available_currencies || ["CZK", "EUR", "USD", "GBP"]
).map((c) => ({ value: c, label: c }));
const closeCreate = () => { const closeCreate = () => {
setCreateOpen(false); setCreateOpen(false);
@@ -556,12 +563,7 @@ export default function OrdersReceived({
onChange={(value) => onChange={(value) =>
setCreateForm({ ...createForm, currency: value }) setCreateForm({ ...createForm, currency: value })
} }
options={[ options={currencyOptions}
{ value: "CZK", label: "CZK" },
{ value: "EUR", label: "EUR" },
{ value: "USD", label: "USD" },
{ value: "GBP", label: "GBP" },
]}
/> />
</Field> </Field>
</Box> </Box>

View File

@@ -630,7 +630,7 @@ export default function ReceivedInvoices({
}, },
{ {
key: "amount", key: "amount",
header: "Částka", header: "Celkem",
width: "14%", width: "14%",
align: "right", align: "right",
sortKey: "amount", sortKey: "amount",