Files
app/src/admin/lib/documentStatus.ts
BOHA a274a49e90 fix(ui): project status convention, linked-order chip, diacritics, lazy dashboard
- PROJECT_STATUS added to documentStatus.ts (aktivni=info, dokonceny=success,
  zruseny=error — app convention); Projects/ProjectDetail local maps deleted;
  status Select derives its MenuItems from the shared map.
- ProjectDetail linked-order status now rendered via the received-order map
  (was the project map with zero key overlap → raw DB token).
- Cancelled label unified across received/issued orders in documentStatus.ts.
- 'Chyba pripojeni' → 'Chyba připojení' (Dashboard, AuthContext).
- Dashboard lazy-loaded like every other route — its 7 subcomponents leave
  the Login critical path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 03:22:26 +02:00

124 lines
4.7 KiB
TypeScript

/**
* Single source of truth for document status labels (Czech) + chip colors
* (MUI semantic) across the Offers / Invoices / Orders / Projects 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> = {
draft: { label: "Koncept", color: "default" },
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: "Stornovaná", 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> = {
draft: { label: "Koncept", color: "default" },
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" },
};
/**
* PROJECT (projects). Czech-keyed like customer orders. Colors follow the
* app-wide convention: open/in-progress = info, done = success,
* cancelled = error.
*/
export const PROJECT_STATUS: Record<string, StatusMeta> = {
aktivni: { label: "Aktivní", color: "info" },
dokonceny: { label: "Dokončený", color: "success" },
zruseny: { label: "Zrušený", color: "error" },
};
/**
* Display label for a document's official number. Drafts (deferred numbering)
* have a NULL/empty number until they are finalized — show "Koncept" instead
* of an empty/`null` heading.
*/
export function documentNumberLabel(num: string | null | undefined): string {
return num && String(num).trim() ? String(num) : "Koncept";
}
/** 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;
};