From a274a49e90bbe325c5e94d91dc1935d03327a687 Mon Sep 17 00:00:00 2001 From: BOHA Date: Sat, 4 Jul 2026 03:22:26 +0200 Subject: [PATCH] fix(ui): project status convention, linked-order chip, diacritics, lazy dashboard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- src/admin/AdminApp.tsx | 2 +- src/admin/context/AuthContext.tsx | 4 +- src/admin/lib/documentStatus.ts | 15 +++++- src/admin/pages/Dashboard.tsx | 2 +- src/admin/pages/ProjectDetail.tsx | 81 +++++++++++++++---------------- src/admin/pages/Projects.tsx | 38 ++++++--------- 6 files changed, 72 insertions(+), 70 deletions(-) diff --git a/src/admin/AdminApp.tsx b/src/admin/AdminApp.tsx index 9393a40..b109e6f 100644 --- a/src/admin/AdminApp.tsx +++ b/src/admin/AdminApp.tsx @@ -12,8 +12,8 @@ import { lazyWithReload } from "./utils/lazyWithReload"; import MuiProvider from "./ui/MuiProvider"; import { LoadingState } from "./ui"; import Login from "./pages/Login"; -import Dashboard from "./pages/Dashboard"; +const Dashboard = lazyWithReload(() => import("./pages/Dashboard")); const Odin = lazyWithReload(() => import("./pages/Odin")); const Users = lazyWithReload(() => import("./pages/Users")); const Attendance = lazyWithReload(() => import("./pages/Attendance")); diff --git a/src/admin/context/AuthContext.tsx b/src/admin/context/AuthContext.tsx index 3fc4209..c929b6b 100644 --- a/src/admin/context/AuthContext.tsx +++ b/src/admin/context/AuthContext.tsx @@ -253,7 +253,7 @@ export function AuthProvider({ children }: { children: ReactNode }) { return { success: false, error: data.error }; } catch { const errorMsg = - "Chyba pripojeni. Zkontrolujte prosim pripojeni k internetu a zkuste to znovu."; + "Chyba připojení. Zkontrolujte prosím připojení k internetu a zkuste to znovu."; setError(errorMsg); return { success: false, error: errorMsg }; } @@ -311,7 +311,7 @@ export function AuthProvider({ children }: { children: ReactNode }) { setError(data.error); return { success: false, error: data.error }; } catch { - const errorMsg = "Chyba pripojeni."; + const errorMsg = "Chyba připojení."; setError(errorMsg); return { success: false, error: errorMsg }; } diff --git a/src/admin/lib/documentStatus.ts b/src/admin/lib/documentStatus.ts index b97973a..47a1c96 100644 --- a/src/admin/lib/documentStatus.ts +++ b/src/admin/lib/documentStatus.ts @@ -1,6 +1,6 @@ /** * Single source of truth for document status labels (Czech) + chip colors - * (MUI semantic) across the Offers / Invoices / Orders modules. + * (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 @@ -46,7 +46,7 @@ export const ORDER_STATUS: Record = { 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" }, + stornovana: { label: "Stornovaná", color: "error" }, }; /** ISSUED ORDER / objednávka vydaná (issued_orders). */ @@ -75,6 +75,17 @@ export const RECEIVED_INVOICE_STATUS: Record = { 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 = { + 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 diff --git a/src/admin/pages/Dashboard.tsx b/src/admin/pages/Dashboard.tsx index 7abe275..025d693 100644 --- a/src/admin/pages/Dashboard.tsx +++ b/src/admin/pages/Dashboard.tsx @@ -124,7 +124,7 @@ export default function Dashboard() { }); alert.success(result?.message || "Docházka zaznamenána"); } catch (e) { - alert.error(e instanceof Error ? e.message : "Chyba pripojeni"); + alert.error(e instanceof Error ? e.message : "Chyba připojení"); } finally { setPunching(false); } diff --git a/src/admin/pages/ProjectDetail.tsx b/src/admin/pages/ProjectDetail.tsx index ef7b5bb..c1fc22c 100644 --- a/src/admin/pages/ProjectDetail.tsx +++ b/src/admin/pages/ProjectDetail.tsx @@ -31,24 +31,15 @@ import { PageEnter, headerActionsSx, } from "../ui"; +import { + PROJECT_STATUS, + ORDER_STATUS, + statusLabel, + statusColor, +} from "../lib/documentStatus"; const API_BASE = "/api/admin"; -const STATUS_LABELS: Record = { - aktivni: "Aktivní", - dokonceny: "Dokončený", - zruseny: "Zrušený", -}; - -const STATUS_COLORS: Record< - string, - "default" | "success" | "error" | "warning" | "info" -> = { - aktivni: "success", - dokonceny: "info", - zruseny: "default", -}; - interface User { id: number; name: string; @@ -335,8 +326,8 @@ export default function ProjectDetail() { @@ -418,9 +409,11 @@ export default function ProjectDetail() { onChange={(v) => updateForm("status", v)} disabled={!canEdit} > - Aktivní - Dokončený - Zrušený + {Object.entries(PROJECT_STATUS).map(([value, s]) => ( + + {s.label} + + ))} @@ -556,30 +549,36 @@ export default function ProjectDetail() { Objednávka - + {project.order_id ? ( - - {project.order_number} + <> + + {project.order_number} + {project.order_status && ( - - ( - {STATUS_LABELS[project.order_status] || - project.order_status} - ) - + )} - + ) : ( "—" )} diff --git a/src/admin/pages/Projects.tsx b/src/admin/pages/Projects.tsx index 56b9a27..b18e4d6 100644 --- a/src/admin/pages/Projects.tsx +++ b/src/admin/pages/Projects.tsx @@ -38,29 +38,20 @@ import { type TabDef, type DataColumn, } from "../ui"; +import { + PROJECT_STATUS, + statusLabel, + statusColor, + statusOptions, +} from "../lib/documentStatus"; const API_BASE = "/api/admin"; -const STATUS_LABELS: Record = { - aktivni: "Aktivní", - dokonceny: "Dokončený", - zruseny: "Zrušený", -}; - -const STATUS_COLORS: Record< - string, - "default" | "success" | "error" | "warning" | "info" -> = { - aktivni: "success", - dokonceny: "info", - zruseny: "default", -}; - // Status filter tabs (mirrors the offers page): "" = all. -const STATUS_TABS: TabDef[] = [ - { value: "", label: "Všechny" }, - ...Object.entries(STATUS_LABELS).map(([value, label]) => ({ value, label })), -]; +const STATUS_TABS: TabDef[] = statusOptions(PROJECT_STATUS, { + value: "", + label: "Všechny", +}); interface Project { id: number; @@ -316,8 +307,8 @@ export default function Projects() { sortKey: "status", render: (p) => ( ), }, @@ -394,9 +385,10 @@ export default function Projects() { const rowSx = (p: Project) => { if (p.status === "dokonceny") { return { - backgroundColor: "rgba(var(--mui-palette-info-mainChannel) / 0.12)", + backgroundColor: "rgba(var(--mui-palette-success-mainChannel) / 0.12)", "&:hover": { - backgroundColor: "rgba(var(--mui-palette-info-mainChannel) / 0.18)", + backgroundColor: + "rgba(var(--mui-palette-success-mainChannel) / 0.18)", }, }; }