@flowselections/logistiek-engine 1.0.187 → 1.0.191

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.
Files changed (52) hide show
  1. package/db/schema/logistiek-engine.sql +1897 -76
  2. package/dist-lib/components/BeladingIndelingCard.d.ts.map +1 -1
  3. package/dist-lib/components/BeladingIndelingCard.js +4 -2
  4. package/dist-lib/components/GeblokkeerdeOrdersCard.d.ts.map +1 -1
  5. package/dist-lib/components/GeblokkeerdeOrdersCard.js +43 -4
  6. package/dist-lib/components/LadingdragersPage.d.ts.map +1 -1
  7. package/dist-lib/components/LadingdragersPage.js +4 -1
  8. package/dist-lib/components/OrdersZonderKarCard.d.ts +2 -0
  9. package/dist-lib/components/OrdersZonderKarCard.d.ts.map +1 -0
  10. package/dist-lib/components/OrdersZonderKarCard.js +36 -0
  11. package/dist-lib/components/RegiePage.d.ts.map +1 -1
  12. package/dist-lib/components/RegiePage.js +2 -1
  13. package/dist-lib/components/RuleEnginePage.js +8 -3
  14. package/dist-lib/hooks/useFloricodeLadingdragers.d.ts +2 -0
  15. package/dist-lib/hooks/useFloricodeLadingdragers.d.ts.map +1 -0
  16. package/dist-lib/hooks/useFloricodeLadingdragers.js +25 -0
  17. package/dist-lib/hooks/useGeblokkeerdeOrders.d.ts +1 -0
  18. package/dist-lib/hooks/useGeblokkeerdeOrders.d.ts.map +1 -1
  19. package/dist-lib/hooks/useGeblokkeerdeOrders.js +38 -1
  20. package/dist-lib/hooks/useRecalcJob.d.ts +1 -0
  21. package/dist-lib/hooks/useRecalcJob.d.ts.map +1 -1
  22. package/dist-lib/hooks/useRecalcJob.js +11 -1
  23. package/dist-lib/integrations/supabase/types.d.ts +2460 -76
  24. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  25. package/dist-lib/lib/beladingRegels.d.ts +18 -0
  26. package/dist-lib/lib/beladingRegels.d.ts.map +1 -1
  27. package/dist-lib/lib/beladingRegels.js +28 -0
  28. package/dist-lib/lib/handlers/logistics-tick.server.d.ts.map +1 -1
  29. package/dist-lib/lib/handlers/logistics-tick.server.js +14 -0
  30. package/dist-lib/lib/loadCarrierPlanner.d.ts +3 -0
  31. package/dist-lib/lib/loadCarrierPlanner.d.ts.map +1 -1
  32. package/dist-lib/lib/loadCarrierPlanner.js +18 -13
  33. package/dist-lib/lib/loadCarrierPlanning.server.d.ts +16 -0
  34. package/dist-lib/lib/loadCarrierPlanning.server.d.ts.map +1 -1
  35. package/dist-lib/lib/loadCarrierPlanning.server.js +394 -24
  36. package/dist-lib/lib/loadCarrierTypes.d.ts +7 -0
  37. package/dist-lib/lib/loadCarrierTypes.d.ts.map +1 -1
  38. package/dist-lib/lib/loadCarrierTypes.js +17 -0
  39. package/dist-lib/lib/logisticsRecalcJob.server.d.ts +1 -0
  40. package/dist-lib/lib/logisticsRecalcJob.server.d.ts.map +1 -1
  41. package/dist-lib/lib/logisticsRecalcJob.server.js +3 -1
  42. package/dist-lib/lib/logisticsRecalculate.functions.js +2 -2
  43. package/dist-lib/lib/moduleVersion.d.ts +1 -1
  44. package/dist-lib/lib/moduleVersion.js +1 -1
  45. package/dist-lib/lib/planQueue.server.d.ts.map +1 -1
  46. package/dist-lib/lib/planQueue.server.js +6 -1
  47. package/dist-lib/lib/ruleEngineSchema.d.ts.map +1 -1
  48. package/dist-lib/lib/ruleEngineSchema.js +9 -0
  49. package/dist-lib/lib/scheduleTasks.server.d.ts +18 -0
  50. package/dist-lib/lib/scheduleTasks.server.d.ts.map +1 -0
  51. package/dist-lib/lib/scheduleTasks.server.js +65 -0
  52. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"BeladingIndelingCard.d.ts","sourceRoot":"","sources":["../../src/components/BeladingIndelingCard.tsx"],"names":[],"mappings":"AAoCA,wBAAgB,oBAAoB,gCAoJnC"}
1
+ {"version":3,"file":"BeladingIndelingCard.d.ts","sourceRoot":"","sources":["../../src/components/BeladingIndelingCard.tsx"],"names":[],"mappings":"AAqCA,wBAAgB,oBAAoB,gCAqJnC"}
@@ -10,10 +10,12 @@ import { Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitl
10
10
  import { useServerFn } from "@tanstack/react-start";
11
11
  import { Boxes, Search } from "lucide-react";
12
12
  import { previewLoadCarrierPlan, replanOrderLoadCarriers } from "../lib/loadCarrierPlanning.functions.js";
13
- import { carrierLabel } from "../lib/loadCarrierTypes.js";
13
+ import { ladingdragerNaam } from "../lib/loadCarrierTypes.js";
14
+ import { useFloricodeLadingdragers } from "../hooks/useFloricodeLadingdragers.js";
14
15
  import { layerUtilizationPct } from "../lib/layerUtilization.js";
15
16
  const db = () => supabase;
16
17
  export function BeladingIndelingCard() {
18
+ const floricodeNamen = useFloricodeLadingdragers();
17
19
  const preview = useServerFn(previewLoadCarrierPlan);
18
20
  const replan = useServerFn(replanOrderLoadCarriers);
19
21
  const [orderNummer, setOrderNummer] = useState("");
@@ -76,7 +78,7 @@ export function BeladingIndelingCard() {
76
78
  }
77
79
  };
78
80
  const safeCarts = Array.isArray(carts) ? carts : [];
79
- return (_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs(CardTitle, { className: "flex items-center gap-2", children: [_jsx(Boxes, { className: "h-5 w-5 text-primary" }), "Beladingindeling"] }), _jsx(CardDescription, { children: "De Engine deelt de orderregels van de hele buffer in over ladingdragers. Bekijk eerst het resultaat en leg het daarna vast." }), _jsxs("div", { className: "flex flex-wrap items-end gap-2 pt-2", children: [_jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "indeling-order", children: "Ordernummer" }), _jsx(Input, { id: "indeling-order", value: orderNummer, onChange: (e) => setOrderNummer(e.target.value), placeholder: "SO-12345", className: "w-56" })] }), _jsxs(Button, { onClick: () => void bereken(), disabled: bezig || !orderNummer.trim(), children: [_jsx(Search, { className: "mr-2 h-4 w-4" }), "Berekenen"] }), _jsx(Button, { variant: "outline", onClick: () => void vastleggen(), disabled: bezig || !orderId, children: "Vastleggen" })] })] }), _jsxs(CardContent, { className: "space-y-4", children: [warnings.length > 0 && (_jsx("ul", { className: "space-y-1 rounded-lg border border-dashed p-3 text-sm text-muted-foreground", children: warnings.map((w, i) => (_jsx("li", { children: w }, i))) })), safeCarts.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Nog geen indeling berekend." })) : (safeCarts.map((cart) => (_jsxs("div", { className: "space-y-2 rounded-lg border p-3", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs("span", { className: "font-medium", children: ["Ladingdrager ", cart.cart_number] }), _jsx(Badge, { variant: "secondary", children: carrierLabel(cart.carrier_type) }), _jsxs(Badge, { variant: cart.is_complete ? "default" : "outline", children: [cart.occupancy_pct, "% vol"] }), _jsxs("span", { className: "text-xs text-muted-foreground", children: [cart.total_layers_used, " van ", cart.max_layers, " lagen", (() => {
81
+ return (_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs(CardTitle, { className: "flex items-center gap-2", children: [_jsx(Boxes, { className: "h-5 w-5 text-primary" }), "Beladingindeling"] }), _jsx(CardDescription, { children: "De Engine deelt de orderregels van de hele buffer in over ladingdragers. Bekijk eerst het resultaat en leg het daarna vast." }), _jsxs("div", { className: "flex flex-wrap items-end gap-2 pt-2", children: [_jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "indeling-order", children: "Ordernummer" }), _jsx(Input, { id: "indeling-order", value: orderNummer, onChange: (e) => setOrderNummer(e.target.value), placeholder: "SO-12345", className: "w-56" })] }), _jsxs(Button, { onClick: () => void bereken(), disabled: bezig || !orderNummer.trim(), children: [_jsx(Search, { className: "mr-2 h-4 w-4" }), "Berekenen"] }), _jsx(Button, { variant: "outline", onClick: () => void vastleggen(), disabled: bezig || !orderId, children: "Vastleggen" })] })] }), _jsxs(CardContent, { className: "space-y-4", children: [warnings.length > 0 && (_jsx("ul", { className: "space-y-1 rounded-lg border border-dashed p-3 text-sm text-muted-foreground", children: warnings.map((w, i) => (_jsx("li", { children: w }, i))) })), safeCarts.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Nog geen indeling berekend." })) : (safeCarts.map((cart) => (_jsxs("div", { className: "space-y-2 rounded-lg border p-3", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs("span", { className: "font-medium", children: ["Ladingdrager ", cart.cart_number] }), _jsx(Badge, { variant: "secondary", children: ladingdragerNaam(cart.carrier_type, floricodeNamen) }), _jsxs(Badge, { variant: cart.is_complete ? "default" : "outline", children: [cart.occupancy_pct, "% vol"] }), _jsxs("span", { className: "text-xs text-muted-foreground", children: [cart.total_layers_used, " van ", cart.max_layers, " lagen", (() => {
80
82
  const p = layerUtilizationPct(cart.total_layers_used, cart.max_layers);
81
83
  return p === null ? "" : ` · ${p}% van de kar`;
82
84
  })(), cart.weight_kg ? ` · ${cart.weight_kg} kg` : ""] })] }), _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Partij" }), _jsx(TableHead, { className: "text-right", children: "Fusten" }), _jsx(TableHead, { className: "text-right", children: "Planten" }), _jsx(TableHead, { className: "text-right", children: "Aandeel" })] }) }), _jsx(TableBody, { children: (Array.isArray(cart.items) ? cart.items : []).map((item, idx) => (_jsxs(TableRow, { children: [_jsx(TableCell, { className: "font-medium", children: item.product_name }), _jsx(TableCell, { className: "text-right", children: item.crates }), _jsx(TableCell, { className: "text-right", children: item.plants }), _jsxs(TableCell, { className: "text-right", children: [Math.round(item.layer_share_pct * 10) / 10, "%"] })] }, `${item.order_item_id}-${idx}`))) })] })] }, cart.cart_number))))] })] }));
@@ -1 +1 @@
1
- {"version":3,"file":"GeblokkeerdeOrdersCard.d.ts","sourceRoot":"","sources":["../../src/components/GeblokkeerdeOrdersCard.tsx"],"names":[],"mappings":"AA6BA,wBAAgB,sBAAsB,gCA8KrC"}
1
+ {"version":3,"file":"GeblokkeerdeOrdersCard.d.ts","sourceRoot":"","sources":["../../src/components/GeblokkeerdeOrdersCard.tsx"],"names":[],"mappings":"AA8BA,wBAAgB,sBAAsB,gCAiMrC"}
@@ -7,8 +7,8 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
7
7
  // Alles komt uit de Engine-functies; hier wordt niets zelf berekend.
8
8
  // GEEN <Header /> hier — die levert de shell.
9
9
  // ============================================================================
10
- import { useMemo, useState } from "react";
11
- import { Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Skeleton, } from "@flowselections/core";
10
+ import { useEffect, useMemo, useState } from "react";
11
+ import { Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Skeleton, supabase, } from "@flowselections/core";
12
12
  import { AlertTriangle, RefreshCw, RotateCcw } from "lucide-react";
13
13
  import { useGeblokkeerdeOrders, redenLabel } from "../hooks/useGeblokkeerdeOrders.js";
14
14
  function datum(waarde) {
@@ -18,7 +18,16 @@ function datum(waarde) {
18
18
  return Number.isNaN(d.getTime()) ? "—" : d.toLocaleDateString("nl-NL");
19
19
  }
20
20
  export function GeblokkeerdeOrdersCard() {
21
- const { rows, overzicht, loading, error, busy, refresh, retry, dubbelenOpruimen } = useGeblokkeerdeOrders();
21
+ const { rows, overzicht, loading, error, busy, refresh, retry, dubbelenOpruimen, beladingBevestigen } = useGeblokkeerdeOrders();
22
+ const bevestigen = async (orderId, orderRef) => {
23
+ try {
24
+ const n = await beladingBevestigen(orderId);
25
+ setMelding(`Belading van ${orderRef} bevestigd (${n} verpakking(en)). De order wordt opnieuw ingedeeld.`);
26
+ }
27
+ catch (e) {
28
+ setMelding(e?.message ? String(e.message) : "Bevestigen is niet gelukt.");
29
+ }
30
+ };
22
31
  const [filter, setFilter] = useState("");
23
32
  const [melding, setMelding] = useState(null);
24
33
  const safeRows = Array.isArray(rows) ? rows : [];
@@ -59,5 +68,35 @@ export function GeblokkeerdeOrdersCard() {
59
68
  setMelding(e?.message ? String(e.message) : "Opruimen is niet gelukt.");
60
69
  }
61
70
  };
62
- return (_jsxs(Card, { children: [_jsxs(CardHeader, { className: "flex flex-row items-start justify-between gap-4", children: [_jsxs("div", { children: [_jsxs(CardTitle, { className: "flex items-center gap-2", children: [_jsx(AlertTriangle, { className: "h-4 w-4 text-destructive" }), "Vastgelopen orders"] }), _jsx(CardDescription, { children: "Deze orders krijgen geen karrenindeling tot er iets aan de order verandert of je ze hier opnieuw aanbiedt." })] }), _jsxs("div", { className: "flex items-center gap-2", children: [zichtbaar.length > 0 && (_jsxs(Button, { variant: "outline", size: "sm", disabled: busy, onClick: () => void opnieuw(zichtbaar.map((r) => r.orderId)), children: [_jsx(RotateCcw, { className: "mr-2 h-4 w-4" }), "Alles opnieuw proberen"] })), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => void refresh(), disabled: loading, children: _jsx(RefreshCw, { className: `h-4 w-4 ${loading ? "animate-spin" : ""}` }) })] })] }), _jsxs(CardContent, { className: "space-y-4", children: [overzicht?.tickStale && (_jsx("p", { className: "rounded-md bg-muted p-3 text-sm text-muted-foreground", children: "De verwerking op de achtergrond heeft zich langer dan tien minuten niet gemeld. Opnieuw proberen zet de order wel klaar, maar hij wordt pas opgepakt zodra de verwerking weer draait." })), error && _jsx("p", { className: "text-sm text-destructive", children: error }), melding && _jsx("p", { className: "text-sm text-muted-foreground", children: melding }), loading && safeRows.length === 0 ? (_jsxs("div", { className: "space-y-2", children: [_jsx(Skeleton, { className: "h-6 w-full" }), _jsx(Skeleton, { className: "h-6 w-full" })] })) : safeRows.length === 0 ? (_jsxs("p", { className: "text-sm text-muted-foreground", children: ["Geen vastgelopen orders. Alles is ingedeeld of staat nog in de wachtrij", overzicht ? ` (${overzicht.pending} wachtend)` : "", "."] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsxs(Badge, { variant: filter === "" ? "default" : "outline", className: "cursor-pointer", onClick: () => setFilter(""), children: ["Alles (", safeRows.length, ")"] }), codes.map(([code, aantal]) => (_jsxs(Badge, { variant: filter === code ? "default" : "outline", className: "cursor-pointer", onClick: () => setFilter(code), children: [redenLabel(code), " (", aantal, ")"] }, code)))] }), _jsx("div", { className: "divide-y rounded-md border", children: zichtbaar.map((r) => (_jsxs("div", { className: "flex items-start justify-between gap-4 p-3", children: [_jsxs("div", { className: "min-w-0", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("span", { className: "font-medium", children: r.orderRef }), _jsx(Badge, { variant: "secondary", children: redenLabel(r.reasonCode) }), _jsx(Badge, { variant: "outline", children: "Wacht op actie" }), _jsxs("span", { className: "text-xs text-muted-foreground", children: ["Leverdatum ", datum(r.deliveryDate), " \u00B7 sinds ", datum(r.blockedSince)] })] }), r.reason && (_jsx("p", { className: "mt-1 line-clamp-2 text-sm text-muted-foreground", children: r.reason }))] }), _jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [r.reasonCode === "dubbele_regels" && (_jsx(Button, { variant: "secondary", size: "sm", disabled: busy, onClick: () => void opruimen(r.orderId, r.orderRef), children: "Dubbele regels opruimen" })), _jsx(Button, { variant: "outline", size: "sm", disabled: busy, onClick: () => void opnieuw([r.orderId]), children: "Opnieuw" })] })] }, r.queueId))) })] }))] })] }));
71
+ return (_jsxs(Card, { children: [_jsxs(CardHeader, { className: "flex flex-row items-start justify-between gap-4", children: [_jsxs("div", { children: [_jsxs(CardTitle, { className: "flex items-center gap-2", children: [_jsx(AlertTriangle, { className: "h-4 w-4 text-destructive" }), "Vastgelopen orders"] }), _jsx(CardDescription, { children: "Deze orders krijgen geen karrenindeling tot er iets aan de order verandert of je ze hier opnieuw aanbiedt." })] }), _jsxs("div", { className: "flex items-center gap-2", children: [zichtbaar.length > 0 && (_jsxs(Button, { variant: "outline", size: "sm", disabled: busy, onClick: () => void opnieuw(zichtbaar.map((r) => r.orderId)), children: [_jsx(RotateCcw, { className: "mr-2 h-4 w-4" }), "Alles opnieuw proberen"] })), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => void refresh(), disabled: loading, children: _jsx(RefreshCw, { className: `h-4 w-4 ${loading ? "animate-spin" : ""}` }) })] })] }), _jsxs(CardContent, { className: "space-y-4", children: [overzicht?.tickStale && (_jsx("p", { className: "rounded-md bg-muted p-3 text-sm text-muted-foreground", children: "De verwerking op de achtergrond heeft zich langer dan tien minuten niet gemeld. Opnieuw proberen zet de order wel klaar, maar hij wordt pas opgepakt zodra de verwerking weer draait." })), error && _jsx("p", { className: "text-sm text-destructive", children: error }), melding && _jsx("p", { className: "text-sm text-muted-foreground", children: melding }), loading && safeRows.length === 0 ? (_jsxs("div", { className: "space-y-2", children: [_jsx(Skeleton, { className: "h-6 w-full" }), _jsx(Skeleton, { className: "h-6 w-full" })] })) : safeRows.length === 0 ? (_jsxs("p", { className: "text-sm text-muted-foreground", children: ["Geen vastgelopen orders. Alles is ingedeeld of staat nog in de wachtrij", overzicht ? ` (${overzicht.pending} wachtend)` : "", "."] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsxs(Badge, { variant: filter === "" ? "default" : "outline", className: "cursor-pointer", onClick: () => setFilter(""), children: ["Alles (", safeRows.length, ")"] }), codes.map(([code, aantal]) => (_jsxs(Badge, { variant: filter === code ? "default" : "outline", className: "cursor-pointer", onClick: () => setFilter(code), children: [redenLabel(code), " (", aantal, ")"] }, code)))] }), _jsx("div", { className: "divide-y rounded-md border", children: zichtbaar.map((r) => (_jsxs("div", { className: "flex items-start justify-between gap-4 p-3", children: [_jsxs("div", { className: "min-w-0", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("span", { className: "font-medium", children: r.orderRef }), _jsx(Badge, { variant: "secondary", children: redenLabel(r.reasonCode) }), _jsx(Badge, { variant: "outline", children: "Wacht op actie" }), _jsxs("span", { className: "text-xs text-muted-foreground", children: ["Leverdatum ", datum(r.deliveryDate), " \u00B7 sinds ", datum(r.blockedSince)] })] }), r.reason && (_jsx("p", { className: "mt-1 line-clamp-2 text-sm text-muted-foreground", children: r.reason }))] }), _jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [r.reasonCode === "belading_onwaarschijnlijk" && (_jsx(Button, { size: "sm", disabled: busy, onClick: () => void bevestigen(r.orderId, r.orderRef), children: "Belading bevestigen" })), r.reasonCode === "dubbele_regels" && (_jsx(Button, { variant: "secondary", size: "sm", disabled: busy, onClick: () => void opruimen(r.orderId, r.orderRef), children: "Dubbele regels opruimen" })), _jsx(Button, { variant: "outline", size: "sm", disabled: busy, onClick: () => void opnieuw([r.orderId]), children: "Opnieuw" })] })] }, r.queueId))) })] })), _jsx(BeladingWaarschuwingen, {})] })] }));
72
+ }
73
+ /** 1.0.190: beladingen die wel zijn ingedeeld maar opvallen (alleen waarschuwing). */
74
+ function BeladingWaarschuwingen() {
75
+ const [rijen, setRijen] = useState([]);
76
+ useEffect(() => {
77
+ let actief = true;
78
+ void (async () => {
79
+ const { data } = await supabase
80
+ .from("logistics_belading_waarschuwingen")
81
+ .select("order_id, tekst, updated_at")
82
+ .order("updated_at", { ascending: false })
83
+ .limit(50);
84
+ const lijst = Array.isArray(data) ? data : [];
85
+ const ids = Array.from(new Set(lijst.map((r) => String(r.order_id))));
86
+ const nummers = new Map();
87
+ if (ids.length > 0) {
88
+ const { data: o } = await supabase.from("orders").select("id, order_number").in("id", ids);
89
+ for (const r of Array.isArray(o) ? o : [])
90
+ nummers.set(String(r.id), String(r.order_number ?? ""));
91
+ }
92
+ if (actief)
93
+ setRijen(lijst.map((r) => ({ ...r, order_number: nummers.get(String(r.order_id)) })));
94
+ })();
95
+ return () => {
96
+ actief = false;
97
+ };
98
+ }, []);
99
+ if (rijen.length === 0)
100
+ return null;
101
+ return (_jsxs("div", { className: "mt-4 space-y-1 rounded-lg border border-dashed p-3 text-sm", children: [_jsx("p", { className: "font-medium", children: "Waarschuwingen belading (wel ingedeeld)" }), _jsx("ul", { className: "space-y-1 text-muted-foreground", children: rijen.map((r, i) => (_jsxs("li", { children: [_jsx("span", { className: "font-medium text-foreground", children: r.order_number || "Order" }), ": ", r.tekst] }, `${r.order_id}-${i}`))) })] }));
63
102
  }
@@ -1 +1 @@
1
- {"version":3,"file":"LadingdragersPage.d.ts","sourceRoot":"","sources":["../../src/components/LadingdragersPage.tsx"],"names":[],"mappings":"AA+HA,wBAAgB,iBAAiB,gCAwUhC"}
1
+ {"version":3,"file":"LadingdragersPage.d.ts","sourceRoot":"","sources":["../../src/components/LadingdragersPage.tsx"],"names":[],"mappings":"AAiIA,wBAAgB,iBAAiB,gCAyUhC"}
@@ -7,6 +7,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
7
7
  // GEEN <Header /> hier — die levert de shell.
8
8
  // ============================================================================
9
9
  import { useState } from "react";
10
+ import { useFloricodeLadingdragers } from "../hooks/useFloricodeLadingdragers.js";
11
+ import { ladingdragerNaam } from "../lib/loadCarrierTypes.js";
10
12
  import { Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, Input, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Skeleton, Switch, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@flowselections/core";
11
13
  import { Container, Pencil, Plus, RefreshCw, Trash2 } from "lucide-react";
12
14
  import { Link } from "@tanstack/react-router";
@@ -74,6 +76,7 @@ function toPayload(form) {
74
76
  }
75
77
  export function LadingdragersPage() {
76
78
  const { items, loading, saving, reload, create, update, remove } = useLadingdragers();
79
+ const floricodeNamen = useFloricodeLadingdragers();
77
80
  const [open, setOpen] = useState(false);
78
81
  const [bewerkId, setBewerkId] = useState(null);
79
82
  const [form, setForm] = useState(LEEG);
@@ -98,5 +101,5 @@ export function LadingdragersPage() {
98
101
  };
99
102
  const set = (key, value) => setForm((prev) => ({ ...prev, [key]: value }));
100
103
  const toonLagen = form.capacity_model === "layered" || form.capacity_model === "stacked";
101
- 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(Container, { className: "h-6 w-6 text-primary" }), "Ladingdragers"] }), _jsx("p", { className: "mt-1 max-w-2xl text-sm text-muted-foreground", children: "Hier staat hoe groot elke ladingdrager is. De Beladingindeling gebruikt deze maten om te bepalen hoeveel fusten er per laag en per drager passen. In de Rule Engine bepaal je per strategie welke ladingdragers gebruikt mogen worden." })] }), _jsxs("div", { className: "flex gap-2", children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: reload, disabled: loading, children: [_jsx(RefreshCw, { className: "mr-2 h-4 w-4" }), "Vernieuwen"] }), _jsxs(Button, { size: "sm", onClick: openNieuw, children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), "Ladingdrager"] }), _jsx(Button, { asChild: true, variant: "ghost", size: "sm", children: _jsx(Link, { to: "/logistiek-engine", children: "Terug" }) })] })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsx(CardTitle, { children: "Capaciteit per ladingdrager" }), _jsx(CardDescription, { children: "Lagen bepalen de hoogte: een product met vier lagen per drager neemt per laag 25% van de hoogte in." })] }), _jsx(CardContent, { children: loading ? (_jsxs("div", { className: "space-y-2", children: [_jsx(Skeleton, { className: "h-10 w-full" }), _jsx(Skeleton, { className: "h-10 w-full" }), _jsx(Skeleton, { className: "h-10 w-full" })] })) : safeItems.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Nog geen ladingdragers ingesteld." })) : (_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Ladingdrager" }), _jsx(TableHead, { children: "Model" }), _jsx(TableHead, { className: "text-right", children: "Lagen" }), _jsx(TableHead, { className: "text-right", children: "Laagmaat" }), _jsx(TableHead, { className: "text-right", children: "Max dozen" }), _jsx(TableHead, { className: "text-right", children: "Max kg" }), _jsx(TableHead, { children: "Mengen" }), _jsx(TableHead, { className: "text-right", children: "Acties" })] }) }), _jsx(TableBody, { children: safeItems.map((row) => (_jsxs(TableRow, { className: row.is_active ? "" : "opacity-50", children: [_jsxs(TableCell, { children: [_jsx("div", { className: "font-medium", children: row.name }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-xs text-muted-foreground", children: row.code }), row.is_default ? _jsx(Badge, { variant: "secondary", children: "Standaard" }) : null, !row.is_active ? _jsx(Badge, { variant: "outline", children: "Uit" }) : null] })] }), _jsx(TableCell, { className: "text-sm", children: CAPACITY_MODEL_LABELS[row.capacity_model] }), _jsx(TableCell, { className: "text-right", children: row.layers_per_carrier }), _jsxs(TableCell, { className: "text-right text-sm text-muted-foreground", children: [row.layer_length_cm, " \u00D7 ", row.layer_width_cm, " cm"] }), _jsx(TableCell, { className: "text-right", children: row.max_boxes ?? "—" }), _jsx(TableCell, { className: "text-right", children: row.max_weight_kg ?? "—" }), _jsx(TableCell, { children: _jsx(Badge, { variant: row.allow_mixing === false ? "outline" : "secondary", children: row.allow_mixing === false ? "Nooit mengen" : "Mengen mag" }) }), _jsx(TableCell, { className: "text-right", children: _jsxs("div", { className: "flex justify-end gap-1", children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: () => openBewerk(row), children: _jsx(Pencil, { className: "h-4 w-4" }) }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => remove(row.id), children: _jsx(Trash2, { className: "h-4 w-4" }) })] }) })] }, row.id))) })] })) })] }), _jsx(Dialog, { open: open, onOpenChange: setOpen, children: _jsxs(DialogContent, { className: "max-h-[85vh] overflow-y-auto sm:max-w-lg", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: bewerkId ? "Ladingdrager bewerken" : "Nieuwe ladingdrager" }), _jsx(DialogDescription, { children: "Deze maten bepalen hoeveel er per laag en per drager past." })] }), _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "code", children: "Code" }), _jsx(Input, { id: "code", value: form.code, onChange: (e) => set("code", e.target.value), placeholder: "pallet", disabled: Boolean(bewerkId) })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "name", children: "Naam" }), _jsx(Input, { id: "name", value: form.name, onChange: (e) => set("name", e.target.value) })] })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { children: "Capaciteitsmodel" }), _jsxs(Select, { value: form.capacity_model, onValueChange: (v) => set("capacity_model", v), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: Object.entries(CAPACITY_MODEL_LABELS).map(([value, label]) => (_jsx(SelectItem, { value: value, children: label }, value))) })] })] }), toonLagen ? (_jsxs("div", { className: "grid grid-cols-3 gap-3", children: [_jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "lagen", children: "Lagen" }), _jsx(Input, { id: "lagen", type: "number", min: 1, value: form.layers_per_carrier, onChange: (e) => set("layers_per_carrier", Number(e.target.value)) })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "lengte", children: "Lengte (cm)" }), _jsx(Input, { id: "lengte", type: "number", min: 0, value: form.layer_length_cm, onChange: (e) => set("layer_length_cm", Number(e.target.value)) })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "breedte", children: "Breedte (cm)" }), _jsx(Input, { id: "breedte", type: "number", min: 0, value: form.layer_width_cm, onChange: (e) => set("layer_width_cm", Number(e.target.value)) })] })] })) : null, _jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "maxfust", children: "Max fusten per laag" }), _jsx(Input, { id: "maxfust", type: "number", min: 1, value: form.max_crates_per_layer, onChange: (e) => set("max_crates_per_layer", e.target.value), placeholder: "Geen grens" })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "maxdozen", children: "Max dozen / fusten totaal" }), _jsx(Input, { id: "maxdozen", type: "number", min: 1, value: form.max_boxes, onChange: (e) => set("max_boxes", e.target.value), placeholder: "Geen grens" })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "maxkg", children: "Max gewicht (kg)" }), _jsx(Input, { id: "maxkg", type: "number", min: 0, value: form.max_weight_kg, onChange: (e) => set("max_weight_kg", e.target.value), placeholder: "Geen grens" })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "maxhoogte", children: "Max hoogte (cm)" }), _jsx(Input, { id: "maxhoogte", type: "number", min: 0, value: form.max_height_cm, onChange: (e) => set("max_height_cm", e.target.value), placeholder: "Geen grens" })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "maxorders", children: "Max orders" }), _jsx(Input, { id: "maxorders", type: "number", min: 1, value: form.max_orders, onChange: (e) => set("max_orders", e.target.value), placeholder: "Geen grens" })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "maxklanten", children: "Max klanten" }), _jsx(Input, { id: "maxklanten", type: "number", min: 1, value: form.max_customers, onChange: (e) => set("max_customers", e.target.value), placeholder: "Geen grens" })] })] }), _jsxs("div", { className: "space-y-3 rounded-lg border p-3", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx(Label, { htmlFor: "mengen", children: "Mengen van partijen toestaan" }), _jsx(Switch, { id: "mengen", checked: form.allow_mixing, onCheckedChange: (v) => set("allow_mixing", v) })] }), _jsxs("div", { className: "flex items-center justify-between", children: [_jsx(Label, { htmlFor: "actief", children: "Actief" }), _jsx(Switch, { id: "actief", checked: form.is_active, onCheckedChange: (v) => set("is_active", v) })] }), _jsxs("div", { className: "flex items-center justify-between", children: [_jsx(Label, { htmlFor: "standaard", children: "Standaard ladingdrager" }), _jsx(Switch, { id: "standaard", checked: form.is_default, onCheckedChange: (v) => set("is_default", v) })] }), _jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsx(Label, { htmlFor: "volgorde", children: "Volgorde" }), _jsx(Input, { id: "volgorde", type: "number", className: "w-24", value: form.sort_order, onChange: (e) => set("sort_order", Number(e.target.value)) })] })] })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { variant: "outline", onClick: () => setOpen(false), children: "Annuleren" }), _jsx(Button, { onClick: opslaan, disabled: saving || !form.code || !form.name, children: "Opslaan" })] })] }) })] }));
104
+ 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(Container, { className: "h-6 w-6 text-primary" }), "Ladingdragers"] }), _jsx("p", { className: "mt-1 max-w-2xl text-sm text-muted-foreground", children: "Hier staat hoe groot elke ladingdrager is. De Beladingindeling gebruikt deze maten om te bepalen hoeveel fusten er per laag en per drager passen. In de Rule Engine bepaal je per strategie welke ladingdragers gebruikt mogen worden." })] }), _jsxs("div", { className: "flex gap-2", children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: reload, disabled: loading, children: [_jsx(RefreshCw, { className: "mr-2 h-4 w-4" }), "Vernieuwen"] }), _jsxs(Button, { size: "sm", onClick: openNieuw, children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), "Ladingdrager"] }), _jsx(Button, { asChild: true, variant: "ghost", size: "sm", children: _jsx(Link, { to: "/logistiek-engine", children: "Terug" }) })] })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsx(CardTitle, { children: "Capaciteit per ladingdrager" }), _jsx(CardDescription, { children: "Lagen bepalen de hoogte: een product met vier lagen per drager neemt per laag 25% van de hoogte in." })] }), _jsx(CardContent, { children: loading ? (_jsxs("div", { className: "space-y-2", children: [_jsx(Skeleton, { className: "h-10 w-full" }), _jsx(Skeleton, { className: "h-10 w-full" }), _jsx(Skeleton, { className: "h-10 w-full" })] })) : safeItems.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Nog geen ladingdragers ingesteld." })) : (_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Ladingdrager" }), _jsx(TableHead, { children: "Model" }), _jsx(TableHead, { className: "text-right", children: "Lagen" }), _jsx(TableHead, { className: "text-right", children: "Laagmaat" }), _jsx(TableHead, { className: "text-right", children: "Max dozen" }), _jsx(TableHead, { className: "text-right", children: "Max kg" }), _jsx(TableHead, { children: "Mengen" }), _jsx(TableHead, { className: "text-right", children: "Acties" })] }) }), _jsx(TableBody, { children: safeItems.map((row) => (_jsxs(TableRow, { className: row.is_active ? "" : "opacity-50", children: [_jsxs(TableCell, { children: [_jsx("div", { className: "font-medium", children: ladingdragerNaam(row.code, floricodeNamen, row.name) }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-xs text-muted-foreground", children: row.code }), row.is_default ? _jsx(Badge, { variant: "secondary", children: "Standaard" }) : null, !row.is_active ? _jsx(Badge, { variant: "outline", children: "Uit" }) : null] })] }), _jsx(TableCell, { className: "text-sm", children: CAPACITY_MODEL_LABELS[row.capacity_model] }), _jsx(TableCell, { className: "text-right", children: row.layers_per_carrier }), _jsxs(TableCell, { className: "text-right text-sm text-muted-foreground", children: [row.layer_length_cm, " \u00D7 ", row.layer_width_cm, " cm"] }), _jsx(TableCell, { className: "text-right", children: row.max_boxes ?? "—" }), _jsx(TableCell, { className: "text-right", children: row.max_weight_kg ?? "—" }), _jsx(TableCell, { children: _jsx(Badge, { variant: row.allow_mixing === false ? "outline" : "secondary", children: row.allow_mixing === false ? "Nooit mengen" : "Mengen mag" }) }), _jsx(TableCell, { className: "text-right", children: _jsxs("div", { className: "flex justify-end gap-1", children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: () => openBewerk(row), children: _jsx(Pencil, { className: "h-4 w-4" }) }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => remove(row.id), children: _jsx(Trash2, { className: "h-4 w-4" }) })] }) })] }, row.id))) })] })) })] }), _jsx(Dialog, { open: open, onOpenChange: setOpen, children: _jsxs(DialogContent, { className: "max-h-[85vh] overflow-y-auto sm:max-w-lg", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: bewerkId ? "Ladingdrager bewerken" : "Nieuwe ladingdrager" }), _jsx(DialogDescription, { children: "Deze maten bepalen hoeveel er per laag en per drager past." })] }), _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "code", children: "Code" }), _jsx(Input, { id: "code", value: form.code, onChange: (e) => set("code", e.target.value), placeholder: "pallet", disabled: Boolean(bewerkId) })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "name", children: "Naam" }), _jsx(Input, { id: "name", value: form.name, onChange: (e) => set("name", e.target.value) })] })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { children: "Capaciteitsmodel" }), _jsxs(Select, { value: form.capacity_model, onValueChange: (v) => set("capacity_model", v), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: Object.entries(CAPACITY_MODEL_LABELS).map(([value, label]) => (_jsx(SelectItem, { value: value, children: label }, value))) })] })] }), toonLagen ? (_jsxs("div", { className: "grid grid-cols-3 gap-3", children: [_jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "lagen", children: "Lagen" }), _jsx(Input, { id: "lagen", type: "number", min: 1, value: form.layers_per_carrier, onChange: (e) => set("layers_per_carrier", Number(e.target.value)) })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "lengte", children: "Lengte (cm)" }), _jsx(Input, { id: "lengte", type: "number", min: 0, value: form.layer_length_cm, onChange: (e) => set("layer_length_cm", Number(e.target.value)) })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "breedte", children: "Breedte (cm)" }), _jsx(Input, { id: "breedte", type: "number", min: 0, value: form.layer_width_cm, onChange: (e) => set("layer_width_cm", Number(e.target.value)) })] })] })) : null, _jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "maxfust", children: "Max fusten per laag" }), _jsx(Input, { id: "maxfust", type: "number", min: 1, value: form.max_crates_per_layer, onChange: (e) => set("max_crates_per_layer", e.target.value), placeholder: "Geen grens" })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "maxdozen", children: "Max dozen / fusten totaal" }), _jsx(Input, { id: "maxdozen", type: "number", min: 1, value: form.max_boxes, onChange: (e) => set("max_boxes", e.target.value), placeholder: "Geen grens" })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "maxkg", children: "Max gewicht (kg)" }), _jsx(Input, { id: "maxkg", type: "number", min: 0, value: form.max_weight_kg, onChange: (e) => set("max_weight_kg", e.target.value), placeholder: "Geen grens" })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "maxhoogte", children: "Max hoogte (cm)" }), _jsx(Input, { id: "maxhoogte", type: "number", min: 0, value: form.max_height_cm, onChange: (e) => set("max_height_cm", e.target.value), placeholder: "Geen grens" })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "maxorders", children: "Max orders" }), _jsx(Input, { id: "maxorders", type: "number", min: 1, value: form.max_orders, onChange: (e) => set("max_orders", e.target.value), placeholder: "Geen grens" })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "maxklanten", children: "Max klanten" }), _jsx(Input, { id: "maxklanten", type: "number", min: 1, value: form.max_customers, onChange: (e) => set("max_customers", e.target.value), placeholder: "Geen grens" })] })] }), _jsxs("div", { className: "space-y-3 rounded-lg border p-3", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx(Label, { htmlFor: "mengen", children: "Mengen van partijen toestaan" }), _jsx(Switch, { id: "mengen", checked: form.allow_mixing, onCheckedChange: (v) => set("allow_mixing", v) })] }), _jsxs("div", { className: "flex items-center justify-between", children: [_jsx(Label, { htmlFor: "actief", children: "Actief" }), _jsx(Switch, { id: "actief", checked: form.is_active, onCheckedChange: (v) => set("is_active", v) })] }), _jsxs("div", { className: "flex items-center justify-between", children: [_jsx(Label, { htmlFor: "standaard", children: "Standaard ladingdrager" }), _jsx(Switch, { id: "standaard", checked: form.is_default, onCheckedChange: (v) => set("is_default", v) })] }), _jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsx(Label, { htmlFor: "volgorde", children: "Volgorde" }), _jsx(Input, { id: "volgorde", type: "number", className: "w-24", value: form.sort_order, onChange: (e) => set("sort_order", Number(e.target.value)) })] })] })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { variant: "outline", onClick: () => setOpen(false), children: "Annuleren" }), _jsx(Button, { onClick: opslaan, disabled: saving || !form.code || !form.name, children: "Opslaan" })] })] }) })] }));
102
105
  }
@@ -0,0 +1,2 @@
1
+ export declare function OrdersZonderKarCard(): import("react").JSX.Element;
2
+ //# sourceMappingURL=OrdersZonderKarCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OrdersZonderKarCard.d.ts","sourceRoot":"","sources":["../../src/components/OrdersZonderKarCard.tsx"],"names":[],"mappings":"AAQA,wBAAgB,mBAAmB,gCAoDlC"}
@@ -0,0 +1,36 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // 1.0.190: open orders die een kar horen te hebben maar er geen hebben.
3
+ // GEEN <Header /> hier — die levert de shell.
4
+ import { useEffect, useState } from "react";
5
+ import { Badge, Card, CardContent, CardDescription, CardHeader, CardTitle, supabase } from "@flowselections/core";
6
+ export function OrdersZonderKarCard() {
7
+ const [rijen, setRijen] = useState([]);
8
+ const [pogingen, setPogingen] = useState(new Map());
9
+ const [fout, setFout] = useState(null);
10
+ useEffect(() => {
11
+ let actief = true;
12
+ void (async () => {
13
+ const { data, error } = await supabase.rpc("logistics_open_orders_zonder_kar");
14
+ const { data: p } = await supabase
15
+ .from("logistics_zonder_kar_pogingen")
16
+ .select("order_id, pogingen, gemeld_at")
17
+ .is("opgelost_at", null);
18
+ if (!actief)
19
+ return;
20
+ if (error)
21
+ setFout(String(error.message));
22
+ setRijen(Array.isArray(data) ? data : []);
23
+ const m = new Map();
24
+ for (const r of Array.isArray(p) ? p : [])
25
+ m.set(String(r.order_id), r);
26
+ setPogingen(m);
27
+ })();
28
+ return () => {
29
+ actief = false;
30
+ };
31
+ }, []);
32
+ return (_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsx(CardTitle, { children: "Open orders zonder kar" }), _jsx(CardDescription, { children: "Orders die een kar horen te hebben. De Engine probeert het hoogstens 3 keer opnieuw; daarna staat de order hier als melding." })] }), _jsxs(CardContent, { className: "space-y-2 text-sm", children: [fout ? _jsx("p", { className: "text-destructive", children: fout }) : null, rijen.length === 0 && !fout ? _jsx("p", { className: "text-muted-foreground", children: "Geen open orders zonder kar." }) : null, rijen.map((r) => {
33
+ const p = pogingen.get(String(r.order_id));
34
+ return (_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("span", { className: "font-medium", children: r.order_number ?? "Order" }), _jsxs("span", { className: "text-muted-foreground", children: ["levering ", r.delivery_date ?? "—"] }), p?.gemeld_at ? (_jsx(Badge, { variant: "destructive", children: "3 pogingen mislukt" })) : (_jsxs(Badge, { variant: "secondary", children: ["poging ", p?.pogingen ?? 0, " van 3"] })), r.laatste_reden ? _jsx("span", { className: "text-muted-foreground", children: r.laatste_reden }) : null] }, r.order_id));
35
+ })] })] }));
36
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"RegiePage.d.ts","sourceRoot":"","sources":["../../src/components/RegiePage.tsx"],"names":[],"mappings":"AAiCA,wBAAgB,SAAS,gCA6FxB"}
1
+ {"version":3,"file":"RegiePage.d.ts","sourceRoot":"","sources":["../../src/components/RegiePage.tsx"],"names":[],"mappings":"AAkCA,wBAAgB,SAAS,gCA8FxB"}
@@ -13,6 +13,7 @@ import { Link } from "@tanstack/react-router";
13
13
  import { ConsistentieCard } from "./ConsistentieCard.js";
14
14
  import { KarplanStatusCard } from "./KarplanStatusCard.js";
15
15
  import { KardekkingCard } from "./KardekkingCard.js";
16
+ import { OrdersZonderKarCard } from "./OrdersZonderKarCard.js";
16
17
  import { MisluktVerzendbrievenCard } from "./MisluktVerzendbrievenCard.js";
17
18
  import { PlannerDiffCard } from "./PlannerDiffCard.js";
18
19
  import { BeladingsafwijkingenCard } from "./BeladingsafwijkingenCard.js";
@@ -36,5 +37,5 @@ export function RegiePage() {
36
37
  useEffect(() => {
37
38
  void laden();
38
39
  }, [laden]);
39
- 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(MinuutrondeCard, {}), _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, {}) }), _jsxs(InklapbaarVenster, { titel: "Kardekking", omschrijving: "Staat elke bestelregel correct op een kar?", children: [_jsx(KardekkingCard, {}), _jsx(MisluktVerzendbrievenCard, {})] }), _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))) })] })] }));
40
+ 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(MinuutrondeCard, {}), _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, {}) }), _jsxs(InklapbaarVenster, { titel: "Kardekking", omschrijving: "Staat elke bestelregel correct op een kar?", children: [_jsx(KardekkingCard, {}), _jsx(OrdersZonderKarCard, {}), _jsx(MisluktVerzendbrievenCard, {})] }), _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))) })] })] }));
40
41
  }
@@ -85,7 +85,7 @@ function RuleEnginePageInhoud({ strategyId: strategyIdProp = null, strategyName:
85
85
  setCreateOpen(false);
86
86
  setStepIndex(0);
87
87
  };
88
- const { job, starting, startRecalculation } = useRecalcJob();
88
+ const { job, totaal: recalcTotaal, starting, startRecalculation } = useRecalcJob();
89
89
  const running = job?.status === "running";
90
90
  const busy = starting || running || recalculating;
91
91
  const handleSaveAndRecalculate = async () => {
@@ -137,11 +137,11 @@ function RuleEnginePageInhoud({ strategyId: strategyIdProp = null, strategyName:
137
137
  };
138
138
  }, [job?.status]);
139
139
  return (_jsxs("div", { className: "space-y-4 p-6", children: [_jsxs("header", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsx("h1", { className: "text-xl font-semibold tracking-tight", children: strategyName ? `Werkwijze: ${strategyName}` : "Werkwijze instellen" }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: () => setCreateOpen(true), children: [_jsx(Plus, { className: "h-4 w-4" }), "Nieuw"] }), _jsxs(Button, { size: "sm", disabled: !activeConfig || saving || busy, onClick: handleSaveAndRecalculate, children: [busy ? _jsx(RefreshCw, { className: "h-4 w-4 animate-spin" }) : _jsx(Save, { className: "h-4 w-4" }), running
140
- ? `${phaseLabel(job?.phase)}… (${job?.processed ?? 0})`
140
+ ? `${phaseLabel(job?.phase)}… (${voortgangTekst(job?.processed, recalcTotaal)})`
141
141
  : busy
142
142
  ? "Bezig…"
143
143
  : "Opslaan & opnieuw berekenen"] })] })] }), error && (_jsx("p", { className: "rounded-md border border-destructive/50 bg-destructive/5 px-3 py-2 text-sm text-destructive", children: "De gegevens konden niet worden geladen. Probeer het opnieuw." })), showJobLine && (_jsxs("div", { className: `rounded-md border px-3 py-2 text-sm ${job?.status === "error" ? "border-destructive/50 bg-destructive/5 text-destructive" : "border-border"}`, children: [_jsx("span", { children: running
144
- ? `${phaseLabel(job?.phase)} — ${job?.processed ?? 0} verwerkt`
144
+ ? `${phaseLabel(job?.phase)} — ${voortgangTekst(job?.processed, recalcTotaal)} opnieuw ingedeeld`
145
145
  : `De laatste volledige herberekening${job?.finished_at
146
146
  ? ` (${new Date(job.finished_at).toLocaleString("nl-NL", { dateStyle: "medium", timeStyle: "short" })})`
147
147
  : ""} is niet volledig gelukt. Deze meldingen horen bij die berekening; opnieuw berekenen ververst ze.` }), jobProblems.length > 0 && (_jsx("ul", { className: "mt-1 list-disc pl-5 text-xs", children: jobProblems.map((p, i) => (_jsx("li", { children: p }, i))) }))] })), !showJobLine && laatsteRonde && (_jsx("p", { className: "rounded-md border border-border px-3 py-2 text-sm text-muted-foreground", children: laatsteRonde.at
@@ -164,3 +164,8 @@ function RuleEnginePageInhoud({ strategyId: strategyIdProp = null, strategyName:
164
164
  export function RuleEnginePage(props) {
165
165
  return (_jsx(RolPoort, { rollen: ENGINE_TOEGANG.ruleEngine, children: _jsx(RuleEnginePageInhoud, { ...props }) }));
166
166
  }
167
+ /** 1.0.190 (B): "x van y" zolang het totaal bekend is, anders alleen x. */
168
+ function voortgangTekst(verwerkt, totaal) {
169
+ const x = Number(verwerkt ?? 0);
170
+ return totaal && totaal > 0 ? `${Math.min(x, totaal)} van ${totaal}` : String(x);
171
+ }
@@ -0,0 +1,2 @@
1
+ export declare function useFloricodeLadingdragers(): Map<string, string> | null;
2
+ //# sourceMappingURL=useFloricodeLadingdragers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useFloricodeLadingdragers.d.ts","sourceRoot":"","sources":["../../src/hooks/useFloricodeLadingdragers.ts"],"names":[],"mappings":"AAIA,wBAAgB,yBAAyB,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAmBtE"}
@@ -0,0 +1,25 @@
1
+ // Ladingdragernamen uit Floriday-authenticatie (floricode_load_carriers). Match op code.
2
+ import { useEffect, useState } from "react";
3
+ import { supabase } from "@flowselections/core";
4
+ export function useFloricodeLadingdragers() {
5
+ const [namen, setNamen] = useState(null);
6
+ useEffect(() => {
7
+ let actief = true;
8
+ supabase
9
+ .rpc("floricode_load_carriers")
10
+ .then(({ data, error }) => {
11
+ if (!actief || error)
12
+ return;
13
+ const m = new Map();
14
+ for (const r of Array.isArray(data) ? data : []) {
15
+ if (r?.code && r?.name && !m.has(r.code))
16
+ m.set(r.code, r.name);
17
+ }
18
+ setNamen(m);
19
+ });
20
+ return () => {
21
+ actief = false;
22
+ };
23
+ }, []);
24
+ return namen;
25
+ }
@@ -31,6 +31,7 @@ export declare function useGeblokkeerdeOrders(): {
31
31
  na: number;
32
32
  meerTeDoen: boolean;
33
33
  }>;
34
+ beladingBevestigen: (orderId: string) => Promise<number>;
34
35
  };
35
36
  export interface GearchiveerdeOrder {
36
37
  queueId: string;
@@ -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,CAe9C,CAAC;AAEF,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,UAGzD;AAED,wBAAgB,qBAAqB;;;;;;;sBAqDhB,MAAM,EAAE;gCAwBT,MAAM;;;;;;;EAyBzB;AAED,MAAM,WAAW,kBAAkB;IACjC,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,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,kFAAkF;AAClF,wBAAgB,sBAAsB;;;;;;iCAsCjB,MAAM,EAAE;EAmB5B"}
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,CAkB9C,CAAC;AAEF,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,UAGzD;AAED,wBAAgB,qBAAqB;;;;;;;sBAqDhB,MAAM,EAAE;gCAwBT,MAAM;;;;;;;kCA8BN,MAAM;EA4BzB;AAED,MAAM,WAAW,kBAAkB;IACjC,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,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,kFAAkF;AAClF,wBAAgB,sBAAsB;;;;;;iCAsCjB,MAAM,EAAE;EAmB5B"}
@@ -16,6 +16,9 @@ export const REDEN_LABEL = {
16
16
  verificatie_mislukt: "Controle mislukt",
17
17
  bufferdekking_onvolledig: "Buffer niet volledig gedekt",
18
18
  dubbele_regels: "Dubbele bestelregels",
19
+ belading_onwaarschijnlijk: "Wacht op bevestiging belading",
20
+ artikel_ontbreekt: "Kies een artikel",
21
+ ladingdrager_onbekend: "Ladingdrager onbekend",
19
22
  karindeling_geblokkeerd: "Karindeling geblokkeerd",
20
23
  indeling_te_lang: "Indeling duurde te lang",
21
24
  other: "Overige reden",
@@ -117,7 +120,41 @@ export function useGeblokkeerdeOrders() {
117
120
  setBusy(false);
118
121
  }
119
122
  }, [load]);
120
- return { rows, overzicht, loading, error, busy, refresh: load, retry, dubbelenOpruimen };
123
+ /**
124
+ * 1.0.190: kantoor bevestigt een onwaarschijnlijke belading. Per verpakking
125
+ * via de bestaande logistics_belading_bevestig (legt wie/wanneer vast en
126
+ * zet de order opnieuw in de planning).
127
+ */
128
+ const beladingBevestigen = useCallback(async (orderId) => {
129
+ setBusy(true);
130
+ try {
131
+ const { data, error: wachtError } = await supabase.rpc("logistics_belading_wacht", {
132
+ p_order_id: orderId,
133
+ });
134
+ if (wachtError)
135
+ throw new Error(wachtError.message);
136
+ const items = Array.isArray(data) ? data : [];
137
+ if (items.length === 0)
138
+ throw new Error("Er staat voor deze order niets meer klaar om te bevestigen.");
139
+ for (const it of items) {
140
+ if (!it?.sleutel)
141
+ continue;
142
+ const { error: bevError } = await supabase.rpc("logistics_belading_bevestig", {
143
+ p_order_id: orderId,
144
+ p_sleutel: it.sleutel,
145
+ p_redenen: Array.isArray(it.redenen) ? it.redenen : [],
146
+ });
147
+ if (bevError)
148
+ throw new Error(bevError.message);
149
+ }
150
+ await load();
151
+ return items.length;
152
+ }
153
+ finally {
154
+ setBusy(false);
155
+ }
156
+ }, [load]);
157
+ return { rows, overzicht, loading, error, busy, refresh: load, retry, dubbelenOpruimen, beladingBevestigen };
121
158
  }
122
159
  /** Lijst "Niet geleverd en gearchiveerd" en "Opnieuw aanbieden" (op order-id). */
123
160
  export function useGearchiveerdeOrders() {
@@ -17,6 +17,7 @@ export interface RecalcJobRow {
17
17
  export declare function phaseLabel(phase?: string | null): string;
18
18
  export declare function useRecalcJob(): {
19
19
  job: RecalcJobRow | null;
20
+ totaal: number | null;
20
21
  starting: boolean;
21
22
  startRecalculation: (strategyId: string | null) => Promise<void>;
22
23
  refresh: () => Promise<RecalcJobRow | null>;
@@ -1 +1 @@
1
- {"version":3,"file":"useRecalcJob.d.ts","sourceRoot":"","sources":["../../src/hooks/useRecalcJob.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;IACrC,KAAK,EAAE,SAAS,GAAG,QAAQ,GAAG,UAAU,CAAC;IACzC,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5D,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;CACpB;AAQD,wBAAgB,UAAU,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,UAE/C;AAwBD,wBAAgB,YAAY;;;qCA6DL,MAAM,GAAG,IAAI;;EAenC"}
1
+ {"version":3,"file":"useRecalcJob.d.ts","sourceRoot":"","sources":["../../src/hooks/useRecalcJob.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;IACrC,KAAK,EAAE,SAAS,GAAG,QAAQ,GAAG,UAAU,CAAC;IACzC,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5D,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;CACpB;AAQD,wBAAgB,UAAU,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,UAE/C;AAwBD,wBAAgB,YAAY;;;;qCAuEL,MAAM,GAAG,IAAI;;EAenC"}
@@ -40,6 +40,8 @@ export function useRecalcJob() {
40
40
  const start = useServerFn(startRecalcJob);
41
41
  const tick = useServerFn(tickRecalcJob);
42
42
  const ticking = useRef(false);
43
+ // 1.0.190 (B): totaal aantal in te delen buffers + losse orders, voor "x van y".
44
+ const [totaal, setTotaal] = useState(null);
43
45
  const load = useCallback(async () => {
44
46
  const { data } = await supabase
45
47
  .from("logistics_recalc_jobs")
@@ -48,6 +50,14 @@ export function useRecalcJob() {
48
50
  .limit(1);
49
51
  const row = Array.isArray(data) && data.length > 0 ? data[0] : null;
50
52
  setJob(row);
53
+ if (row?.status === "running") {
54
+ const [b, o] = await Promise.all([
55
+ supabase.from("order_buffers").select("id", { count: "exact", head: true }),
56
+ supabase.from("orders").select("id", { count: "exact", head: true }).is("buffer_id", null).is("deleted_at", null),
57
+ ]);
58
+ const n = Number(b.count ?? NaN) + Number(o.count ?? NaN);
59
+ setTotaal(Number.isFinite(n) ? n : null);
60
+ }
51
61
  return row;
52
62
  }, []);
53
63
  useEffect(() => {
@@ -104,5 +114,5 @@ export function useRecalcJob() {
104
114
  setStarting(false);
105
115
  }
106
116
  }, [start, load]);
107
- return { job, starting, startRecalculation, refresh: load };
117
+ return { job, totaal, starting, startRecalculation, refresh: load };
108
118
  }