refactor(ui): single shared documentStatus map across offers/invoices/orders (fix issued color drift, add offers status chip)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
101
src/admin/lib/documentStatus.ts
Normal file
101
src/admin/lib/documentStatus.ts
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
/**
|
||||||
|
* Single source of truth for document status labels (Czech) + chip colors
|
||||||
|
* (MUI semantic) across the Offers / Invoices / Orders modules.
|
||||||
|
*
|
||||||
|
* Previously every list AND detail page defined its own `STATUS_LABELS` +
|
||||||
|
* `STATUS_COLORS` maps. They drifted — most notably the issued-invoice status
|
||||||
|
* was `warning` in the list but `info` in the detail. This module consolidates
|
||||||
|
* them so the chip color/label for a given status is identical everywhere.
|
||||||
|
*
|
||||||
|
* Status KEYS are the ACTUAL values stored in the database and are kept AS-IS
|
||||||
|
* (e.g. customer orders are still keyed in Czech: `prijata` / `v_realizaci` /
|
||||||
|
* …). A Czech→English rename is a separate, deferred DB migration.
|
||||||
|
*
|
||||||
|
* The color union matches the kit `StatusChip` (src/admin/ui/StatusChip.tsx).
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type StatusColor = "default" | "info" | "warning" | "success" | "error";
|
||||||
|
|
||||||
|
export interface StatusMeta {
|
||||||
|
label: string;
|
||||||
|
color: StatusColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OFFER / quotations. Stored statuses are `active` (default) / `ordered` /
|
||||||
|
* `invalidated` (see offers.service.ts). Labels are taken from the Offers
|
||||||
|
* filter tabs; colors are synthesized to match the list row tints
|
||||||
|
* (active = neutral-active info, ordered = success, invalidated = error).
|
||||||
|
* `expired` is a defensive entry — it is a FRONT-END derived state (computed
|
||||||
|
* from `valid_until`), not a stored status, but mapping it keeps any future
|
||||||
|
* stored value rendering consistently (warning, matching the expired row wash).
|
||||||
|
*/
|
||||||
|
export const OFFER_STATUS: Record<string, StatusMeta> = {
|
||||||
|
active: { label: "Aktivní", color: "info" },
|
||||||
|
ordered: { label: "Objednaná", color: "success" },
|
||||||
|
invalidated: { label: "Zneplatněná", color: "error" },
|
||||||
|
expired: { label: "Po platnosti", color: "warning" },
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CUSTOMER ORDER / objednávka přijatá (orders). Czech-keyed (legacy-PHP
|
||||||
|
* carryover; rename is a deferred migration).
|
||||||
|
*/
|
||||||
|
export const ORDER_STATUS: Record<string, StatusMeta> = {
|
||||||
|
prijata: { label: "Přijatá", color: "info" },
|
||||||
|
v_realizaci: { label: "V realizaci", color: "warning" },
|
||||||
|
dokoncena: { label: "Dokončená", color: "success" },
|
||||||
|
stornovana: { label: "Stornována", color: "error" },
|
||||||
|
};
|
||||||
|
|
||||||
|
/** ISSUED ORDER / objednávka vydaná (issued_orders). */
|
||||||
|
export const ISSUED_ORDER_STATUS: Record<string, StatusMeta> = {
|
||||||
|
draft: { label: "Koncept", color: "default" },
|
||||||
|
sent: { label: "Odeslaná", color: "info" },
|
||||||
|
confirmed: { label: "Potvrzená", color: "warning" },
|
||||||
|
completed: { label: "Dokončená", color: "success" },
|
||||||
|
cancelled: { label: "Stornovaná", color: "error" },
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ISSUED INVOICE (invoices). `issued` resolves to `warning` (payment-pending
|
||||||
|
* = warning, NOT info) — this fixes the list↔detail color drift.
|
||||||
|
*/
|
||||||
|
export const INVOICE_STATUS: Record<string, StatusMeta> = {
|
||||||
|
issued: { label: "Vystavena", color: "warning" },
|
||||||
|
overdue: { label: "Po splatnosti", color: "error" },
|
||||||
|
paid: { label: "Zaplacena", color: "success" },
|
||||||
|
};
|
||||||
|
|
||||||
|
/** RECEIVED INVOICE (received_invoices). */
|
||||||
|
export const RECEIVED_INVOICE_STATUS: Record<string, StatusMeta> = {
|
||||||
|
unpaid: { label: "Neuhrazena", color: "warning" },
|
||||||
|
paid: { label: "Uhrazena", color: "success" },
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Label with a safe fallback for an unknown/empty key. */
|
||||||
|
export const statusLabel = (
|
||||||
|
m: Record<string, StatusMeta>,
|
||||||
|
k: string | null | undefined,
|
||||||
|
): string => (k && m[k]?.label) || k || "—";
|
||||||
|
|
||||||
|
/** Chip color with a safe fallback for an unknown/empty key. */
|
||||||
|
export const statusColor = (
|
||||||
|
m: Record<string, StatusMeta>,
|
||||||
|
k: string | null | undefined,
|
||||||
|
): StatusColor => (k && m[k]?.color) || "default";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build `{ value, label }` filter options from a status map, optionally
|
||||||
|
* prefixed with an "all" entry. Used by list-page status filters.
|
||||||
|
*/
|
||||||
|
export const statusOptions = (
|
||||||
|
m: Record<string, StatusMeta>,
|
||||||
|
allOption?: { value: string; label: string },
|
||||||
|
): { value: string; label: string }[] => {
|
||||||
|
const opts = Object.entries(m).map(([value, { label }]) => ({
|
||||||
|
value,
|
||||||
|
label,
|
||||||
|
}));
|
||||||
|
return allOption ? [allOption, ...opts] : opts;
|
||||||
|
};
|
||||||
@@ -67,6 +67,11 @@ import {
|
|||||||
RichTextView,
|
RichTextView,
|
||||||
headerActionsSx,
|
headerActionsSx,
|
||||||
} from "../ui";
|
} from "../ui";
|
||||||
|
import {
|
||||||
|
INVOICE_STATUS,
|
||||||
|
statusLabel,
|
||||||
|
statusColor,
|
||||||
|
} from "../lib/documentStatus";
|
||||||
|
|
||||||
const API_BASE = "/api/admin";
|
const API_BASE = "/api/admin";
|
||||||
|
|
||||||
@@ -97,21 +102,6 @@ function addDaysLocalStr(days: number, base?: string): string {
|
|||||||
return `${result.getFullYear()}-${mm}-${dd}`;
|
return `${result.getFullYear()}-${mm}-${dd}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const STATUS_LABELS: Record<string, string> = {
|
|
||||||
issued: "Vystavena",
|
|
||||||
paid: "Zaplacena",
|
|
||||||
overdue: "Po splatnosti",
|
|
||||||
};
|
|
||||||
|
|
||||||
const STATUS_COLORS: Record<
|
|
||||||
string,
|
|
||||||
"default" | "success" | "error" | "warning" | "info"
|
|
||||||
> = {
|
|
||||||
issued: "info",
|
|
||||||
paid: "success",
|
|
||||||
overdue: "error",
|
|
||||||
};
|
|
||||||
|
|
||||||
const TRANSITION_LABELS: Record<string, string> = { paid: "Zaplaceno" };
|
const TRANSITION_LABELS: Record<string, string> = { paid: "Zaplaceno" };
|
||||||
|
|
||||||
const BackIcon = (
|
const BackIcon = (
|
||||||
@@ -1141,8 +1131,8 @@ export default function InvoiceDetail() {
|
|||||||
Faktura {invoice.invoice_number}
|
Faktura {invoice.invoice_number}
|
||||||
</Typography>
|
</Typography>
|
||||||
<StatusChip
|
<StatusChip
|
||||||
label={STATUS_LABELS[invoice.status] || invoice.status}
|
label={statusLabel(INVOICE_STATUS, invoice.status)}
|
||||||
color={STATUS_COLORS[invoice.status] || "default"}
|
color={statusColor(INVOICE_STATUS, invoice.status)}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -1658,8 +1648,8 @@ export default function InvoiceDetail() {
|
|||||||
Faktura {invoice.invoice_number}
|
Faktura {invoice.invoice_number}
|
||||||
</Typography>
|
</Typography>
|
||||||
<StatusChip
|
<StatusChip
|
||||||
label={STATUS_LABELS[invoice.status] || invoice.status}
|
label={statusLabel(INVOICE_STATUS, invoice.status)}
|
||||||
color={STATUS_COLORS[invoice.status] || "default"}
|
color={statusColor(INVOICE_STATUS, invoice.status)}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
) : (
|
) : (
|
||||||
@@ -2249,7 +2239,7 @@ export default function InvoiceDetail() {
|
|||||||
onClose={() => setStatusConfirm({ show: false, status: null })}
|
onClose={() => setStatusConfirm({ show: false, status: null })}
|
||||||
onConfirm={handleStatusChange}
|
onConfirm={handleStatusChange}
|
||||||
title="Změnit stav faktury"
|
title="Změnit stav faktury"
|
||||||
message={`Opravdu chcete změnit stav faktury "${invoice.invoice_number}" na "${STATUS_LABELS[statusConfirm.status || ""]}"?`}
|
message={`Opravdu chcete změnit stav faktury "${invoice.invoice_number}" na "${statusLabel(INVOICE_STATUS, statusConfirm.status)}"?`}
|
||||||
confirmText={
|
confirmText={
|
||||||
TRANSITION_LABELS[statusConfirm.status || ""] || "Potvrdit"
|
TRANSITION_LABELS[statusConfirm.status || ""] || "Potvrdit"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,11 @@ import {
|
|||||||
type TabDef,
|
type TabDef,
|
||||||
type StatCardColor,
|
type StatCardColor,
|
||||||
} from "../ui";
|
} from "../ui";
|
||||||
|
import {
|
||||||
|
INVOICE_STATUS,
|
||||||
|
statusLabel,
|
||||||
|
statusColor,
|
||||||
|
} from "../lib/documentStatus";
|
||||||
|
|
||||||
const ReceivedInvoices = lazy(() => import("./ReceivedInvoices"));
|
const ReceivedInvoices = lazy(() => import("./ReceivedInvoices"));
|
||||||
const API_BASE = "/api/admin";
|
const API_BASE = "/api/admin";
|
||||||
@@ -86,21 +91,6 @@ function formatCzkWithDetail(
|
|||||||
return { value: formatMultiCurrency(amounts), detail: null };
|
return { value: formatMultiCurrency(amounts), detail: null };
|
||||||
}
|
}
|
||||||
|
|
||||||
const STATUS_LABELS: Record<string, string> = {
|
|
||||||
issued: "Vystavena",
|
|
||||||
paid: "Zaplacena",
|
|
||||||
overdue: "Po splatnosti",
|
|
||||||
};
|
|
||||||
|
|
||||||
const STATUS_COLORS: Record<
|
|
||||||
string,
|
|
||||||
"default" | "success" | "warning" | "error"
|
|
||||||
> = {
|
|
||||||
issued: "warning",
|
|
||||||
paid: "success",
|
|
||||||
overdue: "error",
|
|
||||||
};
|
|
||||||
|
|
||||||
const STATUS_FILTERS = [
|
const STATUS_FILTERS = [
|
||||||
{ value: "", label: "Vše" },
|
{ value: "", label: "Vše" },
|
||||||
{ value: "issued", label: "Vystavené" },
|
{ value: "issued", label: "Vystavené" },
|
||||||
@@ -560,13 +550,13 @@ export default function Invoices() {
|
|||||||
render: (inv) =>
|
render: (inv) =>
|
||||||
inv.status === "paid" ? (
|
inv.status === "paid" ? (
|
||||||
<StatusChip
|
<StatusChip
|
||||||
label={STATUS_LABELS[inv.status]}
|
label={statusLabel(INVOICE_STATUS, inv.status)}
|
||||||
color={STATUS_COLORS[inv.status] ?? "default"}
|
color={statusColor(INVOICE_STATUS, inv.status)}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<StatusChip
|
<StatusChip
|
||||||
label={STATUS_LABELS[inv.status] || inv.status}
|
label={statusLabel(INVOICE_STATUS, inv.status)}
|
||||||
color={STATUS_COLORS[inv.status] ?? "default"}
|
color={statusColor(INVOICE_STATUS, inv.status)}
|
||||||
onClick={() => toggleStatus(inv)}
|
onClick={() => toggleStatus(inv)}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -58,28 +58,14 @@ import {
|
|||||||
PageEnter,
|
PageEnter,
|
||||||
headerActionsSx,
|
headerActionsSx,
|
||||||
} from "../ui";
|
} from "../ui";
|
||||||
|
import {
|
||||||
|
ISSUED_ORDER_STATUS,
|
||||||
|
statusLabel,
|
||||||
|
statusColor,
|
||||||
|
} from "../lib/documentStatus";
|
||||||
|
|
||||||
const API_BASE = "/api/admin";
|
const API_BASE = "/api/admin";
|
||||||
|
|
||||||
const STATUS_LABELS: Record<string, string> = {
|
|
||||||
draft: "Koncept",
|
|
||||||
sent: "Odeslaná",
|
|
||||||
confirmed: "Potvrzená",
|
|
||||||
completed: "Dokončená",
|
|
||||||
cancelled: "Stornovaná",
|
|
||||||
};
|
|
||||||
|
|
||||||
const STATUS_COLORS: Record<
|
|
||||||
string,
|
|
||||||
"default" | "success" | "error" | "warning" | "info"
|
|
||||||
> = {
|
|
||||||
draft: "default",
|
|
||||||
sent: "info",
|
|
||||||
confirmed: "warning",
|
|
||||||
completed: "success",
|
|
||||||
cancelled: "error",
|
|
||||||
};
|
|
||||||
|
|
||||||
// Labels for the buttons that trigger a status transition.
|
// Labels for the buttons that trigger a status transition.
|
||||||
const TRANSITION_LABELS: Record<string, string> = {
|
const TRANSITION_LABELS: Record<string, string> = {
|
||||||
sent: "Odeslat",
|
sent: "Odeslat",
|
||||||
@@ -862,8 +848,8 @@ export default function IssuedOrderDetail() {
|
|||||||
</Typography>
|
</Typography>
|
||||||
{isEdit && (
|
{isEdit && (
|
||||||
<StatusChip
|
<StatusChip
|
||||||
label={STATUS_LABELS[form.status] || form.status}
|
label={statusLabel(ISSUED_ORDER_STATUS, form.status)}
|
||||||
color={STATUS_COLORS[form.status] || "default"}
|
color={statusColor(ISSUED_ORDER_STATUS, form.status)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
@@ -1281,9 +1267,10 @@ export default function IssuedOrderDetail() {
|
|||||||
onClose={() => setStatusConfirm({ show: false, status: null })}
|
onClose={() => setStatusConfirm({ show: false, status: null })}
|
||||||
onConfirm={handleStatusChange}
|
onConfirm={handleStatusChange}
|
||||||
title="Změnit stav objednávky"
|
title="Změnit stav objednávky"
|
||||||
message={`Opravdu chcete změnit stav objednávky "${poNumber}" na "${
|
message={`Opravdu chcete změnit stav objednávky "${poNumber}" na "${statusLabel(
|
||||||
STATUS_LABELS[statusConfirm.status || ""] || ""
|
ISSUED_ORDER_STATUS,
|
||||||
}"?`}
|
statusConfirm.status,
|
||||||
|
)}"?`}
|
||||||
confirmText={
|
confirmText={
|
||||||
TRANSITION_LABELS[statusConfirm.status || ""] || "Potvrdit"
|
TRANSITION_LABELS[statusConfirm.status || ""] || "Potvrdit"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,32 +28,19 @@ import {
|
|||||||
LoadingState,
|
LoadingState,
|
||||||
type DataColumn,
|
type DataColumn,
|
||||||
} from "../ui";
|
} from "../ui";
|
||||||
|
import {
|
||||||
|
ISSUED_ORDER_STATUS,
|
||||||
|
statusLabel,
|
||||||
|
statusColor,
|
||||||
|
statusOptions,
|
||||||
|
} from "../lib/documentStatus";
|
||||||
|
|
||||||
const API_BASE = "/api/admin";
|
const API_BASE = "/api/admin";
|
||||||
|
|
||||||
const STATUS_LABELS: Record<string, string> = {
|
const STATUS_OPTIONS = statusOptions(ISSUED_ORDER_STATUS, {
|
||||||
draft: "Koncept",
|
value: "",
|
||||||
sent: "Odeslaná",
|
label: "Všechny stavy",
|
||||||
confirmed: "Potvrzená",
|
});
|
||||||
completed: "Dokončená",
|
|
||||||
cancelled: "Stornovaná",
|
|
||||||
};
|
|
||||||
|
|
||||||
const STATUS_COLORS: Record<
|
|
||||||
string,
|
|
||||||
"default" | "success" | "error" | "warning" | "info"
|
|
||||||
> = {
|
|
||||||
draft: "default",
|
|
||||||
sent: "info",
|
|
||||||
confirmed: "warning",
|
|
||||||
completed: "success",
|
|
||||||
cancelled: "error",
|
|
||||||
};
|
|
||||||
|
|
||||||
const STATUS_OPTIONS = [
|
|
||||||
{ value: "", label: "Všechny stavy" },
|
|
||||||
...Object.entries(STATUS_LABELS).map(([value, label]) => ({ value, label })),
|
|
||||||
];
|
|
||||||
|
|
||||||
const ViewIcon = (
|
const ViewIcon = (
|
||||||
<svg
|
<svg
|
||||||
@@ -224,8 +211,8 @@ export default function IssuedOrders({ month, year }: IssuedOrdersProps) {
|
|||||||
sortKey: "status",
|
sortKey: "status",
|
||||||
render: (o) => (
|
render: (o) => (
|
||||||
<StatusChip
|
<StatusChip
|
||||||
label={STATUS_LABELS[o.status] || o.status}
|
label={statusLabel(ISSUED_ORDER_STATUS, o.status)}
|
||||||
color={STATUS_COLORS[o.status] || "default"}
|
color={statusColor(ISSUED_ORDER_STATUS, o.status)}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ import {
|
|||||||
PageEnter,
|
PageEnter,
|
||||||
headerActionsSx,
|
headerActionsSx,
|
||||||
} from "../ui";
|
} from "../ui";
|
||||||
|
import { OFFER_STATUS, statusLabel, statusColor } from "../lib/documentStatus";
|
||||||
|
|
||||||
const API_BASE = "/api/admin";
|
const API_BASE = "/api/admin";
|
||||||
const DRAFT_KEY = "boha_offer_draft";
|
const DRAFT_KEY = "boha_offer_draft";
|
||||||
@@ -1124,10 +1125,17 @@ export default function OfferDetail() {
|
|||||||
<Typography variant="h4">
|
<Typography variant="h4">
|
||||||
{isEdit ? `Nabídka ${form.quotation_number}` : "Nová nabídka"}
|
{isEdit ? `Nabídka ${form.quotation_number}` : "Nová nabídka"}
|
||||||
</Typography>
|
</Typography>
|
||||||
{isInvalidated && (
|
{isEdit &&
|
||||||
<StatusChip label="Zneplatněna" color="error" />
|
(isCompleted ? (
|
||||||
)}
|
// Completed is derived from the linked order, not the offer's
|
||||||
{isCompleted && <StatusChip label="Dokončeno" color="success" />}
|
// own status — surface it (success) over the base chip.
|
||||||
|
<StatusChip label="Dokončená" color="success" />
|
||||||
|
) : (
|
||||||
|
<StatusChip
|
||||||
|
label={statusLabel(OFFER_STATUS, offerStatus)}
|
||||||
|
color={statusColor(OFFER_STATUS, offerStatus)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={headerActionsSx}>
|
<Box sx={headerActionsSx}>
|
||||||
|
|||||||
@@ -38,15 +38,19 @@ import {
|
|||||||
type DataColumn,
|
type DataColumn,
|
||||||
type TabDef,
|
type TabDef,
|
||||||
} from "../ui";
|
} from "../ui";
|
||||||
|
import { OFFER_STATUS, statusLabel, statusColor } from "../lib/documentStatus";
|
||||||
|
|
||||||
const API_BASE = "/api/admin";
|
const API_BASE = "/api/admin";
|
||||||
const DRAFT_KEY = "boha_offer_draft";
|
const DRAFT_KEY = "boha_offer_draft";
|
||||||
|
|
||||||
|
// Filter tabs cover only the real stored offer statuses (the `expired` entry
|
||||||
|
// in OFFER_STATUS is a front-end-derived state, not a stored value).
|
||||||
const STATUS_FILTERS = [
|
const STATUS_FILTERS = [
|
||||||
{ value: "", label: "Vše" },
|
{ value: "", label: "Vše" },
|
||||||
{ value: "active", label: "Aktivní" },
|
...(["active", "ordered", "invalidated"] as const).map((value) => ({
|
||||||
{ value: "ordered", label: "Objednaná" },
|
value,
|
||||||
{ value: "invalidated", label: "Zneplatněná" },
|
label: statusLabel(OFFER_STATUS, value),
|
||||||
|
})),
|
||||||
];
|
];
|
||||||
|
|
||||||
interface Quotation {
|
interface Quotation {
|
||||||
@@ -497,7 +501,7 @@ export default function Offers() {
|
|||||||
{
|
{
|
||||||
key: "customer_name",
|
key: "customer_name",
|
||||||
header: "Zákazník",
|
header: "Zákazník",
|
||||||
width: "18%",
|
width: "13%",
|
||||||
render: (q) => q.customer_name || "—",
|
render: (q) => q.customer_name || "—",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -511,22 +515,43 @@ export default function Offers() {
|
|||||||
{
|
{
|
||||||
key: "valid_until",
|
key: "valid_until",
|
||||||
header: "Platnost",
|
header: "Platnost",
|
||||||
width: "11%",
|
width: "10%",
|
||||||
sortKey: "valid_until",
|
sortKey: "valid_until",
|
||||||
mono: true,
|
mono: true,
|
||||||
render: (q) => formatDate(q.valid_until),
|
render: (q) => formatDate(q.valid_until),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: "status",
|
||||||
|
header: "Stav",
|
||||||
|
width: "10%",
|
||||||
|
sortKey: "status",
|
||||||
|
render: (q) => {
|
||||||
|
// The offer's own status is `active`/`ordered`/`invalidated`. When its
|
||||||
|
// linked order is completed, surface that as "Dokončená" (success) —
|
||||||
|
// matching the OfferDetail header chip and the completed row tint.
|
||||||
|
const completed =
|
||||||
|
q.status !== "invalidated" && q.order_status === "dokoncena";
|
||||||
|
return completed ? (
|
||||||
|
<StatusChip label="Dokončená" color="success" />
|
||||||
|
) : (
|
||||||
|
<StatusChip
|
||||||
|
label={statusLabel(OFFER_STATUS, q.status)}
|
||||||
|
color={statusColor(OFFER_STATUS, q.status)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: "currency",
|
key: "currency",
|
||||||
header: "Měna",
|
header: "Měna",
|
||||||
width: "8%",
|
width: "7%",
|
||||||
sortKey: "currency",
|
sortKey: "currency",
|
||||||
render: (q) => <StatusChip label={q.currency} color="default" />,
|
render: (q) => <StatusChip label={q.currency} color="default" />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "total",
|
key: "total",
|
||||||
header: "Celkem",
|
header: "Celkem",
|
||||||
width: "12%",
|
width: "11%",
|
||||||
align: "right",
|
align: "right",
|
||||||
mono: true,
|
mono: true,
|
||||||
bold: true,
|
bold: true,
|
||||||
@@ -535,7 +560,7 @@ export default function Offers() {
|
|||||||
{
|
{
|
||||||
key: "actions",
|
key: "actions",
|
||||||
header: "Akce",
|
header: "Akce",
|
||||||
width: "16%",
|
width: "15%",
|
||||||
align: "right",
|
align: "right",
|
||||||
render: (q) => {
|
render: (q) => {
|
||||||
const isInvalidated = q.status === "invalidated";
|
const isInvalidated = q.status === "invalidated";
|
||||||
|
|||||||
@@ -34,26 +34,10 @@ import {
|
|||||||
LoadingState,
|
LoadingState,
|
||||||
type DataColumn,
|
type DataColumn,
|
||||||
} from "../ui";
|
} from "../ui";
|
||||||
|
import { ORDER_STATUS, statusLabel, statusColor } from "../lib/documentStatus";
|
||||||
|
|
||||||
const API_BASE = "/api/admin";
|
const API_BASE = "/api/admin";
|
||||||
|
|
||||||
const STATUS_LABELS: Record<string, string> = {
|
|
||||||
prijata: "Přijatá",
|
|
||||||
v_realizaci: "V realizaci",
|
|
||||||
dokoncena: "Dokončená",
|
|
||||||
stornovana: "Stornována",
|
|
||||||
};
|
|
||||||
|
|
||||||
const STATUS_COLORS: Record<
|
|
||||||
string,
|
|
||||||
"default" | "success" | "error" | "warning" | "info"
|
|
||||||
> = {
|
|
||||||
prijata: "info",
|
|
||||||
v_realizaci: "warning",
|
|
||||||
dokoncena: "success",
|
|
||||||
stornovana: "error",
|
|
||||||
};
|
|
||||||
|
|
||||||
interface Order {
|
interface Order {
|
||||||
id: number;
|
id: number;
|
||||||
order_number: string;
|
order_number: string;
|
||||||
@@ -381,8 +365,8 @@ export default function OrdersReceived({
|
|||||||
sortKey: "status",
|
sortKey: "status",
|
||||||
render: (o) => (
|
render: (o) => (
|
||||||
<StatusChip
|
<StatusChip
|
||||||
label={STATUS_LABELS[o.status] || o.status}
|
label={statusLabel(ORDER_STATUS, o.status)}
|
||||||
color={STATUS_COLORS[o.status] || "default"}
|
color={statusColor(ORDER_STATUS, o.status)}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -42,17 +42,14 @@ import {
|
|||||||
type DataColumn,
|
type DataColumn,
|
||||||
type StatCardColor,
|
type StatCardColor,
|
||||||
} from "../ui";
|
} from "../ui";
|
||||||
|
import {
|
||||||
|
RECEIVED_INVOICE_STATUS,
|
||||||
|
statusLabel,
|
||||||
|
statusColor,
|
||||||
|
} from "../lib/documentStatus";
|
||||||
|
|
||||||
const API_BASE = "/api/admin";
|
const API_BASE = "/api/admin";
|
||||||
|
|
||||||
const STATUS_LABELS: Record<string, string> = {
|
|
||||||
unpaid: "Neuhrazena",
|
|
||||||
paid: "Uhrazena",
|
|
||||||
};
|
|
||||||
const STATUS_COLORS: Record<string, "default" | "success" | "warning"> = {
|
|
||||||
unpaid: "warning",
|
|
||||||
paid: "success",
|
|
||||||
};
|
|
||||||
const DEFAULT_CURRENCIES = ["CZK", "EUR", "USD", "GBP"];
|
const DEFAULT_CURRENCIES = ["CZK", "EUR", "USD", "GBP"];
|
||||||
const DEFAULT_VAT_RATES = [0, 10, 12, 15, 21];
|
const DEFAULT_VAT_RATES = [0, 10, 12, 15, 21];
|
||||||
|
|
||||||
@@ -604,13 +601,13 @@ export default function ReceivedInvoices({
|
|||||||
render: (inv) =>
|
render: (inv) =>
|
||||||
inv.status === "paid" ? (
|
inv.status === "paid" ? (
|
||||||
<StatusChip
|
<StatusChip
|
||||||
label={STATUS_LABELS[inv.status]}
|
label={statusLabel(RECEIVED_INVOICE_STATUS, inv.status)}
|
||||||
color={STATUS_COLORS[inv.status] ?? "default"}
|
color={statusColor(RECEIVED_INVOICE_STATUS, inv.status)}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<StatusChip
|
<StatusChip
|
||||||
label={STATUS_LABELS[inv.status] || inv.status}
|
label={statusLabel(RECEIVED_INVOICE_STATUS, inv.status)}
|
||||||
color={STATUS_COLORS[inv.status] ?? "default"}
|
color={statusColor(RECEIVED_INVOICE_STATUS, inv.status)}
|
||||||
onClick={() => toggleStatus(inv)}
|
onClick={() => toggleStatus(inv)}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user