v1.8.0: warehouse module (16 commits), docházka mzda model, leave_type=holiday removal, api.ts race fix

Highlights:
- Warehouse module: receipts, issues, reservations, inventory, reports, dashboard,
  master data (categories, suppliers, locations), FIFO service, integration tests
- Docházka: mzda PDF counting model (Odpracováno / Vč. svátků / Přesčas / Svátek /
  So/Ne / Noc) with Czech weekday names and decimal hours
- AttendanceAdmin/AttendanceHistory KPI cards unified to mzda formula with
  fund bar colored by delta, badges for Práce/Dov/Nem/Sv/Nep
- Remove leave_type=holiday entirely (auto-computed from Czech public holidays)
- Allow multiple work shifts per day (overlap detection only)
- Pre-flight refresh in api.ts eliminates spurious 401s on fresh page loads
- Prisma: company_settings gets 6 nullable columns for warehouse numbering
  (PRI/VYD/INV prefixes, default patterns); migration seeds defaults
This commit is contained in:
BOHA
2026-06-03 23:13:10 +02:00
parent dac45baaa8
commit 5233db2002
149 changed files with 11132 additions and 26950 deletions

View File

@@ -9,14 +9,13 @@ import apiFetch from "../utils/api";
import { dashboardOptions } from "../lib/queries/dashboard";
import { require2FAOptions } from "../lib/queries/settings";
import { getCzechDate } from "../utils/dashboardHelpers";
import { useApiMutation } from "../lib/queries/mutations";
import DashKpiCards from "../components/dashboard/DashKpiCards";
import DashQuickActions from "../components/dashboard/DashQuickActions";
import DashActivityFeed from "../components/dashboard/DashActivityFeed";
import DashAttendanceToday from "../components/dashboard/DashAttendanceToday";
import DashProfile from "../components/dashboard/DashProfile";
import DashSessions from "../components/dashboard/DashSessions";
import { Skeleton } from "boneyard-js/react";
import DashboardFixture from "../fixtures/DashboardFixture";
const API_BASE = "/api/admin";
@@ -84,6 +83,15 @@ export default function Dashboard() {
useQuery(require2FAOptions());
const totpEnabled = totpData?.require_2fa ?? !!user?.totpEnabled;
const punchMutation = useApiMutation<
Record<string, unknown>,
{ message?: string }
>({
url: () => `${API_BASE}/attendance`,
method: () => "POST",
invalidate: ["attendance", "dashboard"],
});
// 2FA state - sdileny mezi profilem a bannerem
const [show2FASetup, setShow2FASetup] = useState(false);
const [show2FADisable, setShow2FADisable] = useState(false);
@@ -104,21 +112,13 @@ export default function Dashboard() {
const submitPunch = async (gpsData: Record<string, unknown> = {}) => {
try {
const response = await apiFetch(`${API_BASE}/attendance`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ punch_action: action, ...gpsData }),
const result = await punchMutation.mutateAsync({
punch_action: action,
...gpsData,
});
const result = await response.json();
if (result.success) {
alert.success(result.data?.message || "Docházka zaznamenána");
queryClient.invalidateQueries({ queryKey: ["dashboard"] });
queryClient.invalidateQueries({ queryKey: ["attendance"] });
} else {
alert.error(result.error || "Chyba při záznamu docházky");
}
} catch {
alert.error("Chyba pripojeni");
alert.success(result?.message || "Docházka zaznamenána");
} catch (e) {
alert.error(e instanceof Error ? e.message : "Chyba pripojeni");
} finally {
setPunching(false);
}
@@ -137,7 +137,7 @@ export default function Dashboard() {
() => submitPunch({}),
{ enableHighAccuracy: true, timeout: 10000, maximumAge: 60000 },
);
}, [dashData, alert, queryClient]);
}, [dashData, alert, punchMutation]);
// 2FA handlery
const handleStart2FASetup = async () => {
@@ -309,15 +309,11 @@ export default function Dashboard() {
</motion.div>
)}
{/* Skeleton loading */}
{/* Loading spinner */}
{dashLoading && (
<Skeleton
name="dashboard"
loading={dashLoading}
fixture={<DashboardFixture />}
>
<div />
</Skeleton>
<div className="admin-loading">
<div className="admin-spinner" />
</div>
)}
{/* KPI cards — only show if user has any admin-level permissions */}
@@ -325,12 +321,14 @@ export default function Dashboard() {
(hasPermission("offers.view") ||
hasPermission("invoices.view") ||
hasPermission("projects.view") ||
hasPermission("orders.view")) && <DashKpiCards dashData={dashData} />}
hasPermission("orders.view")) && (
<DashKpiCards dashData={dashData ?? null} />
)}
{/* Quick actions */}
{!dashLoading && (
<DashQuickActions
dashData={dashData}
dashData={dashData ?? null}
punching={punching}
onPunch={handleQuickPunch}
/>