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:
@@ -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}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user