@flowselections/logistiek-engine 1.0.53 → 1.0.90
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -24
- package/dist-lib/components/AfrondWachtrijCard.d.ts +2 -0
- package/dist-lib/components/AfrondWachtrijCard.d.ts.map +1 -0
- package/dist-lib/components/AfrondWachtrijCard.js +27 -0
- package/dist-lib/components/BeladingsafwijkingenCard.d.ts +2 -0
- package/dist-lib/components/BeladingsafwijkingenCard.d.ts.map +1 -0
- package/dist-lib/components/BeladingsafwijkingenCard.js +44 -0
- package/dist-lib/components/FlowCanvas.d.ts +6 -0
- package/dist-lib/components/FlowCanvas.d.ts.map +1 -1
- package/dist-lib/components/FlowCanvas.js +79 -18
- package/dist-lib/components/FlowOrders.d.ts.map +1 -1
- package/dist-lib/components/FlowOrders.js +59 -13
- package/dist-lib/components/FlowPage.d.ts.map +1 -1
- package/dist-lib/components/FlowPage.js +175 -6
- package/dist-lib/components/GeblokkeerdeOrdersCard.d.ts.map +1 -1
- package/dist-lib/components/GeblokkeerdeOrdersCard.js +21 -2
- package/dist-lib/components/KardekkingCard.d.ts +2 -0
- package/dist-lib/components/KardekkingCard.d.ts.map +1 -0
- package/dist-lib/components/KardekkingCard.js +29 -0
- package/dist-lib/components/LogistiekEnginePage.d.ts.map +1 -1
- package/dist-lib/components/LogistiekEnginePage.js +7 -1
- package/dist-lib/components/LogistiekEngineQueuePage.d.ts.map +1 -1
- package/dist-lib/components/LogistiekEngineQueuePage.js +4 -2
- package/dist-lib/components/NodeTracePanel.d.ts +4 -1
- package/dist-lib/components/NodeTracePanel.d.ts.map +1 -1
- package/dist-lib/components/NodeTracePanel.js +8 -2
- package/dist-lib/components/OrderStopBanner.d.ts +9 -2
- package/dist-lib/components/OrderStopBanner.d.ts.map +1 -1
- package/dist-lib/components/OrderStopBanner.js +27 -13
- package/dist-lib/components/OrderTraceDetails.d.ts.map +1 -1
- package/dist-lib/components/OrderTraceDetails.js +2 -1
- package/dist-lib/components/PlanWachtrijPage.d.ts +2 -0
- package/dist-lib/components/PlanWachtrijPage.d.ts.map +1 -0
- package/dist-lib/components/PlanWachtrijPage.js +99 -0
- package/dist-lib/components/PlannerDiffCard.d.ts +2 -0
- package/dist-lib/components/PlannerDiffCard.d.ts.map +1 -0
- package/dist-lib/components/PlannerDiffCard.js +25 -0
- package/dist-lib/components/RegiePage.d.ts.map +1 -1
- package/dist-lib/components/RegiePage.js +21 -13
- package/dist-lib/components/TaakGezondheidCard.d.ts +2 -0
- package/dist-lib/components/TaakGezondheidCard.d.ts.map +1 -0
- package/dist-lib/components/TaakGezondheidCard.js +31 -0
- package/dist-lib/hooks/useAfrondWachtrij.d.ts +28 -0
- package/dist-lib/hooks/useAfrondWachtrij.d.ts.map +1 -0
- package/dist-lib/hooks/useAfrondWachtrij.js +88 -0
- package/dist-lib/hooks/useBeladingsafwijkingen.d.ts +39 -0
- package/dist-lib/hooks/useBeladingsafwijkingen.d.ts.map +1 -0
- package/dist-lib/hooks/useBeladingsafwijkingen.js +119 -0
- package/dist-lib/hooks/useGeblokkeerdeOrders.d.ts +7 -0
- package/dist-lib/hooks/useGeblokkeerdeOrders.d.ts.map +1 -1
- package/dist-lib/hooks/useGeblokkeerdeOrders.js +28 -2
- package/dist-lib/hooks/useLogisticsTrace.js +1 -1
- package/dist-lib/hooks/useOrderTrace.js +1 -1
- package/dist-lib/hooks/useOrderVersies.d.ts.map +1 -1
- package/dist-lib/hooks/useOrderVersies.js +17 -3
- package/dist-lib/hooks/usePlanWachtrij.d.ts +33 -0
- package/dist-lib/hooks/usePlanWachtrij.d.ts.map +1 -0
- package/dist-lib/hooks/usePlanWachtrij.js +104 -0
- package/dist-lib/hooks/useRecalcJob.js +1 -1
- package/dist-lib/hooks/useShippingNoteDiagnose.d.ts +20 -0
- package/dist-lib/hooks/useShippingNoteDiagnose.d.ts.map +1 -0
- package/dist-lib/hooks/useShippingNoteDiagnose.js +52 -0
- package/dist-lib/hooks/useTaakGezondheid.d.ts +16 -0
- package/dist-lib/hooks/useTaakGezondheid.d.ts.map +1 -0
- package/dist-lib/hooks/useTaakGezondheid.js +43 -0
- package/dist-lib/hooks/useWerktijden.js +1 -1
- package/dist-lib/hooks/useWerkwijzeStatus.js +1 -1
- package/dist-lib/index.d.ts +10 -0
- package/dist-lib/index.d.ts.map +1 -1
- package/dist-lib/index.js +10 -0
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +3147 -159
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +3147 -159
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.js +1 -1
- package/dist-lib/integrations/supabase/moduleClient.d.ts +3147 -159
- package/dist-lib/integrations/supabase/moduleClient.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/previewAuthStorage.d.ts +1 -1
- package/dist-lib/integrations/supabase/previewAuthStorage.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/previewAuthStorage.js +8 -1
- package/dist-lib/integrations/supabase/types.d.ts +3183 -161
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/attachAuthToken.js +1 -1
- package/dist-lib/lib/bufferDecision.test.d.ts +2 -0
- package/dist-lib/lib/bufferDecision.test.d.ts.map +1 -0
- package/dist-lib/lib/bufferDecision.test.js +104 -0
- package/dist-lib/lib/cartCoverage.functions.d.ts +30820 -0
- package/dist-lib/lib/cartCoverage.functions.d.ts.map +1 -0
- package/dist-lib/lib/cartCoverage.functions.js +20 -0
- package/dist-lib/lib/floridayDocuments.functions.d.ts +8979 -15
- package/dist-lib/lib/floridayDocuments.functions.d.ts.map +1 -1
- package/dist-lib/lib/floridayDocuments.functions.js +5 -5
- package/dist-lib/lib/floridayPush.functions.d.ts +3147 -159
- package/dist-lib/lib/floridayPush.functions.d.ts.map +1 -1
- package/dist-lib/lib/floridayPush.functions.js +3 -3
- package/dist-lib/lib/flowJourney.d.ts +40 -0
- package/dist-lib/lib/flowJourney.d.ts.map +1 -0
- package/dist-lib/lib/flowJourney.js +156 -0
- package/dist-lib/lib/flowNodes.d.ts +3 -0
- package/dist-lib/lib/flowNodes.d.ts.map +1 -1
- package/dist-lib/lib/flowNodes.js +6 -0
- package/dist-lib/lib/flowRoute.d.ts +11 -1
- package/dist-lib/lib/flowRoute.d.ts.map +1 -1
- package/dist-lib/lib/flowRoute.js +343 -40
- package/dist-lib/lib/flowTree.d.ts +86 -1
- package/dist-lib/lib/flowTree.d.ts.map +1 -1
- package/dist-lib/lib/flowTree.js +391 -2
- package/dist-lib/lib/foutTekst.d.ts +4 -0
- package/dist-lib/lib/foutTekst.d.ts.map +1 -0
- package/dist-lib/lib/foutTekst.js +43 -0
- package/dist-lib/lib/handlers/logistics-tick.server.d.ts +2 -0
- package/dist-lib/lib/handlers/logistics-tick.server.d.ts.map +1 -1
- package/dist-lib/lib/handlers/logistics-tick.server.js +56 -0
- package/dist-lib/lib/handlers/logistics-tick.test.d.ts +2 -0
- package/dist-lib/lib/handlers/logistics-tick.test.d.ts.map +1 -0
- package/dist-lib/lib/handlers/logistics-tick.test.js +63 -0
- package/dist-lib/lib/loadCarrierPlanning.functions.d.ts +14965 -25
- package/dist-lib/lib/loadCarrierPlanning.functions.d.ts.map +1 -1
- package/dist-lib/lib/loadCarrierPlanning.functions.js +7 -7
- package/dist-lib/lib/loadCarrierPlanning.server.d.ts +24 -12
- package/dist-lib/lib/loadCarrierPlanning.server.d.ts.map +1 -1
- package/dist-lib/lib/loadCarrierPlanning.server.js +490 -211
- package/dist-lib/lib/logisticsRecalcJob.functions.d.ts +5986 -10
- package/dist-lib/lib/logisticsRecalcJob.functions.d.ts.map +1 -1
- package/dist-lib/lib/logisticsRecalcJob.functions.js +4 -4
- package/dist-lib/lib/logisticsRecalcJob.server.d.ts.map +1 -1
- package/dist-lib/lib/logisticsRecalcJob.server.js +17 -2
- package/dist-lib/lib/logisticsRecalculate.functions.d.ts +3147 -159
- package/dist-lib/lib/logisticsRecalculate.functions.d.ts.map +1 -1
- package/dist-lib/lib/logisticsRecalculate.functions.js +2 -2
- package/dist-lib/lib/modulePrompts.d.ts.map +1 -1
- package/dist-lib/lib/modulePrompts.js +3436 -3325
- package/dist-lib/lib/nodeTrace.d.ts +4 -0
- package/dist-lib/lib/nodeTrace.d.ts.map +1 -1
- package/dist-lib/lib/nodeTrace.js +6 -15
- package/dist-lib/lib/planQueue.server.d.ts.map +1 -1
- package/dist-lib/lib/planQueue.server.js +24 -3
- package/dist-lib/lib/plannerDiff.d.ts +37 -0
- package/dist-lib/lib/plannerDiff.d.ts.map +1 -0
- package/dist-lib/lib/plannerDiff.functions.d.ts +30821 -0
- package/dist-lib/lib/plannerDiff.functions.d.ts.map +1 -0
- package/dist-lib/lib/plannerDiff.functions.js +21 -0
- package/dist-lib/lib/plannerDiff.js +74 -0
- package/dist-lib/lib/plannerDiff.server.d.ts +5 -0
- package/dist-lib/lib/plannerDiff.server.d.ts.map +1 -0
- package/dist-lib/lib/plannerDiff.server.js +60 -0
- package/dist-lib/lib/plannerDiff.test.d.ts +2 -0
- package/dist-lib/lib/plannerDiff.test.d.ts.map +1 -0
- package/dist-lib/lib/plannerDiff.test.js +41 -0
- package/dist-lib/lib/resolveBuffer.test.d.ts +2 -0
- package/dist-lib/lib/resolveBuffer.test.d.ts.map +1 -0
- package/dist-lib/lib/resolveBuffer.test.js +38 -0
- package/dist-lib/lib/ruleEngineSchema.d.ts.map +1 -1
- package/dist-lib/lib/ruleEngineSchema.js +52 -0
- package/dist-lib/lib/stopReden.d.ts +43 -0
- package/dist-lib/lib/stopReden.d.ts.map +1 -0
- package/dist-lib/lib/stopReden.js +197 -0
- package/dist-lib/lib/taskCounts.d.ts +2 -0
- package/dist-lib/lib/taskCounts.d.ts.map +1 -0
- package/dist-lib/lib/taskCounts.js +15 -0
- package/dist-lib/lib/taskCounts.test.d.ts +2 -0
- package/dist-lib/lib/taskCounts.test.d.ts.map +1 -0
- package/dist-lib/lib/taskCounts.test.js +12 -0
- package/dist-lib/lib/trace.server.d.ts.map +1 -1
- package/dist-lib/lib/trace.server.js +3 -0
- package/dist-lib/lib/vakUitleg.d.ts.map +1 -1
- package/dist-lib/lib/vakUitleg.js +2 -1
- package/package.json +12 -9
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PlanWachtrijPage.d.ts","sourceRoot":"","sources":["../../src/components/PlanWachtrijPage.tsx"],"names":[],"mappings":"AAsEA,wBAAgB,gBAAgB,gCA0N/B"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// ============================================================================
|
|
3
|
+
// src/components/PlanWachtrijPage.tsx — de planwachtrij bekijken en resetten
|
|
4
|
+
// ============================================================================
|
|
5
|
+
// Laat zien welke orders wachten op een karrenindeling, hoe vaak het is
|
|
6
|
+
// geprobeerd en wat er precies misging (in gewone taal). Met knoppen om de
|
|
7
|
+
// wachtrij opnieuw te starten. GEEN <Header /> hier — die levert de shell.
|
|
8
|
+
// ============================================================================
|
|
9
|
+
import { useMemo, useState } from "react";
|
|
10
|
+
import { Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Input, Skeleton, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@flowselections/core";
|
|
11
|
+
import { AlertTriangle, RefreshCw, RotateCcw } from "lucide-react";
|
|
12
|
+
import { toast } from "sonner";
|
|
13
|
+
import { usePlanWachtrij } from "../hooks/usePlanWachtrij";
|
|
14
|
+
import { foutTekst } from "../lib/foutTekst";
|
|
15
|
+
const STAND_KLEUR = {
|
|
16
|
+
wacht: "bg-amber-500/15 text-amber-700 dark:text-amber-300",
|
|
17
|
+
bezig: "bg-sky-500/15 text-sky-700 dark:text-sky-300",
|
|
18
|
+
"blijft hangen": "bg-orange-500/20 text-orange-700 dark:text-orange-300",
|
|
19
|
+
klaar: "bg-emerald-500/15 text-emerald-700 dark:text-emerald-300",
|
|
20
|
+
mislukt: "bg-destructive/15 text-destructive",
|
|
21
|
+
geblokkeerd: "bg-destructive/15 text-destructive",
|
|
22
|
+
};
|
|
23
|
+
const FILTERS = [
|
|
24
|
+
{ id: "alles", label: "Alles" },
|
|
25
|
+
{ id: "open", label: "Open" },
|
|
26
|
+
{ id: "mislukt", label: "Mislukt of geblokkeerd" },
|
|
27
|
+
{ id: "blijft hangen", label: "Blijft hangen" },
|
|
28
|
+
{ id: "klaar", label: "Klaar" },
|
|
29
|
+
];
|
|
30
|
+
function tijd(v) {
|
|
31
|
+
if (!v)
|
|
32
|
+
return "—";
|
|
33
|
+
const d = new Date(v);
|
|
34
|
+
return Number.isNaN(d.getTime()) ? "—" : d.toLocaleString("nl-NL");
|
|
35
|
+
}
|
|
36
|
+
function klok(v) {
|
|
37
|
+
if (!v)
|
|
38
|
+
return "—";
|
|
39
|
+
const d = new Date(v);
|
|
40
|
+
return Number.isNaN(d.getTime()) ? "—" : d.toLocaleTimeString("nl-NL", { hour: "2-digit", minute: "2-digit" });
|
|
41
|
+
}
|
|
42
|
+
function past(regel, filter, zoek) {
|
|
43
|
+
const z = zoek.trim().toLowerCase();
|
|
44
|
+
if (z && !`${regel.orderNumber ?? ""} ${regel.klant ?? ""}`.toLowerCase().includes(z))
|
|
45
|
+
return false;
|
|
46
|
+
if (filter === "alles")
|
|
47
|
+
return true;
|
|
48
|
+
if (filter === "open")
|
|
49
|
+
return regel.stand === "wacht" || regel.stand === "bezig";
|
|
50
|
+
if (filter === "mislukt")
|
|
51
|
+
return regel.stand === "mislukt" || regel.stand === "geblokkeerd";
|
|
52
|
+
return regel.stand === filter;
|
|
53
|
+
}
|
|
54
|
+
export function PlanWachtrijPage() {
|
|
55
|
+
const { regels, loading, busy, error, refresh, reset, opnieuw } = usePlanWachtrij();
|
|
56
|
+
const [filter, setFilter] = useState("alles");
|
|
57
|
+
const [zoek, setZoek] = useState("");
|
|
58
|
+
const [nieuwsteEerst, setNieuwsteEerst] = useState(true);
|
|
59
|
+
const veilig = Array.isArray(regels) ? regels : [];
|
|
60
|
+
const zichtbaar = useMemo(() => {
|
|
61
|
+
const lijst = veilig.filter((r) => past(r, filter, zoek));
|
|
62
|
+
const stempel = (r) => {
|
|
63
|
+
// Volgorde volgt de order zelf: wanneer de bestelling is binnengekomen.
|
|
64
|
+
const d = new Date(r.orderGemaaktOp ?? r.aangemeldOp ?? r.bijgewerktOp ?? 0).getTime();
|
|
65
|
+
return Number.isNaN(d) ? 0 : d;
|
|
66
|
+
};
|
|
67
|
+
return [...lijst].sort((a, b) => (nieuwsteEerst ? stempel(b) - stempel(a) : stempel(a) - stempel(b)));
|
|
68
|
+
}, [veilig, filter, zoek, nieuwsteEerst]);
|
|
69
|
+
const tellingen = useMemo(() => {
|
|
70
|
+
const t = {};
|
|
71
|
+
for (const r of veilig)
|
|
72
|
+
t[r.stand] = (t[r.stand] ?? 0) + 1;
|
|
73
|
+
return t;
|
|
74
|
+
}, [veilig]);
|
|
75
|
+
async function doeReset(keuze, vraag) {
|
|
76
|
+
if (!window.confirm(vraag))
|
|
77
|
+
return;
|
|
78
|
+
try {
|
|
79
|
+
const n = await reset(keuze);
|
|
80
|
+
toast.success(`${n} order(s) opnieuw aangemeld voor indeling.`);
|
|
81
|
+
}
|
|
82
|
+
catch (e) {
|
|
83
|
+
toast.error(e?.message ? String(e.message) : "Opnieuw starten is niet gelukt.");
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return (_jsx("div", { className: "space-y-4 p-4", children: _jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsx(CardTitle, { children: "Wachtrij \u2014 orders die op een karrenindeling wachten" }), _jsx(CardDescription, { children: "Per order zie je de stand, hoe vaak het is geprobeerd en wat er misging. Je kunt de wachtrij opnieuw starten: de Engine kijkt dan opnieuw wat er in de order zit en maakt de karrenindeling opnieuw. Al geprinte, gescande of gecontroleerde karren blijven altijd staan." })] }), _jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [FILTERS.map((f) => (_jsxs(Button, { size: "sm", variant: filter === f.id ? "default" : "outline", onClick: () => setFilter(f.id), children: [f.label, f.id !== "alles" && f.id !== "open" ? (_jsx("span", { className: "ml-1 text-xs opacity-70", children: f.id === "mislukt"
|
|
87
|
+
? (tellingen["mislukt"] ?? 0) + (tellingen["geblokkeerd"] ?? 0)
|
|
88
|
+
: tellingen[f.id] ?? 0 })) : null] }, f.id))), _jsx(Input, { value: zoek, onChange: (e) => setZoek(e.target.value), placeholder: "Zoek op ordernummer of klant", className: "h-9 w-64" }), _jsxs("div", { className: "ml-auto flex flex-wrap gap-2", children: [_jsx(Button, { size: "sm", variant: "outline", onClick: () => setNieuwsteEerst((v) => !v), children: nieuwsteEerst ? "Nieuwste eerst" : "Oudste eerst" }), _jsxs(Button, { size: "sm", variant: "outline", onClick: () => void refresh(), disabled: busy, children: [_jsx(RefreshCw, { className: "mr-1 h-4 w-4" }), " Verversen"] }), _jsx(Button, { size: "sm", variant: "outline", disabled: busy, onClick: () => void doeReset("stuck", "Regels die al langer dan een kwartier 'bezig' blijven terugzetten op wachtend?"), children: "Blijft hangen losmaken" }), _jsxs(Button, { size: "sm", disabled: busy, onClick: () => void doeReset("failed", "Alle mislukte en geblokkeerde orders opnieuw aanmelden? De Engine bekijkt ze opnieuw en maakt de karrenindeling opnieuw."), children: [_jsx(RotateCcw, { className: "mr-1 h-4 w-4" }), " Wachtrij resetten"] }), _jsx(Button, { size: "sm", variant: "destructive", disabled: busy, onClick: () => void doeReset("all", "De hele wachtrij opnieuw starten, dus ook orders die al klaar waren? Dit kan even duren."), children: "Alles opnieuw" })] })] }), error ? (_jsxs("p", { className: "flex items-center gap-2 text-sm text-destructive", children: [_jsx(AlertTriangle, { className: "h-4 w-4" }), " ", error] })) : null, loading ? (_jsxs("div", { className: "space-y-2", children: [_jsx(Skeleton, { className: "h-8 w-full" }), _jsx(Skeleton, { className: "h-8 w-full" }), _jsx(Skeleton, { className: "h-8 w-full" })] })) : zichtbaar.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Geen regels in de wachtrij voor deze keuze." })) : (_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Order" }), _jsx(TableHead, { children: "Stand" }), _jsx(TableHead, { children: "Voortgang" }), _jsx(TableHead, { children: "Pogingen" }), _jsx(TableHead, { children: "Volgende poging" }), _jsx(TableHead, { children: "Aangemeld omdat" }), _jsx(TableHead, { children: "Wat ging er mis" }), _jsx(TableHead, { children: "Bijgewerkt" }), _jsx(TableHead, {})] }) }), _jsx(TableBody, { children: zichtbaar.map((r) => (_jsxs(TableRow, { children: [_jsxs(TableCell, { children: [_jsx("div", { className: "font-medium", children: r.orderNumber ?? "—" }), _jsx("div", { className: "text-xs text-muted-foreground", children: r.klant ?? "" })] }), _jsx(TableCell, { children: _jsx(Badge, { className: STAND_KLEUR[r.stand] ?? "", children: r.stand }) }), _jsx(TableCell, { className: "w-40", children: r.voortgang === null ? (_jsx("span", { className: "text-xs text-muted-foreground", children: "\u2014" })) : (_jsxs("div", { className: "space-y-1", children: [_jsx("div", { className: "h-2 w-28 overflow-hidden rounded-full bg-muted", children: _jsx("div", { className: "h-full rounded-full bg-primary transition-all", style: { width: `${Math.min(100, Math.max(0, r.voortgang))}%` } }) }), _jsxs("span", { className: "text-xs text-muted-foreground", children: [r.voortgang, "% van de orderregels ingedeeld"] })] })) }), _jsxs(TableCell, { className: "text-sm", children: [r.pogingen, " van ", r.maxPogingen || "—"] }), _jsx(TableCell, { className: "text-sm", children: klok(r.volgendePoging) }), _jsx(TableCell, { className: "text-sm text-muted-foreground", children: r.reden ?? "—" }), _jsxs(TableCell, { className: "max-w-md text-sm", children: [r.foutTekst ?? foutTekst(r.foutRuw) ?? "—", r.foutRuw && (r.foutTekst ?? foutTekst(r.foutRuw)) !== r.foutRuw ? (_jsxs("details", { className: "mt-1", children: [_jsx("summary", { className: "cursor-pointer text-xs text-muted-foreground", children: "Technische details" }), _jsx("p", { className: "mt-1 break-words text-xs text-muted-foreground", children: r.foutRuw })] })) : null] }), _jsx(TableCell, { className: "text-xs text-muted-foreground", children: tijd(r.bijgewerktOp) }), _jsx(TableCell, { className: "text-right", children: _jsx(Button, { size: "sm", variant: "outline", disabled: busy || !r.orderId, onClick: async () => {
|
|
89
|
+
if (!r.orderId)
|
|
90
|
+
return;
|
|
91
|
+
try {
|
|
92
|
+
await opnieuw(r.orderId);
|
|
93
|
+
toast.success(`${r.orderNumber ?? "Order"} opnieuw aangemeld.`);
|
|
94
|
+
}
|
|
95
|
+
catch (e) {
|
|
96
|
+
toast.error(e?.message ? String(e.message) : "Opnieuw proberen is niet gelukt.");
|
|
97
|
+
}
|
|
98
|
+
}, children: "Opnieuw" }) })] }, r.id))) })] }))] })] }) }));
|
|
99
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PlannerDiffCard.d.ts","sourceRoot":"","sources":["../../src/components/PlannerDiffCard.tsx"],"names":[],"mappings":"AAWA,wBAAgB,eAAe,gCA0D9B"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useQuery } from "@tanstack/react-query";
|
|
3
|
+
import { useServerFn } from "@tanstack/react-start";
|
|
4
|
+
import { Badge, Skeleton, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@flowselections/core";
|
|
5
|
+
import { getPlannerDiffOverview } from "../lib/plannerDiff.functions";
|
|
6
|
+
const MODUS = {
|
|
7
|
+
off: "Uit (wissen en opnieuw opbouwen)",
|
|
8
|
+
dry_run: "Proef (oude aanpak leidend, vergelijking ernaast)",
|
|
9
|
+
apply: "Actief (alleen verschillen aanpassen)",
|
|
10
|
+
};
|
|
11
|
+
export function PlannerDiffCard() {
|
|
12
|
+
const fetchOverview = useServerFn(getPlannerDiffOverview);
|
|
13
|
+
const { data, isLoading, error } = useQuery({ queryKey: ["planner-diff"], queryFn: () => fetchOverview() });
|
|
14
|
+
if (isLoading)
|
|
15
|
+
return _jsx(Skeleton, { className: "h-24 w-full" });
|
|
16
|
+
if (error || !data)
|
|
17
|
+
return _jsx("p", { className: "text-sm text-destructive", children: "Kon de vergelijking niet ophalen." });
|
|
18
|
+
const afwijkingen = Array.isArray(data.afwijkingen) ? data.afwijkingen : [];
|
|
19
|
+
return (_jsxs("div", { className: "space-y-3", children: [_jsxs("p", { className: "text-sm", children: ["Modus: ", _jsx("span", { className: "font-medium", children: MODUS[data.modus] ?? MODUS["off"] })] }), _jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsxs(Badge, { variant: "secondary", children: ["Buffers vergeleken: ", data.totaal] }), _jsxs(Badge, { variant: "secondary", children: ["Identiek: ", data.identiek] }), _jsxs(Badge, { variant: data.afwijkend > 0 ? "destructive" : "secondary", children: ["Afwijkend: ", data.afwijkend] }), data.mislukt > 0 && _jsxs(Badge, { variant: "destructive", children: ["Mislukt: ", data.mislukt] }), data.overgeslagen > 0 && _jsxs(Badge, { variant: "outline", children: ["Overgeslagen (indeling geblokkeerd): ", data.overgeslagen] })] }), afwijkingen.length > 0 && (_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Order" }), _jsx(TableHead, { children: "Karren oud \u2192 nieuw" }), _jsx(TableHead, { children: "Verschil" }), _jsx(TableHead, { children: "Moment" })] }) }), _jsx(TableBody, { children: afwijkingen.map((r) => {
|
|
20
|
+
const diffs = Array.isArray(r.differences) ? r.differences : [];
|
|
21
|
+
return (_jsxs(TableRow, { children: [_jsx(TableCell, { children: r.order_ref ?? "—" }), _jsxs(TableCell, { children: [r.carts_before ?? "?", " \u2192 ", r.carts_after ?? "?"] }), _jsxs(TableCell, { className: "max-w-md text-xs", children: [diffs.slice(0, 3).map((d, i) => (_jsx("div", { children: d?.fout
|
|
22
|
+
? `Fout: ${d.fout}`
|
|
23
|
+
: `Kar ${d?.cartNumber ?? "?"}: ${d?.before ?? "leeg"} → ${d?.after ?? "leeg"}` }, i))), diffs.length > 3 && _jsxs("div", { children: ["\u2026 en ", diffs.length - 3, " meer"] })] }), _jsx(TableCell, { className: "text-xs", children: new Date(r.created_at).toLocaleString("nl-NL") })] }, r.id));
|
|
24
|
+
}) })] }))] }));
|
|
25
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RegiePage.d.ts","sourceRoot":"","sources":["../../src/components/RegiePage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"RegiePage.d.ts","sourceRoot":"","sources":["../../src/components/RegiePage.tsx"],"names":[],"mappings":"AA+BA,wBAAgB,SAAS,gCA0FxB"}
|
|
@@ -6,25 +6,33 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
6
6
|
// orders met het volledige logboek per order.
|
|
7
7
|
// GEEN <Header /> hier — die levert de shell.
|
|
8
8
|
// ============================================================================
|
|
9
|
-
import {
|
|
10
|
-
import { Button, Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@flowselections/core";
|
|
9
|
+
import { useCallback, useEffect, useState } from "react";
|
|
10
|
+
import { Button, Card, CardContent, CardDescription, CardHeader, CardTitle, supabase, } from "@flowselections/core";
|
|
11
11
|
import { RefreshCw, Workflow } from "lucide-react";
|
|
12
12
|
import { Link } from "@tanstack/react-router";
|
|
13
|
-
import { useLogistiekTaken } from "../hooks/useLogistiekTaken";
|
|
14
13
|
import { ConsistentieCard } from "./ConsistentieCard";
|
|
15
14
|
import { KarplanStatusCard } from "./KarplanStatusCard";
|
|
15
|
+
import { KardekkingCard } from "./KardekkingCard";
|
|
16
|
+
import { PlannerDiffCard } from "./PlannerDiffCard";
|
|
17
|
+
import { BeladingsafwijkingenCard } from "./BeladingsafwijkingenCard";
|
|
16
18
|
import { OrderLogboek } from "./OrderLogboek";
|
|
17
19
|
import { InklapbaarVenster } from "./InklapbaarVenster";
|
|
20
|
+
import { tellOpenTaken } from "../lib/taskCounts";
|
|
18
21
|
import { LOGISTICS_MODULES } from "../lib/logisticsTasks";
|
|
19
22
|
export function RegiePage() {
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
// Alleen open en geplande taken, geteld in de database (geen 500-grens, geen geannuleerde).
|
|
24
|
+
const [perModule, setPerModule] = useState({});
|
|
25
|
+
const [loading, setLoading] = useState(true);
|
|
26
|
+
const [telFout, setTelFout] = useState(null);
|
|
27
|
+
const laden = useCallback(async () => {
|
|
28
|
+
setLoading(true);
|
|
29
|
+
const { data, error } = await supabase.rpc("logistics_task_open_counts");
|
|
30
|
+
setTelFout(error ? error.message : null);
|
|
31
|
+
setPerModule(error ? {} : tellOpenTaken(data));
|
|
32
|
+
setLoading(false);
|
|
33
|
+
}, []);
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
void laden();
|
|
36
|
+
}, [laden]);
|
|
37
|
+
return (_jsxs("div", { className: "space-y-6 p-6", children: [_jsxs("div", { className: "flex flex-wrap items-start justify-between gap-4", children: [_jsxs("div", { children: [_jsxs("h1", { className: "flex items-center gap-2 text-2xl font-semibold tracking-tight", children: [_jsx(Workflow, { className: "h-6 w-6 text-primary" }), "Overzicht & meldingen"] }), _jsx("p", { className: "text-muted-foreground text-sm", children: "Wat er vandaag klaarstaat en waar aandacht voor nodig is." })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { variant: "outline", asChild: true, children: _jsx(Link, { to: "/logistiek-engine", children: "Terug naar overzicht" }) }), _jsxs(Button, { variant: "outline", onClick: () => void laden(), disabled: loading, children: [_jsx(RefreshCw, { className: "mr-2 h-4 w-4" }), "Vernieuwen"] })] })] }), _jsx(InklapbaarVenster, { titel: "Binnengekomen orders", omschrijving: "Klik een order open voor elke stap, beslissing, foutmelding en elk document.", standaardOpen: true, children: _jsx(OrderLogboek, { titel: "Binnengekomen orders" }) }), _jsx(InklapbaarVenster, { titel: "Consistentie", omschrijving: "Controle of alle modules hetzelfde tonen.", children: _jsx(ConsistentieCard, {}) }), _jsx(InklapbaarVenster, { titel: "Beladingsafwijkingen", omschrijving: "Verschillen tussen onze belading en de laatste Floriday-gegevens.", children: _jsx(BeladingsafwijkingenCard, {}) }), _jsx(InklapbaarVenster, { titel: "Karplanstatus", omschrijving: "Stand van de karrenindeling.", children: _jsx(KarplanStatusCard, {}) }), _jsx(InklapbaarVenster, { titel: "Kardekking", omschrijving: "Staat elke bestelregel correct op een kar?", children: _jsx(KardekkingCard, {}) }), _jsx(InklapbaarVenster, { titel: "Vergelijkende planner", omschrijving: "Proefvergelijking: blijft de karindeling gelijk?", children: _jsx(PlannerDiffCard, {}) }), _jsxs(InklapbaarVenster, { titel: "Taken per module", omschrijving: "Openstaande en geplande taken.", children: [telFout && _jsxs("p", { className: "mb-2 text-sm text-destructive", children: ["Telling niet beschikbaar: ", telFout] }), _jsx("div", { className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3", children: Object.keys(LOGISTICS_MODULES).map((key) => (_jsxs(Card, { children: [_jsxs(CardHeader, { className: "pb-2", children: [_jsx(CardDescription, { children: LOGISTICS_MODULES[key] }), _jsx(CardTitle, { className: "text-2xl", children: perModule[key] ?? 0 })] }), _jsx(CardContent, { className: "text-muted-foreground text-xs", children: "openstaande en geplande taken" })] }, key))) })] })] }));
|
|
30
38
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TaakGezondheidCard.d.ts","sourceRoot":"","sources":["../../src/components/TaakGezondheidCard.tsx"],"names":[],"mappings":"AAoBA,wBAAgB,kBAAkB,gCA6DjC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// ============================================================================
|
|
3
|
+
// src/components/TaakGezondheidCard.tsx — draaien de achtergrondtaken nog?
|
|
4
|
+
// ============================================================================
|
|
5
|
+
// Toont per achtergrondtaak van de engine wanneer hij voor het laatst draaide
|
|
6
|
+
// en of hij mislukt. Alleen lezen.
|
|
7
|
+
// ============================================================================
|
|
8
|
+
import { AlertTriangle, CheckCircle2, RefreshCw } from "lucide-react";
|
|
9
|
+
import { useTaakGezondheid } from "../hooks/useTaakGezondheid";
|
|
10
|
+
function geleden(tijd) {
|
|
11
|
+
if (!tijd)
|
|
12
|
+
return "nooit gedraaid";
|
|
13
|
+
const ms = Date.now() - new Date(tijd).getTime();
|
|
14
|
+
if (!Number.isFinite(ms))
|
|
15
|
+
return "onbekend";
|
|
16
|
+
const min = Math.round(ms / 60000);
|
|
17
|
+
if (min < 1)
|
|
18
|
+
return "zojuist";
|
|
19
|
+
if (min < 60)
|
|
20
|
+
return `${min} min geleden`;
|
|
21
|
+
return `${Math.round(min / 6) / 10} uur geleden`;
|
|
22
|
+
}
|
|
23
|
+
export function TaakGezondheidCard() {
|
|
24
|
+
const { taken, laden, fout, herlaad } = useTaakGezondheid();
|
|
25
|
+
const veilig = Array.isArray(taken) ? taken : [];
|
|
26
|
+
const problemen = veilig.filter((t) => !t.actief || t.mislukt24u > 0 || t.laatsteStatus === "failed");
|
|
27
|
+
return (_jsxs("div", { className: "rounded-lg border bg-card p-4 text-card-foreground", children: [_jsxs("div", { className: "mb-3 flex items-center justify-between gap-2", children: [_jsxs("div", { children: [_jsx("h3", { className: "font-semibold", children: "Achtergrondtaken" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Indelen, taken uitvoeren, afronden en opruimen draaien automatisch." })] }), _jsx("button", { type: "button", onClick: () => void herlaad(), className: "rounded-md border px-3 py-1.5 text-sm hover:bg-accent", "aria-label": "Vernieuwen", children: _jsx(RefreshCw, { className: "h-4 w-4" }) })] }), fout ? _jsx("p", { className: "mb-3 text-sm text-destructive", children: fout }) : null, !laden && problemen.length === 0 && veilig.length > 0 ? (_jsxs("p", { className: "mb-3 flex items-center gap-2 text-sm text-muted-foreground", children: [_jsx(CheckCircle2, { className: "h-4 w-4" }), " Alle taken draaien zonder fouten."] })) : null, _jsx("ul", { className: "space-y-2", children: veilig.map((t) => {
|
|
28
|
+
const slecht = !t.actief || t.mislukt24u > 0 || t.laatsteStatus === "failed";
|
|
29
|
+
return (_jsxs("li", { className: "rounded-md border p-3 text-sm", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx("span", { className: "font-medium", children: t.naam }), _jsx("span", { className: slecht ? "text-destructive" : "text-muted-foreground", children: geleden(t.laatsteStart) })] }), _jsxs("p", { className: "text-muted-foreground", children: [t.actief ? "actief" : "uitgezet", " \u00B7 ", t.schema, t.mislukt24u > 0 ? ` · ${t.mislukt24u} keer mislukt in 24 uur` : ""] }), slecht && t.laatsteBericht ? (_jsxs("p", { className: "mt-1 flex items-start gap-2 text-destructive", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0" }), t.laatsteBericht] })) : null] }, t.naam));
|
|
30
|
+
}) })] }));
|
|
31
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export interface AfrondStand {
|
|
2
|
+
wachtend: number;
|
|
3
|
+
bezig: number;
|
|
4
|
+
mislukt: number;
|
|
5
|
+
oudsteWachttijdSeconden: number;
|
|
6
|
+
laatsteFout: string | null;
|
|
7
|
+
laatstAfgerond: string | null;
|
|
8
|
+
}
|
|
9
|
+
export interface AfrondRegel {
|
|
10
|
+
orderId: string;
|
|
11
|
+
status: string;
|
|
12
|
+
pogingen: number;
|
|
13
|
+
maxPogingen: number;
|
|
14
|
+
reden: string | null;
|
|
15
|
+
fout: string | null;
|
|
16
|
+
aangemeldOp: string | null;
|
|
17
|
+
volgendePoging: string | null;
|
|
18
|
+
}
|
|
19
|
+
export declare function useAfrondWachtrij(intervalMs?: number): {
|
|
20
|
+
stand: AfrondStand;
|
|
21
|
+
regels: AfrondRegel[];
|
|
22
|
+
laden: boolean;
|
|
23
|
+
fout: string | null;
|
|
24
|
+
herlaad: () => Promise<void>;
|
|
25
|
+
opnieuw: (orderId: string) => Promise<boolean>;
|
|
26
|
+
naloop: () => Promise<boolean>;
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=useAfrondWachtrij.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useAfrondWachtrij.d.ts","sourceRoot":"","sources":["../../src/hooks/useAfrondWachtrij.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,uBAAuB,EAAE,MAAM,CAAC;IAChC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAWD,wBAAgB,iBAAiB,CAAC,UAAU,SAAQ;;;;;;uBA4DhC,MAAM;;EAuBzB"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// src/hooks/useAfrondWachtrij.ts — stand van de afrondwachtrij na de controle
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// Leest logistics_order_finalize_overview en de openstaande/mislukte regels.
|
|
5
|
+
// Muteert niets, behalve wanneer expliciet opnieuw proberen wordt gevraagd.
|
|
6
|
+
// ============================================================================
|
|
7
|
+
import { useCallback, useEffect, useState } from "react";
|
|
8
|
+
import { supabase } from "../integrations/supabase/moduleClient.js";
|
|
9
|
+
const LEEG = {
|
|
10
|
+
wachtend: 0,
|
|
11
|
+
bezig: 0,
|
|
12
|
+
mislukt: 0,
|
|
13
|
+
oudsteWachttijdSeconden: 0,
|
|
14
|
+
laatsteFout: null,
|
|
15
|
+
laatstAfgerond: null,
|
|
16
|
+
};
|
|
17
|
+
export function useAfrondWachtrij(intervalMs = 20000) {
|
|
18
|
+
const [stand, setStand] = useState(LEEG);
|
|
19
|
+
const [regels, setRegels] = useState([]);
|
|
20
|
+
const [laden, setLaden] = useState(true);
|
|
21
|
+
const [fout, setFout] = useState(null);
|
|
22
|
+
const laad = useCallback(async () => {
|
|
23
|
+
setFout(null);
|
|
24
|
+
const { data, error } = await supabase.rpc("logistics_order_finalize_overview");
|
|
25
|
+
if (error) {
|
|
26
|
+
setFout(error.message);
|
|
27
|
+
setLaden(false);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const rijen = Array.isArray(data) ? data : [];
|
|
31
|
+
const r = (rijen[0] ?? {});
|
|
32
|
+
setStand({
|
|
33
|
+
wachtend: Number(r["wachtend"] ?? 0),
|
|
34
|
+
bezig: Number(r["bezig"] ?? 0),
|
|
35
|
+
mislukt: Number(r["mislukt"] ?? 0),
|
|
36
|
+
oudsteWachttijdSeconden: Number(r["oudste_wachttijd_seconden"] ?? 0),
|
|
37
|
+
laatsteFout: r["laatste_fout"] ?? null,
|
|
38
|
+
laatstAfgerond: r["laatst_afgerond"] ?? null,
|
|
39
|
+
});
|
|
40
|
+
const { data: lijst } = await supabase
|
|
41
|
+
.from("logistics_order_finalize_queue")
|
|
42
|
+
.select("order_id,status,attempts,max_attempts,reason,last_error,requested_at,next_attempt_at")
|
|
43
|
+
.in("status", ["pending", "running", "failed"])
|
|
44
|
+
.order("requested_at", { ascending: true })
|
|
45
|
+
.limit(50);
|
|
46
|
+
const veilig = Array.isArray(lijst) ? lijst : [];
|
|
47
|
+
setRegels(veilig.map((x) => {
|
|
48
|
+
const row = x;
|
|
49
|
+
return {
|
|
50
|
+
orderId: String(row["order_id"] ?? ""),
|
|
51
|
+
status: String(row["status"] ?? ""),
|
|
52
|
+
pogingen: Number(row["attempts"] ?? 0),
|
|
53
|
+
maxPogingen: Number(row["max_attempts"] ?? 0),
|
|
54
|
+
reden: row["reason"] ?? null,
|
|
55
|
+
fout: row["last_error"] ?? null,
|
|
56
|
+
aangemeldOp: row["requested_at"] ?? null,
|
|
57
|
+
volgendePoging: row["next_attempt_at"] ?? null,
|
|
58
|
+
};
|
|
59
|
+
}));
|
|
60
|
+
setLaden(false);
|
|
61
|
+
}, []);
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
void laad();
|
|
64
|
+
const t = setInterval(() => {
|
|
65
|
+
void laad();
|
|
66
|
+
}, Math.max(5000, intervalMs));
|
|
67
|
+
return () => clearInterval(t);
|
|
68
|
+
}, [laad, intervalMs]);
|
|
69
|
+
const opnieuw = useCallback(async (orderId) => {
|
|
70
|
+
const { error } = await supabase.rpc("logistics_order_finalize_retry", { p_order_id: orderId });
|
|
71
|
+
if (error) {
|
|
72
|
+
setFout(error.message);
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
await laad();
|
|
76
|
+
return true;
|
|
77
|
+
}, [laad]);
|
|
78
|
+
const naloop = useCallback(async () => {
|
|
79
|
+
const { error } = await supabase.rpc("logistics_order_finalize_reconcile", { p_days: 14, p_limit: 200 });
|
|
80
|
+
if (error) {
|
|
81
|
+
setFout(error.message);
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
await laad();
|
|
85
|
+
return true;
|
|
86
|
+
}, [laad]);
|
|
87
|
+
return { stand, regels, laden, fout, herlaad: laad, opnieuw, naloop };
|
|
88
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export interface BeladingStand {
|
|
2
|
+
openTotaal: number;
|
|
3
|
+
beoordelen: number;
|
|
4
|
+
herstelbaar: number;
|
|
5
|
+
producten: number;
|
|
6
|
+
orders: number;
|
|
7
|
+
oudsteSeconden: number;
|
|
8
|
+
laatstHersteld: string | null;
|
|
9
|
+
laatstGemeten: string | null;
|
|
10
|
+
}
|
|
11
|
+
export interface Belading {
|
|
12
|
+
piecesPerPackage: number | null;
|
|
13
|
+
packagesPerLayer: number | null;
|
|
14
|
+
layersPerLoadCarrier: number | null;
|
|
15
|
+
loadCarrier: string | null;
|
|
16
|
+
}
|
|
17
|
+
export interface Beladingsafwijking {
|
|
18
|
+
id: string;
|
|
19
|
+
soort: "product" | "order_line";
|
|
20
|
+
productNaam: string | null;
|
|
21
|
+
orderNummer: string | null;
|
|
22
|
+
lokaal: Belading;
|
|
23
|
+
floriday: Belading;
|
|
24
|
+
beoordelen: boolean;
|
|
25
|
+
reden: string | null;
|
|
26
|
+
sinds: string | null;
|
|
27
|
+
laatstGezien: string | null;
|
|
28
|
+
}
|
|
29
|
+
export declare function useBeladingsafwijkingen(intervalMs?: number): {
|
|
30
|
+
stand: BeladingStand;
|
|
31
|
+
afwijkingen: Beladingsafwijking[];
|
|
32
|
+
laden: boolean;
|
|
33
|
+
bezig: boolean;
|
|
34
|
+
fout: string | null;
|
|
35
|
+
herlaad: () => Promise<void>;
|
|
36
|
+
nuControleren: () => Promise<boolean>;
|
|
37
|
+
afsluiten: (id: string, notitie?: string) => Promise<boolean>;
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=useBeladingsafwijkingen.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useBeladingsafwijkingen.d.ts","sourceRoot":"","sources":["../../src/hooks/useBeladingsafwijkingen.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,MAAM,WAAW,QAAQ;IACvB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,SAAS,GAAG,YAAY,CAAC;IAChC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,QAAQ,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAwBD,wBAAgB,uBAAuB,CAAC,UAAU,SAAQ;;;;;;;;oBAsF3C,MAAM,YAAY,MAAM;EAgBtC"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// src/hooks/useBeladingsafwijkingen.ts — verschillen tussen onze belading en Floriday
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// Leest logistics_packing_overview en de openstaande afwijkingen. Muteert niets,
|
|
5
|
+
// behalve wanneer expliciet om een nieuwe controle of om afsluiten wordt gevraagd.
|
|
6
|
+
// Een regel die handmatig is vastgezet wordt nooit automatisch aangepast; dat
|
|
7
|
+
// bewaakt de database zelf.
|
|
8
|
+
// ============================================================================
|
|
9
|
+
import { useCallback, useEffect, useState } from "react";
|
|
10
|
+
import { supabase } from "../integrations/supabase/moduleClient.js";
|
|
11
|
+
const LEEG = {
|
|
12
|
+
openTotaal: 0,
|
|
13
|
+
beoordelen: 0,
|
|
14
|
+
herstelbaar: 0,
|
|
15
|
+
producten: 0,
|
|
16
|
+
orders: 0,
|
|
17
|
+
oudsteSeconden: 0,
|
|
18
|
+
laatstHersteld: null,
|
|
19
|
+
laatstGemeten: null,
|
|
20
|
+
};
|
|
21
|
+
function leesBelading(waarde) {
|
|
22
|
+
const b = (waarde ?? {});
|
|
23
|
+
const getal = (v) => (v === null || v === undefined ? null : Number(v));
|
|
24
|
+
return {
|
|
25
|
+
piecesPerPackage: getal(b["pieces_per_package"]),
|
|
26
|
+
packagesPerLayer: getal(b["packages_per_layer"]),
|
|
27
|
+
layersPerLoadCarrier: getal(b["layers_per_load_carrier"]),
|
|
28
|
+
loadCarrier: b["load_carrier"] ?? null,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export function useBeladingsafwijkingen(intervalMs = 60000) {
|
|
32
|
+
const [stand, setStand] = useState(LEEG);
|
|
33
|
+
const [afwijkingen, setAfwijkingen] = useState([]);
|
|
34
|
+
const [laden, setLaden] = useState(true);
|
|
35
|
+
const [bezig, setBezig] = useState(false);
|
|
36
|
+
const [fout, setFout] = useState(null);
|
|
37
|
+
const laad = useCallback(async () => {
|
|
38
|
+
setFout(null);
|
|
39
|
+
const { data, error } = await supabase.rpc("logistics_packing_overview");
|
|
40
|
+
if (error) {
|
|
41
|
+
setFout(error.message);
|
|
42
|
+
setLaden(false);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const rijen = Array.isArray(data) ? data : [];
|
|
46
|
+
const r = (rijen[0] ?? {});
|
|
47
|
+
setStand({
|
|
48
|
+
openTotaal: Number(r["open_totaal"] ?? 0),
|
|
49
|
+
beoordelen: Number(r["beoordelen"] ?? 0),
|
|
50
|
+
herstelbaar: Number(r["herstelbaar"] ?? 0),
|
|
51
|
+
producten: Number(r["producten"] ?? 0),
|
|
52
|
+
orders: Number(r["orders"] ?? 0),
|
|
53
|
+
oudsteSeconden: Number(r["oudste_seconden"] ?? 0),
|
|
54
|
+
laatstHersteld: r["laatst_hersteld"] ?? null,
|
|
55
|
+
laatstGemeten: r["laatst_gemeten"] ?? null,
|
|
56
|
+
});
|
|
57
|
+
const { data: lijst, error: lijstFout } = await supabase
|
|
58
|
+
.from("logistics_packing_deviations")
|
|
59
|
+
.select("id,scope,product_name,order_number,local_packing,floriday_packing,needs_human,human_reason,first_seen_at,last_seen_at")
|
|
60
|
+
.is("resolved_at", null)
|
|
61
|
+
.order("first_seen_at", { ascending: true })
|
|
62
|
+
.limit(100);
|
|
63
|
+
if (lijstFout)
|
|
64
|
+
setFout(lijstFout.message);
|
|
65
|
+
const veilig = Array.isArray(lijst) ? lijst : [];
|
|
66
|
+
setAfwijkingen(veilig.map((x) => {
|
|
67
|
+
const row = (x ?? {});
|
|
68
|
+
return {
|
|
69
|
+
id: String(row["id"] ?? ""),
|
|
70
|
+
soort: row["scope"] ?? "order_line",
|
|
71
|
+
productNaam: row["product_name"] ?? null,
|
|
72
|
+
orderNummer: row["order_number"] ?? null,
|
|
73
|
+
lokaal: leesBelading(row["local_packing"]),
|
|
74
|
+
floriday: leesBelading(row["floriday_packing"]),
|
|
75
|
+
beoordelen: row["needs_human"] === true,
|
|
76
|
+
reden: row["human_reason"] ?? null,
|
|
77
|
+
sinds: row["first_seen_at"] ?? null,
|
|
78
|
+
laatstGezien: row["last_seen_at"] ?? null,
|
|
79
|
+
};
|
|
80
|
+
}));
|
|
81
|
+
setLaden(false);
|
|
82
|
+
}, []);
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
void laad();
|
|
85
|
+
const t = setInterval(() => {
|
|
86
|
+
void laad();
|
|
87
|
+
}, Math.max(15000, intervalMs));
|
|
88
|
+
return () => clearInterval(t);
|
|
89
|
+
}, [laad, intervalMs]);
|
|
90
|
+
/** Nu meten en waar veilig mogelijk bijwerken. */
|
|
91
|
+
const nuControleren = useCallback(async () => {
|
|
92
|
+
setBezig(true);
|
|
93
|
+
const { error } = await supabase.rpc("logistics_packing_reconcile", {
|
|
94
|
+
p_scan_limit: 500,
|
|
95
|
+
p_fix_limit: 100,
|
|
96
|
+
});
|
|
97
|
+
setBezig(false);
|
|
98
|
+
if (error) {
|
|
99
|
+
setFout(error.message);
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
await laad();
|
|
103
|
+
return true;
|
|
104
|
+
}, [laad]);
|
|
105
|
+
/** Een afwijking bewust afsluiten nadat een mens ernaar heeft gekeken. */
|
|
106
|
+
const afsluiten = useCallback(async (id, notitie) => {
|
|
107
|
+
const { error } = await supabase.rpc("logistics_packing_dismiss", {
|
|
108
|
+
p_id: id,
|
|
109
|
+
p_note: notitie ?? null,
|
|
110
|
+
});
|
|
111
|
+
if (error) {
|
|
112
|
+
setFout(error.message);
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
await laad();
|
|
116
|
+
return true;
|
|
117
|
+
}, [laad]);
|
|
118
|
+
return { stand, afwijkingen, laden, bezig, fout, herlaad: laad, nuControleren, afsluiten };
|
|
119
|
+
}
|
|
@@ -24,5 +24,12 @@ export declare function useGeblokkeerdeOrders(): {
|
|
|
24
24
|
busy: boolean;
|
|
25
25
|
refresh: () => Promise<void>;
|
|
26
26
|
retry: (orderIds: string[]) => Promise<number>;
|
|
27
|
+
dubbelenOpruimen: (orderId: string) => Promise<{
|
|
28
|
+
ok: boolean;
|
|
29
|
+
reden: string;
|
|
30
|
+
verwijderd: number;
|
|
31
|
+
na: number;
|
|
32
|
+
meerTeDoen: boolean;
|
|
33
|
+
}>;
|
|
27
34
|
};
|
|
28
35
|
//# sourceMappingURL=useGeblokkeerdeOrders.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useGeblokkeerdeOrders.d.ts","sourceRoot":"","sources":["../../src/hooks/useGeblokkeerdeOrders.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"useGeblokkeerdeOrders.d.ts","sourceRoot":"","sources":["../../src/hooks/useGeblokkeerdeOrders.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAc9C,CAAC;AAEF,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,UAGzD;AAED,wBAAgB,qBAAqB;;;;;;;sBAqDhB,MAAM,EAAE;gCAwBT,MAAM;;;;;;;EAyBzB"}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// logistics_blocked_orders / logistics_blocked_overview / logistics_plan_retry.
|
|
5
5
|
// ============================================================================
|
|
6
6
|
import { useCallback, useEffect, useState } from "react";
|
|
7
|
-
import { supabase } from "../integrations/supabase/moduleClient";
|
|
7
|
+
import { supabase } from "../integrations/supabase/moduleClient.js";
|
|
8
8
|
export const REDEN_LABEL = {
|
|
9
9
|
belading_onbekend: "Belading onbekend",
|
|
10
10
|
belading_onvolledig: "Belading onvolledig",
|
|
@@ -15,6 +15,7 @@ export const REDEN_LABEL = {
|
|
|
15
15
|
geen_karren: "Geen karren berekend",
|
|
16
16
|
verificatie_mislukt: "Controle mislukt",
|
|
17
17
|
bufferdekking_onvolledig: "Buffer niet volledig gedekt",
|
|
18
|
+
dubbele_regels: "Dubbele bestelregels",
|
|
18
19
|
karindeling_geblokkeerd: "Karindeling geblokkeerd",
|
|
19
20
|
other: "Overige reden",
|
|
20
21
|
};
|
|
@@ -90,5 +91,30 @@ export function useGeblokkeerdeOrders() {
|
|
|
90
91
|
setBusy(false);
|
|
91
92
|
}
|
|
92
93
|
}, [load]);
|
|
93
|
-
|
|
94
|
+
/**
|
|
95
|
+
* Dubbele bestelregels van één order opruimen. Bewust handmatig: van elke
|
|
96
|
+
* identieke regel blijft er één staan. De Engine weigert bij orders waarvan
|
|
97
|
+
* al iets is geprint, gescand of gecontroleerd.
|
|
98
|
+
*/
|
|
99
|
+
const dubbelenOpruimen = useCallback(async (orderId) => {
|
|
100
|
+
setBusy(true);
|
|
101
|
+
try {
|
|
102
|
+
const { data, error: rpcError } = await supabase.rpc("logistics_cleanup_duplicate_order_lines", { p_order_id: orderId });
|
|
103
|
+
if (rpcError)
|
|
104
|
+
throw new Error(rpcError.message);
|
|
105
|
+
const raw = (data ?? {});
|
|
106
|
+
await load();
|
|
107
|
+
return {
|
|
108
|
+
ok: Boolean(raw["ok"]),
|
|
109
|
+
reden: raw["reden"] ?? null,
|
|
110
|
+
verwijderd: Number(raw["verwijderd"] ?? 0),
|
|
111
|
+
na: Number(raw["na"] ?? 0),
|
|
112
|
+
meerTeDoen: Boolean(raw["meerTeDoen"]),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
finally {
|
|
116
|
+
setBusy(false);
|
|
117
|
+
}
|
|
118
|
+
}, [load]);
|
|
119
|
+
return { rows, overzicht, loading, error, busy, refresh: load, retry, dubbelenOpruimen };
|
|
94
120
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// staat, is echt door de Engine vastgelegd.
|
|
7
7
|
// ============================================================================
|
|
8
8
|
import { useCallback, useEffect, useState } from "react";
|
|
9
|
-
import { supabase } from "../integrations/supabase/moduleClient";
|
|
9
|
+
import { supabase } from "../integrations/supabase/moduleClient.js";
|
|
10
10
|
function mapOrder(r) {
|
|
11
11
|
return {
|
|
12
12
|
orderRef: String(r?.order_ref ?? ""),
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// afgeleid of aangevuld: wat hier staat is echt door de Engine vastgelegd.
|
|
7
7
|
// ============================================================================
|
|
8
8
|
import { useCallback, useEffect, useState } from "react";
|
|
9
|
-
import { supabase } from "../integrations/supabase/moduleClient";
|
|
9
|
+
import { supabase } from "../integrations/supabase/moduleClient.js";
|
|
10
10
|
const LEEG = { search: "", state: "", errorClass: "", source: "", step: "", hours: 72 };
|
|
11
11
|
function mapRij(r) {
|
|
12
12
|
return {
|