@flowselections/logistiek-engine 1.0.34 → 1.0.53

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 (160) hide show
  1. package/README.md +24 -24
  2. package/dist-lib/components/FlowCanvas.d.ts +28 -0
  3. package/dist-lib/components/FlowCanvas.d.ts.map +1 -0
  4. package/dist-lib/components/FlowCanvas.js +429 -0
  5. package/dist-lib/components/FlowOrders.d.ts +8 -0
  6. package/dist-lib/components/FlowOrders.d.ts.map +1 -0
  7. package/dist-lib/components/FlowOrders.js +125 -0
  8. package/dist-lib/components/FlowPage.d.ts +2 -0
  9. package/dist-lib/components/FlowPage.d.ts.map +1 -0
  10. package/dist-lib/components/FlowPage.js +141 -0
  11. package/dist-lib/components/InklapbaarVenster.d.ts +9 -0
  12. package/dist-lib/components/InklapbaarVenster.d.ts.map +1 -0
  13. package/dist-lib/components/InklapbaarVenster.js +10 -0
  14. package/dist-lib/components/LogistiekEnginePage.d.ts.map +1 -1
  15. package/dist-lib/components/LogistiekEnginePage.js +9 -3
  16. package/dist-lib/components/LogistiekEngineQueuePage.d.ts +2 -0
  17. package/dist-lib/components/LogistiekEngineQueuePage.d.ts.map +1 -0
  18. package/dist-lib/components/LogistiekEngineQueuePage.js +11 -0
  19. package/dist-lib/components/NodeTracePanel.d.ts +11 -0
  20. package/dist-lib/components/NodeTracePanel.d.ts.map +1 -0
  21. package/dist-lib/components/NodeTracePanel.js +40 -0
  22. package/dist-lib/components/OrderJourneyBar.d.ts +9 -0
  23. package/dist-lib/components/OrderJourneyBar.d.ts.map +1 -0
  24. package/dist-lib/components/OrderJourneyBar.js +26 -0
  25. package/dist-lib/components/OrderLogboek.d.ts +10 -0
  26. package/dist-lib/components/OrderLogboek.d.ts.map +1 -0
  27. package/dist-lib/components/OrderLogboek.js +92 -0
  28. package/dist-lib/components/OrderStopBanner.d.ts +5 -0
  29. package/dist-lib/components/OrderStopBanner.d.ts.map +1 -0
  30. package/dist-lib/components/OrderStopBanner.js +22 -0
  31. package/dist-lib/components/OrderTraceDetails.d.ts +22 -0
  32. package/dist-lib/components/OrderTraceDetails.d.ts.map +1 -0
  33. package/dist-lib/components/OrderTraceDetails.js +87 -0
  34. package/dist-lib/components/RegiePage.d.ts.map +1 -1
  35. package/dist-lib/components/RegiePage.js +11 -69
  36. package/dist-lib/components/WerktijdenCard.d.ts +2 -0
  37. package/dist-lib/components/WerktijdenCard.d.ts.map +1 -0
  38. package/dist-lib/components/WerktijdenCard.js +37 -0
  39. package/dist-lib/components/WerkwijzeHerindelingCard.d.ts.map +1 -1
  40. package/dist-lib/components/WerkwijzeHerindelingCard.js +100 -6
  41. package/dist-lib/hooks/useLogisticsTrace.d.ts +78 -0
  42. package/dist-lib/hooks/useLogisticsTrace.d.ts.map +1 -0
  43. package/dist-lib/hooks/useLogisticsTrace.js +152 -0
  44. package/dist-lib/hooks/useOrderNodeTrace.d.ts +11 -0
  45. package/dist-lib/hooks/useOrderNodeTrace.d.ts.map +1 -0
  46. package/dist-lib/hooks/useOrderNodeTrace.js +26 -0
  47. package/dist-lib/hooks/useOrderTrace.d.ts +74 -0
  48. package/dist-lib/hooks/useOrderTrace.d.ts.map +1 -0
  49. package/dist-lib/hooks/useOrderTrace.js +134 -0
  50. package/dist-lib/hooks/useOrderVersies.d.ts +47 -0
  51. package/dist-lib/hooks/useOrderVersies.d.ts.map +1 -0
  52. package/dist-lib/hooks/useOrderVersies.js +180 -0
  53. package/dist-lib/hooks/useRuleEngine.d.ts +1 -0
  54. package/dist-lib/hooks/useRuleEngine.d.ts.map +1 -1
  55. package/dist-lib/hooks/useRuleEngine.js +1 -0
  56. package/dist-lib/hooks/useShippingNoteStatus.d.ts +29 -0
  57. package/dist-lib/hooks/useShippingNoteStatus.d.ts.map +1 -0
  58. package/dist-lib/hooks/useShippingNoteStatus.js +107 -0
  59. package/dist-lib/hooks/useWerktijden.d.ts +33 -0
  60. package/dist-lib/hooks/useWerktijden.d.ts.map +1 -0
  61. package/dist-lib/hooks/useWerktijden.js +88 -0
  62. package/dist-lib/hooks/useWerkwijzeStatus.d.ts +12 -1
  63. package/dist-lib/hooks/useWerkwijzeStatus.d.ts.map +1 -1
  64. package/dist-lib/hooks/useWerkwijzeStatus.js +60 -4
  65. package/dist-lib/index.d.ts +13 -0
  66. package/dist-lib/index.d.ts.map +1 -1
  67. package/dist-lib/index.js +13 -0
  68. package/dist-lib/integrations/supabase/auth-middleware.d.ts +1600 -54
  69. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  70. package/dist-lib/integrations/supabase/client.server.d.ts +1600 -54
  71. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  72. package/dist-lib/integrations/supabase/client.server.js +1 -1
  73. package/dist-lib/integrations/supabase/moduleClient.d.ts +1600 -54
  74. package/dist-lib/integrations/supabase/moduleClient.d.ts.map +1 -1
  75. package/dist-lib/integrations/supabase/types.d.ts +1618 -50
  76. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  77. package/dist-lib/lib/cartPlanRequests.server.d.ts +1 -1
  78. package/dist-lib/lib/cartPlanRequests.server.d.ts.map +1 -1
  79. package/dist-lib/lib/cartPlanRequests.server.js +49 -8
  80. package/dist-lib/lib/floridayDocuments.functions.d.ts +4644 -6
  81. package/dist-lib/lib/floridayDocuments.functions.d.ts.map +1 -1
  82. package/dist-lib/lib/floridayDocuments.functions.js +1 -1
  83. package/dist-lib/lib/floridayDocuments.server.d.ts +14 -0
  84. package/dist-lib/lib/floridayDocuments.server.d.ts.map +1 -1
  85. package/dist-lib/lib/floridayDocuments.server.js +58 -0
  86. package/dist-lib/lib/floridayGuard.server.d.ts +1 -1
  87. package/dist-lib/lib/floridayGuard.server.d.ts.map +1 -1
  88. package/dist-lib/lib/floridayGuard.server.js +6 -3
  89. package/dist-lib/lib/floridayPush.functions.d.ts +1600 -54
  90. package/dist-lib/lib/floridayPush.functions.d.ts.map +1 -1
  91. package/dist-lib/lib/floridayPush.functions.js +1 -1
  92. package/dist-lib/lib/flowGraph.d.ts +43 -0
  93. package/dist-lib/lib/flowGraph.d.ts.map +1 -0
  94. package/dist-lib/lib/flowGraph.js +153 -0
  95. package/dist-lib/lib/flowNodes.d.ts +23 -0
  96. package/dist-lib/lib/flowNodes.d.ts.map +1 -0
  97. package/dist-lib/lib/flowNodes.js +58 -0
  98. package/dist-lib/lib/flowRoute.d.ts +107 -0
  99. package/dist-lib/lib/flowRoute.d.ts.map +1 -0
  100. package/dist-lib/lib/flowRoute.js +280 -0
  101. package/dist-lib/lib/flowTree.d.ts +64 -0
  102. package/dist-lib/lib/flowTree.d.ts.map +1 -0
  103. package/dist-lib/lib/flowTree.js +519 -0
  104. package/dist-lib/lib/handlers/index.d.ts +1 -0
  105. package/dist-lib/lib/handlers/index.d.ts.map +1 -1
  106. package/dist-lib/lib/handlers/index.js +1 -0
  107. package/dist-lib/lib/handlers/logistics-shipping-note-now.server.d.ts +3 -0
  108. package/dist-lib/lib/handlers/logistics-shipping-note-now.server.d.ts.map +1 -0
  109. package/dist-lib/lib/handlers/logistics-shipping-note-now.server.js +100 -0
  110. package/dist-lib/lib/handlers/logistics-tick.server.d.ts.map +1 -1
  111. package/dist-lib/lib/handlers/logistics-tick.server.js +97 -2
  112. package/dist-lib/lib/loadCarrierPlanner.d.ts +2 -0
  113. package/dist-lib/lib/loadCarrierPlanner.d.ts.map +1 -1
  114. package/dist-lib/lib/loadCarrierPlanner.js +18 -0
  115. package/dist-lib/lib/loadCarrierPlanning.functions.d.ts +7740 -10
  116. package/dist-lib/lib/loadCarrierPlanning.functions.d.ts.map +1 -1
  117. package/dist-lib/lib/loadCarrierPlanning.functions.js +1 -1
  118. package/dist-lib/lib/loadCarrierPlanning.server.d.ts +9 -0
  119. package/dist-lib/lib/loadCarrierPlanning.server.d.ts.map +1 -1
  120. package/dist-lib/lib/loadCarrierPlanning.server.js +44 -17
  121. package/dist-lib/lib/logisticsPlanner.d.ts.map +1 -1
  122. package/dist-lib/lib/logisticsPlanner.js +14 -2
  123. package/dist-lib/lib/logisticsRecalcJob.functions.d.ts +3096 -4
  124. package/dist-lib/lib/logisticsRecalcJob.functions.d.ts.map +1 -1
  125. package/dist-lib/lib/logisticsRecalcJob.functions.js +1 -1
  126. package/dist-lib/lib/logisticsRecalculate.functions.d.ts +1600 -54
  127. package/dist-lib/lib/logisticsRecalculate.functions.d.ts.map +1 -1
  128. package/dist-lib/lib/logisticsRecalculate.functions.js +1 -1
  129. package/dist-lib/lib/modulePrompts.d.ts.map +1 -1
  130. package/dist-lib/lib/modulePrompts.js +3459 -2677
  131. package/dist-lib/lib/nodeTrace.d.ts +61 -0
  132. package/dist-lib/lib/nodeTrace.d.ts.map +1 -0
  133. package/dist-lib/lib/nodeTrace.js +191 -0
  134. package/dist-lib/lib/pickQueueCleanup.server.d.ts +14 -0
  135. package/dist-lib/lib/pickQueueCleanup.server.d.ts.map +1 -0
  136. package/dist-lib/lib/pickQueueCleanup.server.js +80 -0
  137. package/dist-lib/lib/planQueue.server.d.ts.map +1 -1
  138. package/dist-lib/lib/planQueue.server.js +140 -0
  139. package/dist-lib/lib/ruleEngineImpact.d.ts.map +1 -1
  140. package/dist-lib/lib/ruleEngineImpact.js +11 -4
  141. package/dist-lib/lib/ruleEngineSchema.d.ts.map +1 -1
  142. package/dist-lib/lib/ruleEngineSchema.js +50 -6
  143. package/dist-lib/lib/ruleEngineWizard.d.ts.map +1 -1
  144. package/dist-lib/lib/ruleEngineWizard.js +3 -1
  145. package/dist-lib/lib/shippingNoteQueue.server.d.ts +18 -0
  146. package/dist-lib/lib/shippingNoteQueue.server.d.ts.map +1 -0
  147. package/dist-lib/lib/shippingNoteQueue.server.js +304 -0
  148. package/dist-lib/lib/trace.server.d.ts +75 -0
  149. package/dist-lib/lib/trace.server.d.ts.map +1 -0
  150. package/dist-lib/lib/trace.server.js +75 -0
  151. package/dist-lib/lib/vakUitleg.d.ts +19 -0
  152. package/dist-lib/lib/vakUitleg.d.ts.map +1 -0
  153. package/dist-lib/lib/vakUitleg.js +132 -0
  154. package/package.json +26 -2
  155. package/dist-lib/components/rule-engine/FloridayConnectionBanner.d.ts +0 -2
  156. package/dist-lib/components/rule-engine/FloridayConnectionBanner.d.ts.map +0 -1
  157. package/dist-lib/components/rule-engine/FloridayConnectionBanner.js +0 -41
  158. package/dist-lib/integrations/supabase/client.d.ts +0 -20293
  159. package/dist-lib/integrations/supabase/client.d.ts.map +0 -1
  160. package/dist-lib/integrations/supabase/client.js +0 -13
@@ -0,0 +1,125 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // ============================================================================
3
+ // src/components/FlowOrders.tsx — orders en herberekeningen onder de flowkaart
4
+ // ============================================================================
5
+ // Alle orders die het systeem kent. Klik een order: alle herberekeningen
6
+ // (versies). Klik een versie: de gelopen route wordt blauw op de kaart, en
7
+ // hieronder staat elke gebeurtenis, beslissing, foutcode en document van die
8
+ // versie. Alleen lezen. GEEN <Header /> hier — die levert de shell.
9
+ // ============================================================================
10
+ import { useEffect, useMemo, useState } from "react";
11
+ import { Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Input, Skeleton, } from "@flowselections/core";
12
+ import { AlertTriangle, CheckCircle2, ChevronDown, ChevronRight, Clock, RefreshCw, Search, X } from "lucide-react";
13
+ import { useAlleOrders, useOrderVersies, useVersieEvents } from "../hooks/useOrderVersies";
14
+ import { bepaalRoute, redenTekst } from "../lib/flowRoute";
15
+ import { Gebeurtenis } from "./OrderLogboek";
16
+ function tijd(v) {
17
+ if (!v)
18
+ return "—";
19
+ const d = new Date(v);
20
+ if (Number.isNaN(d.getTime()))
21
+ return v;
22
+ return d.toLocaleString("nl-NL", { day: "2-digit", month: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit" });
23
+ }
24
+ function duur(a, b) {
25
+ if (!a || !b)
26
+ return null;
27
+ const ms = new Date(b).getTime() - new Date(a).getTime();
28
+ if (!Number.isFinite(ms) || ms < 0)
29
+ return null;
30
+ if (ms < 1000)
31
+ return `${ms} ms`;
32
+ if (ms < 60000)
33
+ return `${(ms / 1000).toFixed(1)} s`;
34
+ return `${Math.round(ms / 60000)} min`;
35
+ }
36
+ const VERSIE_STATUS = {
37
+ done: { label: "Ingedeeld", kleur: "default" },
38
+ running: { label: "Bezig", kleur: "secondary" },
39
+ pending: { label: "Wachtend", kleur: "secondary" },
40
+ failed: { label: "Mislukt", kleur: "destructive" },
41
+ geblokkeerd: { label: "Geblokkeerd", kleur: "destructive" },
42
+ };
43
+ function orderStand(r) {
44
+ if (r.cancelledAt)
45
+ return { label: "Geannuleerd", kleur: "outline" };
46
+ const q = (r.queueStatus ?? "").toLowerCase();
47
+ if (q === "failed" || q === "geblokkeerd")
48
+ return { label: q === "failed" ? "Mislukt" : "Geblokkeerd", kleur: "destructive" };
49
+ if (r.errors > 0 && r.lastStatus === "mislukt")
50
+ return { label: "Fout", kleur: "destructive" };
51
+ if (q === "running" || q === "pending")
52
+ return { label: q === "running" ? "Bezig" : "Wachtend", kleur: "secondary" };
53
+ if (r.orderStatus)
54
+ return { label: r.orderStatus, kleur: "default" };
55
+ return { label: "Onbekend", kleur: "outline" };
56
+ }
57
+ export function FlowOrders({ onRoute }) {
58
+ const [zoek, setZoek] = useState("");
59
+ const [zoekActief, setZoekActief] = useState("");
60
+ const [filter, setFilter] = useState("");
61
+ const [orderRef, setOrderRef] = useState(null);
62
+ const [versieId, setVersieId] = useState(null);
63
+ useEffect(() => {
64
+ const t = setTimeout(() => setZoekActief(zoek.trim()), 300);
65
+ return () => clearTimeout(t);
66
+ }, [zoek]);
67
+ const { rows, loading, error, refresh } = useAlleOrders(zoekActief);
68
+ const veiligeRows = Array.isArray(rows) ? rows : [];
69
+ const gefilterd = useMemo(() => veiligeRows.filter((r) => {
70
+ if (!filter)
71
+ return true;
72
+ const st = orderStand(r);
73
+ if (filter === "fout")
74
+ return st.kleur === "destructive";
75
+ if (filter === "bezig")
76
+ return st.kleur === "secondary";
77
+ return st.kleur === "default" || st.kleur === "outline";
78
+ }), [veiligeRows, filter]);
79
+ const versies = useOrderVersies(orderRef);
80
+ const veiligeVersies = Array.isArray(versies.versions) ? versies.versions : [];
81
+ const gekozen = veiligeVersies.find((v) => v.id === versieId) ?? null;
82
+ const laatsteId = veiligeVersies[0]?.id ?? null;
83
+ // Route naar de kaart sturen zodra order/versie verandert.
84
+ useEffect(() => {
85
+ if (!orderRef) {
86
+ onRoute(null, null, null, null);
87
+ return;
88
+ }
89
+ if (versies.loading)
90
+ return;
91
+ const v = gekozen ?? (versieId ? null : veiligeVersies[0] ?? null);
92
+ const route = bepaalRoute(v, versies.order, versies.carts, !v || v.id === laatsteId);
93
+ const label = v ? `${orderRef} · versie ${v.nr}${v.id === laatsteId ? " (laatste)" : ""}` : orderRef;
94
+ onRoute(route, label, orderRef, versieId ? v : null);
95
+ // eslint-disable-next-line react-hooks/exhaustive-deps
96
+ }, [orderRef, versieId, versies.loading, versies.order, versies.carts, veiligeVersies.length]);
97
+ const kies = (nr) => {
98
+ setVersieId(null);
99
+ setOrderRef((v) => (v === nr ? null : nr));
100
+ };
101
+ return (_jsxs(Card, { children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsxs("div", { className: "flex flex-wrap items-start justify-between gap-3", children: [_jsxs("div", { children: [_jsx(CardTitle, { className: "text-base", children: "Orders in het systeem" }), _jsx(CardDescription, { children: "Klik een order voor alle herberekeningen; klik een versie om de gelopen route blauw op de kaart te zien en precies te lezen waar hij is vastgelopen." })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => void refresh(), disabled: loading, children: [_jsx(RefreshCw, { className: `mr-1 h-3.5 w-3.5 ${loading ? "animate-spin" : ""}` }), "Vernieuwen"] })] }), _jsxs("div", { className: "mt-2 flex flex-wrap items-center gap-2", children: [_jsxs("div", { className: "relative w-full max-w-xs", children: [_jsx(Search, { className: "pointer-events-none absolute left-2 top-2.5 h-4 w-4 text-muted-foreground" }), _jsx(Input, { className: "pl-8", placeholder: "Zoek op ordernummer of klant", value: zoek, onChange: (e) => setZoek(e.target.value) })] }), [
102
+ ["", "Alles"],
103
+ ["fout", "Vastgelopen"],
104
+ ["bezig", "Bezig / wachtend"],
105
+ ["klaar", "Overig"],
106
+ ].map(([w, l]) => (_jsx(Button, { size: "sm", variant: filter === w ? "default" : "outline", onClick: () => setFilter(w), children: l }, w))), _jsxs("span", { className: "ml-auto text-xs text-muted-foreground", children: [gefilterd.length, " orders"] })] })] }), _jsxs(CardContent, { className: "space-y-3", children: [error && _jsx("p", { className: "text-sm text-destructive", children: error }), loading && veiligeRows.length === 0 ? (_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" })] })) : gefilterd.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Geen orders gevonden." })) : (_jsx("div", { className: "max-h-[420px] overflow-y-auto rounded-md border border-border", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { className: "sticky top-0 bg-muted/60 text-xs text-muted-foreground", children: _jsxs("tr", { children: [_jsx("th", { className: "px-2 py-1.5 text-left font-medium", children: "Order" }), _jsx("th", { className: "px-2 py-1.5 text-left font-medium", children: "Klant" }), _jsx("th", { className: "px-2 py-1.5 text-left font-medium", children: "Bron" }), _jsx("th", { className: "px-2 py-1.5 text-left font-medium", children: "Levering" }), _jsx("th", { className: "px-2 py-1.5 text-left font-medium", children: "Stand" }), _jsx("th", { className: "px-2 py-1.5 text-right font-medium", children: "Versies" }), _jsx("th", { className: "px-2 py-1.5 text-right font-medium", children: "Fouten" }), _jsx("th", { className: "px-2 py-1.5 text-left font-medium", children: "Laatste activiteit" })] }) }), _jsx("tbody", { children: gefilterd.map((r) => {
107
+ const st = orderStand(r);
108
+ const actief = r.orderNumber === orderRef;
109
+ return (_jsxs("tr", { className: `cursor-pointer border-t border-border hover:bg-muted/40 ${actief ? "bg-primary/10" : ""}`, onClick: () => kies(r.orderNumber), children: [_jsx("td", { className: "px-2 py-1.5 font-medium", children: _jsxs("span", { className: "inline-flex items-center gap-1", children: [actief ? _jsx(ChevronDown, { className: "h-3.5 w-3.5" }) : _jsx(ChevronRight, { className: "h-3.5 w-3.5" }), r.orderNumber] }) }), _jsx("td", { className: "max-w-[180px] truncate px-2 py-1.5", children: r.customerName ?? "—" }), _jsx("td", { className: "px-2 py-1.5", children: r.orderSource === "floriday" ? "Floriday" : r.orderSource ? "Eigen verkoop" : "—" }), _jsx("td", { className: "px-2 py-1.5", children: r.deliveryDate ?? "—" }), _jsxs("td", { className: "px-2 py-1.5", children: [_jsx(Badge, { variant: st.kleur, children: st.label }), r.reasonCode && st.kleur === "destructive" && (_jsx("span", { className: "ml-1 text-xs text-muted-foreground", children: redenTekst(r.reasonCode) }))] }), _jsx("td", { className: "px-2 py-1.5 text-right tabular-nums", children: r.versions }), _jsx("td", { className: `px-2 py-1.5 text-right tabular-nums ${r.errors > 0 ? "text-destructive" : ""}`, children: r.errors }), _jsx("td", { className: "px-2 py-1.5 text-xs text-muted-foreground", children: tijd(r.lastAt ?? r.createdAt) })] }, r.orderNumber));
110
+ }) })] }) })), orderRef && (_jsxs("div", { className: "space-y-3 rounded-md border border-primary/40 bg-primary/5 p-3", children: [_jsxs("div", { className: "flex flex-wrap items-start justify-between gap-2", children: [_jsxs("div", { children: [_jsx("p", { className: "font-semibold", children: orderRef }), _jsxs("p", { className: "text-xs text-muted-foreground", children: [versies.order?.customerName ?? "", versies.order?.status ? ` · status: ${versies.order.status}` : "", versies.carts && versies.carts.total > 0
111
+ ? ` · karren: ${versies.carts.total} (${versies.carts.printed} geprint, ${versies.carts.controlled} gecontroleerd)`
112
+ : "", versies.order?.planLockedAt ? " · indeling bevroren" : ""] })] }), _jsx(Button, { variant: "ghost", size: "icon", "aria-label": "Order sluiten", onClick: () => kies(orderRef), children: _jsx(X, { className: "h-4 w-4" }) })] }), versies.error && _jsx("p", { className: "text-sm text-destructive", children: versies.error }), versies.loading ? (_jsx(Skeleton, { className: "h-16 w-full" })) : veiligeVersies.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Nog geen herberekening vastgelegd voor deze order. De kaart toont waar de order nu staat." })) : (_jsxs("div", { className: "space-y-1.5", children: [_jsxs("p", { className: "text-xs font-medium text-muted-foreground", children: [veiligeVersies.length, " herberekening", veiligeVersies.length === 1 ? "" : "en", " \u2014 nieuwste bovenaan"] }), veiligeVersies.map((v) => (_jsx(VersieRij, { versie: v, laatste: v.id === laatsteId, gekozen: (gekozen?.id ?? (versieId ? null : laatsteId)) === v.id, onKies: () => setVersieId((cur) => (cur === v.id ? null : v.id)) }, v.id)))] })), (gekozen ?? (versieId ? null : veiligeVersies[0])) && (_jsx(VersieDetail, { orderRef: orderRef, versie: (gekozen ?? veiligeVersies[0]), route: bepaalRoute(gekozen ?? veiligeVersies[0], versies.order, versies.carts, (gekozen ?? veiligeVersies[0])?.id === laatsteId) }))] }))] })] }));
113
+ }
114
+ function VersieRij({ versie, laatste, gekozen, onKies, }) {
115
+ const st = VERSIE_STATUS[(versie.status ?? "pending").toLowerCase()] ?? { label: versie.status ?? "—", kleur: "outline" };
116
+ const d = duur(versie.claimedAt ?? versie.startedAt, versie.finishedAt);
117
+ return (_jsxs("button", { type: "button", onClick: onKies, className: `flex w-full flex-wrap items-center gap-2 rounded-md border px-2 py-1.5 text-left text-sm hover:bg-muted/40 ${gekozen ? "border-primary bg-background ring-1 ring-primary" : "border-border bg-background/60"}`, children: [_jsxs("span", { className: "w-20 font-medium", children: ["Versie ", versie.nr] }), _jsx(Badge, { variant: st.kleur, children: st.label }), laatste && _jsx(Badge, { variant: "outline", children: "laatste" }), _jsxs("span", { className: "text-xs text-muted-foreground", children: [_jsx(Clock, { className: "mr-1 inline h-3 w-3" }), tijd(versie.startedAt), d ? ` · ${d}` : ""] }), versie.reason && !/^(insert|update|delete)$/i.test(versie.reason) && (_jsxs("span", { className: "text-xs text-muted-foreground", children: ["aanleiding: ", versie.reason] })), _jsxs("span", { className: "ml-auto flex items-center gap-2 text-xs text-muted-foreground", children: [_jsxs("span", { children: [versie.events, " gebeurtenissen"] }), versie.printJobs > 0 && _jsxs("span", { children: [versie.printJobs, " print"] }), versie.errors > 0 && (_jsxs("span", { className: "text-destructive", children: [_jsx(AlertTriangle, { className: "mr-0.5 inline h-3 w-3" }), versie.errors] })), versie.attempts > 1 && _jsxs("span", { children: [versie.attempts, " pogingen"] })] }), (versie.reasonCode || versie.error) && (_jsx("p", { className: "w-full text-xs text-destructive", children: redenTekst(versie.reasonCode, versie.error) }))] }));
118
+ }
119
+ function VersieDetail({ orderRef, versie, route }) {
120
+ const { events, loading, error } = useVersieEvents(orderRef, versie);
121
+ const veilig = Array.isArray(events) ? events : [];
122
+ const [alles, setAlles] = useState(false);
123
+ const getoond = alles ? veilig : veilig.filter((e) => e.status !== "info" || e.phase !== "database");
124
+ return (_jsxs("div", { className: "space-y-2 rounded-md border border-border bg-background p-3", children: [_jsxs("div", { className: `flex items-start gap-2 rounded-md px-2 py-1.5 text-sm ${route.stuck ? "bg-destructive/10 text-destructive" : route.finished ? "bg-primary/10 text-primary" : "bg-muted text-foreground"}`, children: [route.stuck ? _jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0" }) : route.finished ? _jsx(CheckCircle2, { className: "mt-0.5 h-4 w-4 shrink-0" }) : _jsx(Clock, { className: "mt-0.5 h-4 w-4 shrink-0" }), _jsxs("div", { children: [_jsx("p", { className: "font-medium", children: route.summary }), _jsx("p", { className: "text-xs opacity-80", children: "Deze route is blauw op de kaart. De kaart toont de werkwijze van nu; welke keuze binnen een stap destijds gold is niet apart bewaard." })] })] }), _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("p", { className: "text-xs font-medium text-muted-foreground", children: ["Gebeurtenissen van versie ", versie.nr, " (", getoond.length, getoond.length !== veilig.length ? ` van ${veilig.length}` : "", ")"] }), veilig.length !== getoond.length || alles ? (_jsx(Button, { variant: "ghost", size: "sm", onClick: () => setAlles((v) => !v), children: alles ? "Alleen belangrijk" : "Alles tonen (ook databaseregels)" })) : null] }), error && _jsx("p", { className: "text-sm text-destructive", children: error }), loading ? (_jsx(Skeleton, { className: "h-20 w-full" })) : getoond.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Geen gebeurtenissen vastgelegd voor deze versie." })) : (_jsx("div", { className: "max-h-[520px] space-y-1 overflow-y-auto pr-1", children: getoond.map((e) => (_jsx(Gebeurtenis, { event: e }, e.id))) }))] }));
125
+ }
@@ -0,0 +1,2 @@
1
+ export declare function FlowPage(): import("react").JSX.Element;
2
+ //# sourceMappingURL=FlowPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FlowPage.d.ts","sourceRoot":"","sources":["../../src/components/FlowPage.tsx"],"names":[],"mappings":"AAuDA,wBAAgB,QAAQ,gCAqVvB"}
@@ -0,0 +1,141 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // ============================================================================
3
+ // src/components/FlowPage.tsx — visuele flow van de Logistiek Engine
4
+ // ============================================================================
5
+ // Het schema wordt opgebouwd uit dezelfde werkwijze die de engine gebruikt
6
+ // (WIZARD_STEPS + de opgeslagen instellingen). Klik je een stap aan, dan pas
7
+ // je diezelfde instellingen aan; opslaan werkt door in de engine én in dit
8
+ // schema. GEEN <Header /> hier — die levert de shell.
9
+ // ============================================================================
10
+ import { useCallback, useMemo, useRef, useState } from "react";
11
+ import { Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Skeleton, toast, } from "@flowselections/core";
12
+ import { CheckCircle2, RefreshCw, Save, X } from "lucide-react";
13
+ import { fieldVisible, } from "../lib/ruleEngineSchema";
14
+ import { WIZARD_STEPS, groupSummary, isAdvancedField } from "../lib/ruleEngineWizard";
15
+ import { buildFlowTree } from "../lib/flowTree";
16
+ import { applyRoute } from "../lib/flowRoute";
17
+ import { FlowCanvas } from "./FlowCanvas";
18
+ import { FlowOrders } from "./FlowOrders";
19
+ import { OrderJourneyBar } from "./OrderJourneyBar";
20
+ import { OrderStopBanner } from "./OrderStopBanner";
21
+ import { useOrderNodeTrace } from "../hooks/useOrderNodeTrace";
22
+ import { useRuleEngine } from "../hooks/useRuleEngine";
23
+ import { useLogistiekStrategieen } from "../hooks/useLogistiekStrategieen";
24
+ import { useRecalcJob, phaseLabel } from "../hooks/useRecalcJob";
25
+ import { RuleFieldInput } from "./rule-engine/RuleFieldInput";
26
+ import { CollapsibleGroup } from "./rule-engine/CollapsibleGroup";
27
+ import { ConditionsEditor } from "./rule-engine/ConditionsEditor";
28
+ const WIDE_TYPES = ["priority", "toggle", "days", "radio", "textarea", "guidance"];
29
+ function fieldClass(field) {
30
+ return WIDE_TYPES.includes(field.type) ? "md:col-span-2" : "";
31
+ }
32
+ export function FlowPage() {
33
+ const { configs, activeConfig, activeId, setActiveId, loading, saving, error, patchSettings, saveConfig, activateConfig, } = useRuleEngine();
34
+ const { items: strategieen } = useLogistiekStrategieen();
35
+ const [openStep, setOpenStep] = useState(null);
36
+ // Naam van de werkwijze = de naam uit het Werkwijzen-overzicht (strategie),
37
+ // zodat beide schermen exact hetzelfde zeggen.
38
+ const werkwijzeNaam = (config) => {
39
+ if (!config)
40
+ return "";
41
+ const lijst = Array.isArray(strategieen) ? strategieen : [];
42
+ const match = config.strategy_id
43
+ ? lijst.find((s) => s.id === config.strategy_id)
44
+ : undefined;
45
+ return match?.name ?? config.name.replace(/^Werkwijze\s+/i, "");
46
+ };
47
+ const settings = activeConfig?.settings ?? {};
48
+ const basisTree = useMemo(() => buildFlowTree(settings), [settings]);
49
+ // Gekozen order/versie: de echte reis wordt als laag over de kaart gelegd.
50
+ const [orderRoute, setOrderRoute] = useState(null);
51
+ const [routeLabel, setRouteLabel] = useState(null);
52
+ const [orderRef, setOrderRef] = useState(null);
53
+ const [versie, setVersie] = useState(null);
54
+ const [traceNodeId, setTraceNodeId] = useState(null);
55
+ const kaartRef = useRef(null);
56
+ const onRoute = useCallback((route, label, ref, v) => {
57
+ setOrderRoute(route);
58
+ setRouteLabel(label);
59
+ setOrderRef(ref);
60
+ setVersie(v);
61
+ setTraceNodeId(null);
62
+ if (route && label) {
63
+ // De kaart staat boven de orderlijst: breng hem meteen in beeld.
64
+ setTimeout(() => kaartRef.current?.scrollIntoView({ behavior: "smooth", block: "start" }), 50);
65
+ }
66
+ }, []);
67
+ const { groepen, reis, loading: traceLoading } = useOrderNodeTrace(orderRef, versie);
68
+ const overlay = useMemo(() => applyRoute(basisTree, orderRoute, orderRef ? groepen : null), [basisTree, orderRoute, groepen, orderRef]);
69
+ const tree = overlay.tree;
70
+ const step = WIZARD_STEPS.find((s) => s.id === openStep) ?? null;
71
+ const stepValues = step ? (settings?.[step.storeId]?.fields ?? {}) : {};
72
+ const stepConditions = step
73
+ ? (settings?.[step.storeId]?.conditions ?? [])
74
+ : [];
75
+ const setFieldValue = (key, value) => {
76
+ if (!activeConfig || !step)
77
+ return;
78
+ patchSettings(activeConfig.id, {
79
+ ...settings,
80
+ [step.storeId]: {
81
+ ...(settings[step.storeId] ?? { fields: {}, conditions: [] }),
82
+ fields: { ...stepValues, [key]: value },
83
+ },
84
+ });
85
+ };
86
+ // Keuze veranderen vanuit een ruit in het schema (elke stap, niet alleen de
87
+ // geopende): schrijft naar dezelfde werkwijze die de engine gebruikt.
88
+ const setFieldByStore = (storeId, key, value) => {
89
+ if (!activeConfig)
90
+ return;
91
+ const huidig = settings[storeId] ?? { fields: {}, conditions: [] };
92
+ patchSettings(activeConfig.id, {
93
+ ...settings,
94
+ [storeId]: { ...huidig, fields: { ...(huidig.fields ?? {}), [key]: value } },
95
+ });
96
+ };
97
+ const setConditions = (conditions) => {
98
+ if (!activeConfig || !step)
99
+ return;
100
+ patchSettings(activeConfig.id, {
101
+ ...settings,
102
+ [step.storeId]: {
103
+ ...(settings[step.storeId] ?? { fields: {}, conditions: [] }),
104
+ conditions,
105
+ },
106
+ });
107
+ };
108
+ const { job, starting, startRecalculation } = useRecalcJob();
109
+ const running = job?.status === "running";
110
+ const [busy, setBusy] = useState(false);
111
+ const opslaan = async (opnieuwBerekenen) => {
112
+ if (!activeConfig)
113
+ return;
114
+ setBusy(true);
115
+ try {
116
+ await saveConfig(activeConfig);
117
+ if (opnieuwBerekenen) {
118
+ if (!activeConfig.is_active)
119
+ await activateConfig(activeConfig.id);
120
+ await startRecalculation(null);
121
+ toast.success("Opgeslagen — de herberekening loopt op de achtergrond");
122
+ }
123
+ }
124
+ catch (e) {
125
+ toast.error(e?.message ?? "Opslaan is niet gelukt. Probeer het nog een keer.");
126
+ }
127
+ finally {
128
+ setBusy(false);
129
+ }
130
+ };
131
+ const veiligeConfigs = Array.isArray(configs) ? configs : [];
132
+ return (_jsxs("div", { className: "space-y-4 p-6", children: [_jsxs("header", { className: "flex flex-wrap items-start justify-between gap-3", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-xl font-semibold tracking-tight", children: "Flow van de Logistiek Engine" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Alle mogelijke routes door de engine in \u00E9\u00E9n kaart. De gekleurde route geldt nu; kies in een beslistegel iets anders en de actieve tak springt direct mee." })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [veiligeConfigs.length > 1 && (_jsxs(Select, { value: activeId ?? "", onValueChange: (v) => setActiveId(v), children: [_jsx(SelectTrigger, { className: "w-56", children: _jsx(SelectValue, { placeholder: "Kies een werkwijze" }) }), _jsx(SelectContent, { children: veiligeConfigs.map((c) => (_jsxs(SelectItem, { value: c.id, children: [werkwijzeNaam(c), c.is_active ? " (actief)" : ""] }, c.id))) })] })), _jsxs(Button, { variant: "outline", size: "sm", disabled: !activeConfig || saving || busy, onClick: () => void opslaan(false), children: [_jsx(Save, { className: "h-4 w-4" }), " Opslaan"] }), _jsxs(Button, { size: "sm", disabled: !activeConfig || saving || busy || starting || running, onClick: () => void opslaan(true), children: [busy || starting || running ? (_jsx(RefreshCw, { className: "h-4 w-4 animate-spin" })) : (_jsx(CheckCircle2, { className: "h-4 w-4" })), running ? `${phaseLabel(job?.phase)}…` : "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 werkwijze kon niet worden geladen. Probeer het opnieuw." })), loading ? (_jsxs("div", { className: "space-y-3", children: [_jsx(Skeleton, { className: "h-24 w-full" }), _jsx(Skeleton, { className: "h-24 w-full" }), _jsx(Skeleton, { className: "h-24 w-full" })] })) : !activeConfig ? (_jsx(Card, { children: _jsxs(CardHeader, { children: [_jsx(CardTitle, { children: "Nog geen werkwijze" }), _jsx(CardDescription, { children: "Maak eerst een werkwijze aan bij Werkwijze instellen; daarna verschijnt de flow hier vanzelf." })] }) })) : (_jsxs("div", { className: `grid gap-6 ${openStep ? "lg:grid-cols-[minmax(0,1fr)_420px]" : "lg:grid-cols-1"}`, children: [_jsxs("div", { className: "space-y-2", ref: kaartRef, children: [_jsxs("p", { className: "text-xs text-muted-foreground", children: ["werkwijze: ", werkwijzeNaam(activeConfig), activeConfig.is_active ? " (actief)" : " (concept)"] }), orderRef && _jsx(OrderStopBanner, { orderRef: orderRef, onNode: setTraceNodeId }), _jsx(FlowCanvas, { tree: tree, selectedStepId: openStep, settings: settings, markers: overlay.markers, nodeTrace: orderRef ? groepen : null, routeLabel: routeLabel, routeSummary: orderRoute?.summary ?? null, traceNodeId: traceNodeId, onTraceNode: setTraceNodeId, onClearRoute: () => onRoute(null, null, null, null), onSelectStep: (stepId) => setOpenStep((v) => (v === stepId ? null : stepId)), onChangeChoice: setFieldByStore }), orderRef && (_jsx(OrderJourneyBar, { reis: reis, actiefNodeId: traceNodeId, onKies: setTraceNodeId })), orderRef && !traceLoading && reis.length === 0 && (_jsx("p", { className: "text-xs text-muted-foreground", children: "Voor deze order zijn nog geen stappen per vakje vastgelegd; de kaart toont dan de afgeleide route." }))] }), step && (_jsx("aside", { className: "space-y-3 lg:sticky lg:top-4 lg:max-h-[calc(100vh-8rem)] lg:overflow-y-auto", children: _jsxs(Card, { children: [_jsx(CardHeader, { className: "pb-3", children: _jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { children: [_jsx(CardTitle, { className: "text-base", children: step.label }), _jsx(CardDescription, { children: step.description })] }), _jsx(Button, { variant: "ghost", size: "icon", "aria-label": "Paneel sluiten", onClick: () => setOpenStep(null), children: _jsx(X, { className: "h-4 w-4" }) })] }) }), _jsxs(CardContent, { className: "space-y-3", children: [step.groups.map((group, groupIndex) => {
133
+ const visibleFields = group.fields.filter((field) => fieldVisible(field, stepValues, settings));
134
+ if (visibleFields.length === 0)
135
+ return null;
136
+ const basicFields = visibleFields.filter((f) => !isAdvancedField(f));
137
+ const advancedFields = visibleFields.filter((f) => isAdvancedField(f));
138
+ const isLastGroup = groupIndex === step.groups.length - 1;
139
+ return (_jsxs(CollapsibleGroup, { title: group.label, help: group.description, summary: groupSummary(group, stepValues), defaultOpen: groupIndex === 0, children: [basicFields.length > 0 && (_jsx("div", { className: "grid gap-4 md:grid-cols-2", children: basicFields.map((field) => (_jsx("div", { className: fieldClass(field), children: _jsx(RuleFieldInput, { field: field, value: stepValues[field.key], stepValues: stepValues, onChange: (value) => setFieldValue(field.key, value) }) }, field.key))) })), (advancedFields.length > 0 || (step.showConditions && isLastGroup)) && (_jsxs("details", { className: "rounded-lg border border-dashed border-border", children: [_jsx("summary", { className: "cursor-pointer px-3 py-2 text-sm font-medium text-muted-foreground", children: "Geavanceerd" }), _jsxs("div", { className: "space-y-4 border-t border-border px-3 py-4", children: [advancedFields.length > 0 && (_jsx("div", { className: "grid gap-4 md:grid-cols-2", children: advancedFields.map((field) => (_jsx("div", { className: fieldClass(field), children: _jsx(RuleFieldInput, { field: field, value: stepValues[field.key], stepValues: stepValues, onChange: (value) => setFieldValue(field.key, value) }) }, field.key))) })), step.showConditions && isLastGroup && (_jsx(ConditionsEditor, { conditions: stepConditions, onChange: setConditions }))] })] }))] }, group.id));
140
+ }), _jsxs("div", { className: "flex flex-wrap gap-2 pt-1", children: [_jsxs(Button, { variant: "outline", size: "sm", disabled: saving || busy, onClick: () => void opslaan(false), children: [_jsx(Save, { className: "h-4 w-4" }), " Opslaan"] }), _jsxs(Button, { size: "sm", disabled: saving || busy, onClick: () => void opslaan(true), children: [_jsx(CheckCircle2, { className: "h-4 w-4" }), " Opslaan & opnieuw berekenen"] })] })] })] }) }))] })), !loading && activeConfig && _jsx(FlowOrders, { onRoute: onRoute })] }));
141
+ }
@@ -0,0 +1,9 @@
1
+ import { type ReactNode } from "react";
2
+ export interface InklapbaarVensterProps {
3
+ titel: string;
4
+ omschrijving?: string;
5
+ standaardOpen?: boolean;
6
+ children: ReactNode;
7
+ }
8
+ export declare function InklapbaarVenster({ titel, omschrijving, standaardOpen, children, }: InklapbaarVensterProps): import("react").JSX.Element;
9
+ //# sourceMappingURL=InklapbaarVenster.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InklapbaarVenster.d.ts","sourceRoot":"","sources":["../../src/components/InklapbaarVenster.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGjD,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,wBAAgB,iBAAiB,CAAC,EAChC,KAAK,EACL,YAAY,EACZ,aAAqB,EACrB,QAAQ,GACT,EAAE,sBAAsB,+BA0BxB"}
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // ============================================================================
3
+ // src/components/InklapbaarVenster.tsx — venster dat in- en uitgeklapt kan worden
4
+ // ============================================================================
5
+ import { useState } from "react";
6
+ import { ChevronDown } from "lucide-react";
7
+ export function InklapbaarVenster({ titel, omschrijving, standaardOpen = false, children, }) {
8
+ const [open, setOpen] = useState(standaardOpen);
9
+ return (_jsxs("section", { className: "rounded-lg border border-border", children: [_jsxs("button", { type: "button", onClick: () => setOpen((v) => !v), "aria-expanded": open, className: "flex w-full items-center justify-between gap-3 rounded-lg px-4 py-3 text-left transition-colors hover:bg-muted/60", children: [_jsxs("span", { className: "min-w-0", children: [_jsx("span", { className: "block text-sm font-semibold", children: titel }), omschrijving && (_jsx("span", { className: "mt-0.5 block truncate text-xs text-muted-foreground", children: omschrijving }))] }), _jsx(ChevronDown, { className: `h-4 w-4 shrink-0 text-muted-foreground transition-transform ${open ? "rotate-180" : ""}` })] }), open && _jsx("div", { className: "border-t border-border p-4", children: children })] }));
10
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"LogistiekEnginePage.d.ts","sourceRoot":"","sources":["../../src/components/LogistiekEnginePage.tsx"],"names":[],"mappings":"AAuEA,wBAAgB,mBAAmB,gCAkPlC"}
1
+ {"version":3,"file":"LogistiekEnginePage.d.ts","sourceRoot":"","sources":["../../src/components/LogistiekEnginePage.tsx"],"names":[],"mappings":"AAiFA,wBAAgB,mBAAmB,gCAgPlC"}
@@ -7,11 +7,11 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
7
7
  // ============================================================================
8
8
  import { useState } from "react";
9
9
  import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Badge, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Skeleton, } from "@flowselections/core";
10
- import { Container, Pencil, Plus, Puzzle, SlidersHorizontal, Trash2, Workflow } from "lucide-react";
10
+ import { Container, GitBranch, Pencil, Plus, Puzzle, SlidersHorizontal, Trash2, Workflow } from "lucide-react";
11
11
  import { Link } from "@tanstack/react-router";
12
12
  import { StrategieDialog } from "./StrategieDialog";
13
13
  import { WerkwijzeHerindelingCard } from "./WerkwijzeHerindelingCard";
14
- import { FloridayPoortCard } from "./FloridayPoortCard";
14
+ import { WerktijdenCard } from "./WerktijdenCard";
15
15
  import { useLogistiekStrategieen, } from "../hooks/useLogistiekStrategieen";
16
16
  const SNELKOPPELINGEN = [
17
17
  {
@@ -20,6 +20,12 @@ const SNELKOPPELINGEN = [
20
20
  titel: "Werkwijze instellen",
21
21
  uitleg: "Bepaal stap voor stap hoe orders worden verzameld, gepickt en klaargezet.",
22
22
  },
23
+ {
24
+ to: "/logistiek-engine/flow",
25
+ icon: GitBranch,
26
+ titel: "Flow",
27
+ uitleg: "Volg per order de reis door de flow en pas de werkwijze aan.",
28
+ },
23
29
  {
24
30
  to: "/logistiek-engine/regie",
25
31
  icon: Workflow,
@@ -64,7 +70,7 @@ export function LogistiekEnginePage() {
64
70
  setEditing(item);
65
71
  setDialogOpen(true);
66
72
  };
67
- return (_jsxs("main", { className: "p-6 space-y-6", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "Hier bepaal je hoe orders in je bedrijf logistiek worden afgehandeld." }), _jsx("div", { className: "grid gap-3 sm:grid-cols-2 lg:grid-cols-4", children: SNELKOPPELINGEN.map(({ to, icon: Icon, titel, uitleg }) => (_jsx(Link, { to: to, className: "group", children: _jsx(Card, { className: "h-full transition-colors group-hover:border-primary", children: _jsxs(CardHeader, { className: "p-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-md bg-primary/10 text-primary", children: _jsx(Icon, { className: "h-4 w-4" }) }), _jsx(CardTitle, { className: "text-sm font-semibold leading-tight", children: titel })] }), _jsx(CardDescription, { className: "pt-1 text-xs leading-relaxed", children: uitleg })] }) }) }, to))) }), _jsx(WerkwijzeHerindelingCard, {}), _jsx(FloridayPoortCard, {}), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsxs("div", { className: "flex flex-wrap items-start justify-between gap-3", children: [_jsxs("div", { children: [_jsx(CardTitle, { children: "Werkwijzen" }), _jsx(CardDescription, { children: "Alleen een werkwijze die aan staat wordt op je orders toegepast." })] }), _jsxs(Button, { size: "sm", onClick: openNew, children: [_jsx(Plus, { className: "h-4 w-4" }), "Nieuwe werkwijze"] })] }) }), _jsx(CardContent, { children: 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" })] })) : error ? (_jsx("p", { className: "text-sm text-destructive", children: "Kon de gegevens niet ophalen. Probeer het over een moment opnieuw." })) : safeItems.length === 0 ? (_jsxs("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "Je hebt nog geen werkwijze ingesteld." }), _jsxs(Button, { variant: "outline", size: "sm", onClick: openNew, children: [_jsx(Plus, { className: "h-4 w-4" }), "Eerste werkwijze aanmaken"] })] })) : (_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Naam" }), _jsx(TableHead, { children: "Status" }), _jsx(TableHead, { className: "w-48 text-right", children: "Acties" })] }) }), _jsx(TableBody, { children: safeItems.map((item) => (_jsxs(TableRow, { children: [_jsx(TableCell, { children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "h-3 w-3 shrink-0 rounded-full border border-border", style: { backgroundColor: item.color ?? "#3b82f6" } }), _jsxs("div", { children: [_jsx("p", { className: "font-medium", children: item.name ?? "—" }), item.description && (_jsx("p", { className: "text-xs text-muted-foreground", children: item.description }))] })] }) }), _jsx(TableCell, { children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsxs("label", { className: "flex items-center gap-2 text-sm", children: [_jsx("input", { type: "radio", name: "actieve-werkwijze", className: "h-4 w-4 accent-primary", checked: !!item.is_active, disabled: saving, onChange: () => kiesWerkwijze(item), "aria-label": `${item.name ?? "Werkwijze"} als actieve werkwijze kiezen` }), _jsx("span", { className: "sr-only", children: "Actief" })] }), _jsx(Badge, { variant: item.is_active ? "default" : "secondary", children: item.is_active ? "Actief" : "Uit" }), item.is_active && (_jsx(Button, { size: "sm", variant: "ghost", disabled: saving, onClick: () => void clearActive(), children: "Uitzetten" }))] }) }), _jsx(TableCell, { className: "text-right", children: _jsxs("div", { className: "flex justify-end gap-1", children: [_jsx(Button, { size: "sm", variant: "outline", asChild: true, children: _jsxs(Link, { to: "/logistiek-engine/rule-engine", search: { strategy: String(item.id), naam: item.name ?? "" }, children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), "Instellen"] }) }), _jsx(Button, { size: "icon", variant: "ghost", onClick: () => openEdit(item), "aria-label": "Bewerken", children: _jsx(Pencil, { className: "h-4 w-4" }) }), _jsx(Button, { size: "icon", variant: "ghost", onClick: () => setDeleting(item), "aria-label": "Verwijderen", children: _jsx(Trash2, { className: "h-4 w-4" }) })] }) })] }, String(item.id)))) })] })) })] }), _jsx(StrategieDialog, { open: dialogOpen, onOpenChange: setDialogOpen, strategie: editing, saving: saving, onSubmit: (input) => (editing ? update(editing.id, input) : create(input)) }), _jsx(AlertDialog, { open: !!deleting, onOpenChange: (open) => !open && setDeleting(null), children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Werkwijze verwijderen?" }), _jsxs(AlertDialogDescription, { children: ["\"", deleting?.name, "\" wordt definitief verwijderd. Dit kan niet ongedaan worden gemaakt."] })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { children: "Annuleren" }), _jsx(AlertDialogAction, { onClick: async () => {
73
+ return (_jsxs("main", { className: "p-6 space-y-6", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "Hier bepaal je hoe orders in je bedrijf logistiek worden afgehandeld." }), _jsx("div", { className: "grid gap-3 sm:grid-cols-2 lg:grid-cols-4", children: SNELKOPPELINGEN.map(({ to, icon: Icon, titel, uitleg }) => (_jsx(Link, { to: to, className: "group", children: _jsx(Card, { className: "h-full transition-colors group-hover:border-primary", children: _jsxs(CardHeader, { className: "p-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-md bg-primary/10 text-primary", children: _jsx(Icon, { className: "h-4 w-4" }) }), _jsx(CardTitle, { className: "text-sm font-semibold leading-tight", children: titel })] }), _jsx(CardDescription, { className: "pt-1 text-xs leading-relaxed", children: uitleg })] }) }) }, to))) }), _jsx(WerkwijzeHerindelingCard, {}), _jsx(WerktijdenCard, {}), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsxs("div", { className: "flex flex-wrap items-start justify-between gap-3", children: [_jsxs("div", { children: [_jsx(CardTitle, { children: "Werkwijzen" }), _jsx(CardDescription, { children: "Alleen een werkwijze die aan staat wordt op je orders toegepast." })] }), _jsxs(Button, { size: "sm", onClick: openNew, children: [_jsx(Plus, { className: "h-4 w-4" }), "Nieuwe werkwijze"] })] }) }), _jsx(CardContent, { children: 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" })] })) : error ? (_jsx("p", { className: "text-sm text-destructive", children: "Kon de gegevens niet ophalen. Probeer het over een moment opnieuw." })) : safeItems.length === 0 ? (_jsxs("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "Je hebt nog geen werkwijze ingesteld." }), _jsxs(Button, { variant: "outline", size: "sm", onClick: openNew, children: [_jsx(Plus, { className: "h-4 w-4" }), "Eerste werkwijze aanmaken"] })] })) : (_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Naam" }), _jsx(TableHead, { children: "Status" }), _jsx(TableHead, { className: "w-48 text-right", children: "Acties" })] }) }), _jsx(TableBody, { children: safeItems.map((item) => (_jsxs(TableRow, { children: [_jsx(TableCell, { children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "h-3 w-3 shrink-0 rounded-full border border-border", style: { backgroundColor: item.color ?? "#3b82f6" } }), _jsxs("div", { children: [_jsx("p", { className: "font-medium", children: item.name ?? "—" }), item.description && (_jsx("p", { className: "text-xs text-muted-foreground", children: item.description }))] })] }) }), _jsx(TableCell, { children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsxs("label", { className: "flex items-center gap-2 text-sm", children: [_jsx("input", { type: "radio", name: "actieve-werkwijze", className: "h-4 w-4 accent-primary", checked: !!item.is_active, disabled: saving, onChange: () => kiesWerkwijze(item), "aria-label": `${item.name ?? "Werkwijze"} als actieve werkwijze kiezen` }), _jsx("span", { className: "sr-only", children: "Actief" })] }), _jsx(Badge, { variant: item.is_active ? "default" : "secondary", children: item.is_active ? "Actief" : "Uit" }), item.is_active && (_jsx(Button, { size: "sm", variant: "ghost", disabled: saving, onClick: () => void clearActive(), children: "Uitzetten" }))] }) }), _jsx(TableCell, { className: "text-right", children: _jsxs("div", { className: "flex justify-end gap-1", children: [_jsx(Button, { size: "sm", variant: "outline", asChild: true, children: _jsxs(Link, { to: "/logistiek-engine/rule-engine", search: { strategy: String(item.id), naam: item.name ?? "" }, children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), "Instellen"] }) }), _jsx(Button, { size: "icon", variant: "ghost", onClick: () => openEdit(item), "aria-label": "Bewerken", children: _jsx(Pencil, { className: "h-4 w-4" }) }), _jsx(Button, { size: "icon", variant: "ghost", onClick: () => setDeleting(item), "aria-label": "Verwijderen", children: _jsx(Trash2, { className: "h-4 w-4" }) })] }) })] }, String(item.id)))) })] })) })] }), _jsx(StrategieDialog, { open: dialogOpen, onOpenChange: setDialogOpen, strategie: editing, saving: saving, onSubmit: (input) => (editing ? update(editing.id, input) : create(input)) }), _jsx(AlertDialog, { open: !!deleting, onOpenChange: (open) => !open && setDeleting(null), children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Werkwijze verwijderen?" }), _jsxs(AlertDialogDescription, { children: ["\"", deleting?.name, "\" wordt definitief verwijderd. Dit kan niet ongedaan worden gemaakt."] })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { children: "Annuleren" }), _jsx(AlertDialogAction, { onClick: async () => {
68
74
  if (deleting)
69
75
  await remove(deleting.id);
70
76
  setDeleting(null);
@@ -0,0 +1,2 @@
1
+ export declare function LogistiekEngineQueuePage(): import("react").JSX.Element;
2
+ //# sourceMappingURL=LogistiekEngineQueuePage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LogistiekEngineQueuePage.d.ts","sourceRoot":"","sources":["../../src/components/LogistiekEngineQueuePage.tsx"],"names":[],"mappings":"AASA,wBAAgB,wBAAwB,gCASvC"}
@@ -0,0 +1,11 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // ============================================================================
3
+ // src/components/LogistiekEngineQueuePage.tsx — Queue: de volledige orderflow
4
+ // ============================================================================
5
+ // Gebruikt hetzelfde orderlogboek als het overzicht, zodat er één weergave is.
6
+ // GEEN <Header /> hier — die levert de shell.
7
+ // ============================================================================
8
+ import { OrderLogboek } from "./OrderLogboek";
9
+ export function LogistiekEngineQueuePage() {
10
+ return (_jsx("div", { className: "space-y-4 p-4", children: _jsx(OrderLogboek, { titel: "Queue \u2014 volledige orderflow", omschrijving: "Per order precies wat de Logistiek Engine heeft gedaan, waarom, en wat er is gewijzigd. Alleen echt vastgelegde stappen; er wordt niets aangevuld." }) }));
11
+ }
@@ -0,0 +1,11 @@
1
+ import { type NodeTraceGroep } from "../lib/nodeTrace";
2
+ interface Props {
3
+ groep: NodeTraceGroep;
4
+ orderLabel: string | null;
5
+ debug: boolean;
6
+ onDebug: (aan: boolean) => void;
7
+ onClose: () => void;
8
+ }
9
+ export declare function NodeTracePanel({ groep, orderLabel, debug, onDebug, onClose }: Props): import("react").JSX.Element;
10
+ export {};
11
+ //# sourceMappingURL=NodeTracePanel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NodeTracePanel.d.ts","sourceRoot":"","sources":["../../src/components/NodeTracePanel.tsx"],"names":[],"mappings":"AAcA,OAAO,EAIL,KAAK,cAAc,EACpB,MAAM,kBAAkB,CAAC;AAa1B,UAAU,KAAK;IACb,KAAK,EAAE,cAAc,CAAC;IACtB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;IAChC,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AA+CD,wBAAgB,cAAc,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,KAAK,+BAyJnF"}
@@ -0,0 +1,40 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // ============================================================================
3
+ // src/components/NodeTracePanel.tsx — wat er in dit vakje met de order gebeurde
4
+ // ============================================================================
5
+ // Klik je op een vakje van de flowkaart, dan staat hier eerst een korte
6
+ // samenvatting van de besluitvorming, en daarna alleen de gebeurtenissen die
7
+ // bij dit vakje horen. Automatische database-regels worden gebundeld.
8
+ // Alleen lezen.
9
+ // ============================================================================
10
+ import { useMemo, useState } from "react";
11
+ import { Badge, Button } from "@flowselections/core";
12
+ import { ChevronDown, ChevronRight, X } from "lucide-react";
13
+ import { flowNodeLabel } from "../lib/flowNodes";
14
+ import { bundelGebeurtenissen, samenvatVak, } from "../lib/nodeTrace";
15
+ import { vakUitleg } from "../lib/vakUitleg";
16
+ import { FOUTSOORT_LABEL, FOUTSOORT_UITLEG, STATUS_KLEUR, Stap, Veld, duur, tijd, } from "./OrderTraceDetails";
17
+ const EERSTE_AANTAL = 20;
18
+ function Bundel({ item, debug }) {
19
+ const [open, setOpen] = useState(false);
20
+ const [toon, setToon] = useState(10);
21
+ const items = Array.isArray(item.events) ? item.events : [];
22
+ return (_jsxs("div", { className: "rounded-lg border bg-muted/30", children: [_jsxs("button", { type: "button", onClick: () => setOpen((v) => !v), className: "flex w-full items-start gap-3 p-3 text-left", children: [open ? (_jsx(ChevronDown, { className: "mt-1 h-4 w-4 shrink-0" })) : (_jsx(ChevronRight, { className: "mt-1 h-4 w-4 shrink-0" })), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("span", { className: "font-medium", children: item.label }), _jsxs(Badge, { variant: "outline", children: [item.aantal, "x"] })] }), _jsxs("p", { className: "mt-1 text-xs text-muted-foreground", children: ["Automatische gegevenswijziging \u00B7 laatste ", tijd(item.laatste)] })] })] }), open ? (_jsxs("div", { className: "space-y-2 border-t px-3 py-3", children: [items.slice(0, toon).map((e) => (_jsx(Stap, { e: e, debug: debug }, e.id))), items.length > toon ? (_jsxs(Button, { size: "sm", variant: "outline", onClick: () => setToon((n) => n + 25), children: ["Toon meer (", items.length - toon, ")"] })) : null] })) : null] }));
23
+ }
24
+ export function NodeTracePanel({ groep, orderLabel, debug, onDebug, onClose }) {
25
+ const events = Array.isArray(groep.events) ? groep.events : [];
26
+ const [toon, setToon] = useState(EERSTE_AANTAL);
27
+ const samenvatting = useMemo(() => samenvatVak(groep), [groep]);
28
+ const uitleg = useMemo(() => vakUitleg(groep), [groep]);
29
+ const regels = useMemo(() => (debug ? [] : bundelGebeurtenissen(events)), [events, debug]);
30
+ const ruw = useMemo(() => (debug ? [...events].sort((a, b) => (a.createdAt < b.createdAt ? 1 : -1)) : []), [events, debug]);
31
+ const zichtbaar = debug ? ruw.slice(0, toon) : regels.slice(0, toon);
32
+ const totaal = debug ? ruw.length : regels.length;
33
+ const calc = samenvatting.berekening;
34
+ return (_jsxs("div", { className: "pointer-events-auto absolute right-3 top-16 bottom-16 z-30 flex w-[26rem] max-w-[90%] flex-col rounded-xl border border-border bg-background shadow-xl", children: [_jsxs("div", { className: "flex items-start justify-between gap-2 border-b border-border px-3 py-2", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "truncate text-sm font-semibold", children: flowNodeLabel(groep.nodeId) }), _jsx("p", { className: "truncate text-xs text-muted-foreground", children: orderLabel ?? "Order" })] }), _jsx(Button, { variant: "ghost", size: "icon", "aria-label": "Sluiten", onClick: onClose, children: _jsx(X, { className: "h-4 w-4" }) })] }), _jsxs("div", { className: "min-h-0 flex-1 overflow-y-auto", children: [_jsxs("div", { className: "space-y-2 border-b border-border px-3 py-3", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx(Badge, { className: STATUS_KLEUR[groep.status] ?? "", children: groep.status }), samenvatting.errorClass ? (_jsx(Badge, { variant: "outline", children: FOUTSOORT_LABEL[samenvatting.errorClass] ?? samenvatting.errorClass })) : null] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Veld, { label: "Beslissing", children: uitleg.beslissing }), _jsx(Veld, { label: "Waarom", children: uitleg.waarom }), uitleg.feiten.length > 0 ? (_jsx(Veld, { label: "Bepalend", children: _jsx("span", { className: "block", children: uitleg.feiten.map((f) => (_jsx("span", { className: "block", children: f }, f))) }) })) : null, _jsx(Veld, { label: "Regel", children: uitleg.regel ?? samenvatting.regel }), _jsx(Veld, { label: "Hierna", children: samenvatting.volgendeStap }), uitleg.afgeleid ? (_jsx("p", { className: "text-xs text-muted-foreground", children: "Afgeleid uit de registraties: de Engine legde hier nog geen besluit met reden vast." })) : null] }), calc ? (_jsxs("div", { className: "rounded-md border bg-muted/40 p-2", children: [_jsx("p", { className: "text-sm font-medium", children: "Berekening" }), _jsx(Veld, { label: "Instelling", children: calc.setting }), _jsx(Veld, { label: "Rekenstap", children: calc.formula }), _jsx(Veld, { label: "Uitkomst", children: calc.result }), _jsx(Veld, { label: "Conclusie", children: calc.conclusion })] })) : null, samenvatting.fout ? (_jsxs("div", { className: "rounded-md border border-destructive/40 bg-destructive/5 p-2", children: [_jsx(Veld, { label: "Fout", children: samenvatting.fout }), _jsx(Veld, { label: "Uitleg", children: samenvatting.errorClass ? FOUTSOORT_UITLEG[samenvatting.errorClass] ?? null : null })] })) : null, _jsxs("div", { className: "space-y-1", children: [_jsx(Veld, { label: "Eerste", children: tijd(groep.eerste) }), _jsx(Veld, { label: "Laatste", children: tijd(groep.laatste) }), _jsx(Veld, { label: "Totale duur", children: groep.duurMs === null ? null : duur(groep.duurMs) }), _jsx(Veld, { label: "Pogingen", children: samenvatting.pogingen > 0 ? String(samenvatting.pogingen) : null }), _jsx(Veld, { label: "Volgende poging", children: samenvatting.volgendePoging ? tijd(samenvatting.volgendePoging) : null })] }), _jsxs("p", { className: "text-xs text-muted-foreground", children: [samenvatting.aantalBeslissingen, " beslismoment", samenvatting.aantalBeslissingen === 1 ? "" : "en", " van de Engine", samenvatting.aantalAutomatisch > 0
35
+ ? ` · ${samenvatting.aantalAutomatisch} automatische gegevenswijziging${samenvatting.aantalAutomatisch === 1 ? "" : "en"}`
36
+ : ""] })] }), _jsxs("div", { className: "flex items-center justify-between px-3 py-2", children: [_jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Stappen in dit vakje" }), _jsx(Button, { size: "sm", variant: debug ? "default" : "outline", onClick: () => {
37
+ setToon(EERSTE_AANTAL);
38
+ onDebug(!debug);
39
+ }, children: "Technische details" })] }), _jsxs("div", { className: "space-y-2 px-3 pb-3", children: [totaal === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Nog niets vastgelegd in dit vakje." })) : debug ? (zichtbaar.map((e) => _jsx(Stap, { e: e, debug: true }, e.id))) : (zichtbaar.map((item) => item.event ? (_jsx(Stap, { e: item.event, debug: false }, item.key)) : (_jsx(Bundel, { item: item, debug: false }, item.key)))), totaal > toon ? (_jsxs(Button, { size: "sm", variant: "outline", className: "w-full", onClick: () => setToon((n) => n + EERSTE_AANTAL), children: ["Toon meer (", totaal - toon, ")"] })) : null] })] })] }));
40
+ }
@@ -0,0 +1,9 @@
1
+ import type { NodeTraceGroep } from "../lib/nodeTrace";
2
+ interface Props {
3
+ reis: NodeTraceGroep[];
4
+ actiefNodeId: string | null;
5
+ onKies: (nodeId: string) => void;
6
+ }
7
+ export declare function OrderJourneyBar({ reis, actiefNodeId, onKies }: Props): import("react").JSX.Element | null;
8
+ export {};
9
+ //# sourceMappingURL=OrderJourneyBar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OrderJourneyBar.d.ts","sourceRoot":"","sources":["../../src/components/OrderJourneyBar.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGvD,UAAU,KAAK;IACb,IAAI,EAAE,cAAc,EAAE,CAAC;IACvB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CAClC;AAED,wBAAgB,eAAe,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,EAAE,KAAK,sCAsCpE"}
@@ -0,0 +1,26 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // ============================================================================
3
+ // src/components/OrderJourneyBar.tsx — de reis van de order als balk
4
+ // ============================================================================
5
+ // Elk vakje dat de order echt gepasseerd is, op volgorde van tijd. Klikken
6
+ // opent datzelfde vakje op de kaart. Alleen lezen.
7
+ // ============================================================================
8
+ import { AlertTriangle, CheckCircle2, ChevronRight, Clock } from "lucide-react";
9
+ import { flowNodeLabel } from "../lib/flowNodes";
10
+ import { duur, klok } from "./OrderTraceDetails";
11
+ export function OrderJourneyBar({ reis, actiefNodeId, onKies }) {
12
+ const veilig = Array.isArray(reis) ? reis : [];
13
+ if (veilig.length === 0)
14
+ return null;
15
+ return (_jsxs("div", { className: "flex flex-wrap items-center gap-1 rounded-lg border border-border bg-background px-2 py-2", children: [_jsx("span", { className: "mr-1 text-xs font-medium text-muted-foreground", children: "Reis van de order:" }), veilig.map((g, i) => {
16
+ const fout = g.status === "mislukt";
17
+ const wacht = g.status === "wachtend" || g.status === "waarschuwing";
18
+ const Icoon = fout ? AlertTriangle : wacht ? Clock : CheckCircle2;
19
+ const actief = g.nodeId === actiefNodeId;
20
+ return (_jsxs("span", { className: "flex items-center gap-1", children: [i > 0 && _jsx(ChevronRight, { className: "h-3 w-3 text-muted-foreground" }), _jsxs("button", { type: "button", onClick: () => onKies(g.nodeId), className: `flex items-center gap-1 rounded-md border px-2 py-1 text-[11px] ${fout
21
+ ? "border-destructive/50 bg-destructive/10 text-destructive"
22
+ : wacht
23
+ ? "border-amber-500/50 bg-amber-500/10 text-amber-700 dark:text-amber-300"
24
+ : "border-border bg-muted/40 text-foreground"} ${actief ? "ring-2 ring-primary" : ""}`, title: g.reden ?? undefined, children: [_jsx(Icoon, { className: "h-3 w-3" }), _jsx("span", { className: "max-w-[10rem] truncate font-medium", children: flowNodeLabel(g.nodeId) }), _jsx("span", { className: "text-muted-foreground", children: klok(g.eerste) }), g.duurMs !== null && _jsxs("span", { className: "text-muted-foreground", children: ["\u00B7 ", duur(g.duurMs)] }), g.aantal > 1 && _jsxs("span", { className: "text-muted-foreground", children: ["\u00B7 ", g.aantal, "\u00D7"] })] })] }, g.nodeId));
25
+ })] }));
26
+ }
@@ -0,0 +1,10 @@
1
+ import { type TraceEventRow } from "../hooks/useLogisticsTrace";
2
+ export declare function Gebeurtenis({ event }: {
3
+ event: TraceEventRow;
4
+ }): import("react").JSX.Element;
5
+ export interface OrderLogboekProps {
6
+ titel?: string;
7
+ omschrijving?: string;
8
+ }
9
+ export declare function OrderLogboek({ titel, omschrijving, }: OrderLogboekProps): import("react").JSX.Element;
10
+ //# sourceMappingURL=OrderLogboek.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OrderLogboek.d.ts","sourceRoot":"","sources":["../../src/components/OrderLogboek.tsx"],"names":[],"mappings":"AAgCA,OAAO,EAGL,KAAK,aAAa,EACnB,MAAM,4BAA4B,CAAC;AAsDpC,wBAAgB,WAAW,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,aAAa,CAAA;CAAE,+BAoG9D;AAyBD,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,YAAY,CAAC,EAC3B,KAA8B,EAC9B,YAAsI,GACvI,EAAE,iBAAiB,+BAmGnB"}