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>
This commit is contained in:
@@ -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"));
|
||||
|
||||
@@ -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 };
|
||||
}
|
||||
|
||||
@@ -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<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" },
|
||||
stornovana: { label: "Stornovaná", color: "error" },
|
||||
};
|
||||
|
||||
/** ISSUED ORDER / objednávka vydaná (issued_orders). */
|
||||
@@ -75,6 +75,17 @@ export const RECEIVED_INVOICE_STATUS: Record<string, StatusMeta> = {
|
||||
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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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<string, string> = {
|
||||
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() {
|
||||
</Box>
|
||||
</Typography>
|
||||
<StatusChip
|
||||
label={STATUS_LABELS[project.status] || project.status}
|
||||
color={STATUS_COLORS[project.status] || "default"}
|
||||
label={statusLabel(PROJECT_STATUS, project.status)}
|
||||
color={statusColor(PROJECT_STATUS, project.status)}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
@@ -418,9 +409,11 @@ export default function ProjectDetail() {
|
||||
onChange={(v) => updateForm("status", v)}
|
||||
disabled={!canEdit}
|
||||
>
|
||||
<MenuItem value="aktivni">Aktivní</MenuItem>
|
||||
<MenuItem value="dokonceny">Dokončený</MenuItem>
|
||||
<MenuItem value="zruseny">Zrušený</MenuItem>
|
||||
{Object.entries(PROJECT_STATUS).map(([value, s]) => (
|
||||
<MenuItem key={value} value={value}>
|
||||
{s.label}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</Field>
|
||||
<Field label="Datum zahájení">
|
||||
@@ -556,8 +549,18 @@ export default function ProjectDetail() {
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
Objednávka
|
||||
</Typography>
|
||||
<Typography variant="body2">
|
||||
<Typography
|
||||
variant="body2"
|
||||
component="div"
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 1,
|
||||
flexWrap: "wrap",
|
||||
}}
|
||||
>
|
||||
{project.order_id ? (
|
||||
<>
|
||||
<Box
|
||||
component={RouterLink}
|
||||
to={`/orders/${project.order_id}`}
|
||||
@@ -568,18 +571,14 @@ export default function ProjectDetail() {
|
||||
}}
|
||||
>
|
||||
{project.order_number}
|
||||
</Box>
|
||||
{project.order_status && (
|
||||
<Box
|
||||
component="span"
|
||||
sx={{ color: "text.secondary", ml: 1 }}
|
||||
>
|
||||
(
|
||||
{STATUS_LABELS[project.order_status] ||
|
||||
project.order_status}
|
||||
)
|
||||
</Box>
|
||||
<StatusChip
|
||||
label={statusLabel(ORDER_STATUS, project.order_status)}
|
||||
color={statusColor(ORDER_STATUS, project.order_status)}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</>
|
||||
) : (
|
||||
"—"
|
||||
)}
|
||||
|
||||
@@ -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<string, string> = {
|
||||
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) => (
|
||||
<StatusChip
|
||||
label={STATUS_LABELS[p.status] || p.status}
|
||||
color={STATUS_COLORS[p.status] || "default"}
|
||||
label={statusLabel(PROJECT_STATUS, p.status)}
|
||||
color={statusColor(PROJECT_STATUS, p.status)}
|
||||
/>
|
||||
),
|
||||
},
|
||||
@@ -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)",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user