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:
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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) => {
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
Reference in New Issue
Block a user