@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
package/README.md CHANGED
@@ -1,24 +1,24 @@
1
- # logistiek Engine
2
-
3
- blank project start
4
-
5
- This project was built with [Lovable](https://lovable.dev).
6
-
7
- ## Build with Lovable
8
-
9
- Continue developing this project in the [Lovable editor](https://lovable.dev/projects/316ec9c5-0f01-408a-b7ec-3d4dccf9b65d).
10
-
11
- - **Ship faster**: describe what you want to build and Lovable handles the code.
12
- - **Stay in sync**: every change made in Lovable is committed straight to this repository.
13
- - **Full ownership**: this code is yours. Push to `main` on GitHub and your changes sync back into Lovable, ready for your next prompt.
14
-
15
- ## Development
16
-
17
- Prefer working locally? You need Node.js and npm — [install with nvm](https://github.com/nvm-sh/nvm#installing-and-updating).
18
-
19
- ```sh
20
- git clone <this-repository-url>
21
- cd <repository-name>
22
- npm i
23
- npm run dev
24
- ```
1
+ # logistiek Engine
2
+
3
+ blank project start
4
+
5
+ This project was built with [Lovable](https://lovable.dev).
6
+
7
+ ## Build with Lovable
8
+
9
+ Continue developing this project in the [Lovable editor](https://lovable.dev/projects/316ec9c5-0f01-408a-b7ec-3d4dccf9b65d).
10
+
11
+ - **Ship faster**: describe what you want to build and Lovable handles the code.
12
+ - **Stay in sync**: every change made in Lovable is committed straight to this repository.
13
+ - **Full ownership**: this code is yours. Push to `main` on GitHub and your changes sync back into Lovable, ready for your next prompt.
14
+
15
+ ## Development
16
+
17
+ Prefer working locally? You need Node.js and npm — [install with nvm](https://github.com/nvm-sh/nvm#installing-and-updating).
18
+
19
+ ```sh
20
+ git clone <this-repository-url>
21
+ cd <repository-name>
22
+ npm i
23
+ npm run dev
24
+ ```
@@ -0,0 +1,28 @@
1
+ import "@xyflow/react/dist/style.css";
2
+ import { type FlowTree } from "../lib/flowTree";
3
+ import type { RouteMarker } from "../lib/flowRoute";
4
+ import type { NodeTraceGroep } from "../lib/nodeTrace";
5
+ import type { RuleEngineSettings } from "../lib/ruleEngineSchema";
6
+ export type { RouteMarker };
7
+ interface Props {
8
+ tree: FlowTree;
9
+ selectedStepId: string | null;
10
+ /** Alle waarden van de werkwijze, zodat een vakje zijn eigen velden kan tonen. */
11
+ settings: RuleEngineSettings;
12
+ /** Markeringen per knoop-id (vastgelopen / bezig / klaar) van een gekozen orderversie. */
13
+ markers?: Map<string, RouteMarker>;
14
+ /** Wat de Engine per vakje voor de gekozen order heeft vastgelegd. */
15
+ nodeTrace?: Map<string, NodeTraceGroep> | null;
16
+ /** Tekst boven de kaart als een orderroute getoond wordt. */
17
+ routeLabel?: string | null;
18
+ /** Korte samenvatting van de gelopen route (waar hij nu staat of vastliep). */
19
+ routeSummary?: string | null;
20
+ /** Vakje waarvan het tracepaneel openstaat. */
21
+ traceNodeId?: string | null;
22
+ onTraceNode?: (nodeId: string | null) => void;
23
+ onClearRoute?: () => void;
24
+ onSelectStep: (stepId: string) => void;
25
+ onChangeChoice: (storeId: string, fieldKey: string, value: unknown) => void;
26
+ }
27
+ export declare function FlowCanvas(props: Props): import("react").JSX.Element;
28
+ //# sourceMappingURL=FlowCanvas.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FlowCanvas.d.ts","sourceRoot":"","sources":["../../src/components/FlowCanvas.tsx"],"names":[],"mappings":"AAgDA,OAAO,8BAA8B,CAAC;AAEtC,OAAO,EAA6B,KAAK,QAAQ,EAAiB,MAAM,iBAAiB,CAAC;AAC1F,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAOlE,YAAY,EAAE,WAAW,EAAE,CAAC;AAE5B,UAAU,KAAK;IACb,IAAI,EAAE,QAAQ,CAAC;IACf,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,kFAAkF;IAClF,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,0FAA0F;IAC1F,OAAO,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACnC,sEAAsE;IACtE,SAAS,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,GAAG,IAAI,CAAC;IAC/C,6DAA6D;IAC7D,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,+EAA+E;IAC/E,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAC9C,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAE1B,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,cAAc,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CAC7E;AA87BD,wBAAgB,UAAU,CAAC,KAAK,EAAE,KAAK,+BAMtC"}
@@ -0,0 +1,429 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // ============================================================================
3
+ // src/components/FlowCanvas.tsx — vertakkende kaart van de Logistiek Engine
4
+ // ============================================================================
5
+ // Tekent de beslisboom uit buildFlowTree() als node-based flow (React Flow).
6
+ // Elke beslissing laat al haar uitkomsten zien; de actieve route is gekleurd,
7
+ // alternatieve takken lichter en onbereikbare takken grijs/gestippeld. Keuzes
8
+ // in een beslistegel schrijven direct naar dezelfde werkwijze als de engine.
9
+ // ============================================================================
10
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
11
+ import { BaseEdge, Background, BackgroundVariant, EdgeLabelRenderer, Handle, MiniMap, Position, ReactFlow, ReactFlowProvider, getSmoothStepPath, useReactFlow, } from "@xyflow/react";
12
+ import dagre from "@dagrejs/dagre";
13
+ import { Button } from "@flowselections/core";
14
+ import { AlertTriangle, ArrowDownUp, ArrowLeftRight, CheckCircle2, ChevronDown, ChevronRight, Clock, Maximize2, Minimize2, Expand, ListChecks, Pencil, Settings2, Minus, Plus, X, } from "lucide-react";
15
+ import "@xyflow/react/dist/style.css";
16
+ import { fieldsFor } from "../lib/flowTree";
17
+ import { RuleFieldInput } from "./rule-engine/RuleFieldInput";
18
+ import { NodeTracePanel } from "./NodeTracePanel";
19
+ import { duur as duurTekst, klok } from "./OrderTraceDetails";
20
+ // ── Toestand → uiterlijk ────────────────────────────────────────────────────
21
+ /** Extra opmaak voor vakjes op (of juist buiten) de gelopen orderroute. */
22
+ function routeClass(node) {
23
+ if (node.route === true)
24
+ return " ring-2 ring-primary ring-offset-1";
25
+ if (node.route === false)
26
+ return " opacity-40";
27
+ return "";
28
+ }
29
+ const NODE_STATE_CLASS = {
30
+ active: "border-primary bg-background shadow-md",
31
+ possible: "border-border bg-background",
32
+ disabled: "border-dashed border-border bg-muted text-muted-foreground",
33
+ };
34
+ // ── Lay-out ─────────────────────────────────────────────────────────────────
35
+ function layout(tree, collapsed, richting) {
36
+ const nodes = Array.isArray(tree?.nodes) ? tree.nodes : [];
37
+ const edges = Array.isArray(tree?.edges) ? tree.edges : [];
38
+ const verborgen = (ancestors) => ancestors.some((a) => collapsed.has(a));
39
+ const zichtbaar = nodes.filter((n) => !verborgen(n.ancestors));
40
+ const zichtbaarIds = new Set(zichtbaar.map((n) => n.id));
41
+ const zichtbareEdges = edges.filter((e) => zichtbaarIds.has(e.from) && zichtbaarIds.has(e.to) && !verborgen(e.ancestors));
42
+ // Ingeklapte beslissingen krijgen één samenvattende pijl naar hun samenkomst.
43
+ const extra = [];
44
+ for (const id of collapsed) {
45
+ const dec = nodes.find((n) => n.id === id);
46
+ if (!dec?.mergeId || !zichtbaarIds.has(id))
47
+ continue;
48
+ const aantal = nodes.filter((n) => n.ancestors.includes(id) && n.kind !== "merge").length;
49
+ extra.push({
50
+ id: `collapsed:${id}`,
51
+ from: id,
52
+ to: dec.mergeId,
53
+ label: `+${aantal} tegels`,
54
+ state: dec.state,
55
+ kind: "flow",
56
+ ancestors: [],
57
+ });
58
+ }
59
+ const g = new dagre.graphlib.Graph();
60
+ g.setGraph({
61
+ rankdir: richting,
62
+ nodesep: richting === "TB" ? 72 : 56,
63
+ ranksep: richting === "TB" ? 96 : 130,
64
+ marginx: 40,
65
+ marginy: 40,
66
+ });
67
+ g.setDefaultEdgeLabel(() => ({}));
68
+ for (const n of zichtbaar)
69
+ g.setNode(n.id, { width: n.w, height: n.h });
70
+ const alle = [...zichtbareEdges, ...extra];
71
+ for (const e of alle) {
72
+ // Verwijspijlen tellen niet mee voor de rangorde (anders trekt dagre stappen scheef).
73
+ if (e.kind === "ref")
74
+ continue;
75
+ g.setEdge(e.from, e.to);
76
+ }
77
+ dagre.layout(g);
78
+ const posities = new Map();
79
+ for (const n of zichtbaar) {
80
+ const p = g.node(n.id);
81
+ if (p)
82
+ posities.set(n.id, { x: p.x - n.w / 2, y: p.y - n.h / 2 });
83
+ }
84
+ return { zichtbaar, edges: alle, posities };
85
+ }
86
+ // ── Knopen ──────────────────────────────────────────────────────────────────
87
+ function handles(richting) {
88
+ const inPos = richting === "TB" ? Position.Top : Position.Left;
89
+ const outPos = richting === "TB" ? Position.Bottom : Position.Right;
90
+ return (_jsxs(_Fragment, { children: [_jsx(Handle, { type: "target", position: inPos, className: "!h-1.5 !w-1.5 !border-0 !bg-transparent" }), _jsx(Handle, { type: "source", position: outPos, className: "!h-1.5 !w-1.5 !border-0 !bg-transparent" })] }));
91
+ }
92
+ function VraagTeken({ onClick }) {
93
+ return (_jsx("button", { type: "button", "aria-label": "Uitleg over dit vakje", title: "Uitleg over dit vakje", className: "nodrag nopan flex h-5 w-5 shrink-0 items-center justify-center rounded-full border border-muted-foreground/60 text-[11px] font-semibold text-muted-foreground hover:bg-primary/10", onClick: (e) => {
94
+ e.stopPropagation();
95
+ onClick();
96
+ }, children: "?" }));
97
+ }
98
+ /** Zwevend paneeltje op het vakje zelf: dezelfde velden als in het zijpaneel. */
99
+ function VeldBewerker({ data }) {
100
+ const { node, settings, onChangeChoice, onBewerk } = data;
101
+ const velden = fieldsFor(node.storeId, node.fieldKeys);
102
+ const waarden = (node.storeId ? settings?.[node.storeId]?.fields : undefined) ?? {};
103
+ if (!velden.length || !node.storeId)
104
+ return null;
105
+ return (_jsxs("div", { className: "nodrag nopan nowheel absolute left-0 top-full z-50 mt-2 w-[22rem] max-w-[90vw] space-y-3 rounded-xl border border-primary bg-background p-3 shadow-xl", onClick: (e) => e.stopPropagation(), onDoubleClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), children: [_jsxs("div", { className: "flex items-start justify-between gap-2", children: [_jsxs("p", { className: "text-xs font-semibold text-foreground", children: [node.label, " aanpassen"] }), _jsx("button", { type: "button", "aria-label": "Bewerken sluiten", className: "rounded text-muted-foreground hover:text-foreground", onClick: () => onBewerk(null), children: _jsx(X, { className: "h-4 w-4" }) })] }), _jsx("div", { className: "max-h-[22rem] space-y-3 overflow-y-auto pr-1", children: velden.map((field) => (_jsx(RuleFieldInput, { field: field, value: waarden[field.key], stepValues: waarden, onChange: (waarde) => onChangeChoice(node.storeId, field.key, waarde) }, field.key))) }), _jsx("p", { className: "text-[10px] text-muted-foreground", children: "Wijzigingen gaan naar dezelfde werkwijze als de engine. Vergeet niet bovenaan op te slaan." })] }));
106
+ }
107
+ /** Kleine hint dat je hier kunt dubbelklikken. */
108
+ function BewerkHint() {
109
+ return (_jsxs("span", { className: "pointer-events-none absolute bottom-0.5 right-1.5 flex items-center gap-1 text-[9px] text-muted-foreground", children: [_jsx(Pencil, { className: "h-2.5 w-2.5" }), " dubbelklik"] }));
110
+ }
111
+ /**
112
+ * BPMN-achtige aanduiding linksboven in een vakje: een taak-icoon bij stappen
113
+ * en instellingen, zodat de notatie herkenbaar is voor wie BPMN gewend is.
114
+ */
115
+ function TaakTeken({ soort, actief }) {
116
+ const Icoon = soort === "taak" ? Settings2 : ListChecks;
117
+ return (_jsx("span", { className: `pointer-events-none absolute left-1 top-1 flex h-4 w-4 items-center justify-center rounded-sm border ${actief ? "border-primary/60 text-primary" : "border-border text-muted-foreground"}`, title: soort === "taak" ? "Taak (BPMN)" : "Regels / instellingen", children: _jsx(Icoon, { className: "h-2.5 w-2.5" }) }));
118
+ }
119
+ /** BPMN-gateway: geruit vakje met een kruis, bij elk beslismoment. */
120
+ function GatewayTeken({ actief }) {
121
+ return (_jsx("span", { className: `pointer-events-none absolute -left-3 top-1/2 flex h-6 w-6 -translate-y-1/2 rotate-45 items-center justify-center border-2 bg-background ${actief ? "border-primary text-primary" : "border-muted-foreground/60 text-muted-foreground"}`, title: "Beslismoment (BPMN-gateway)", children: _jsx(X, { className: "h-3 w-3 -rotate-45" }) }));
122
+ }
123
+ function TerminalNode({ data }) {
124
+ const { node, richting, marker } = data;
125
+ const inactief = node.state !== "active";
126
+ return (_jsxs("div", { className: `relative flex items-center justify-between gap-2 rounded-full px-4 text-center ${node.kind === "end" ? "border-4" : "border-2"} ${inactief ? "border-border bg-background" : "border-primary bg-primary/10"}`, title: node.kind === "end" ? "Eindgebeurtenis (BPMN)" : "Startgebeurtenis (BPMN)", style: { width: node.w, height: node.h }, children: [_jsx(Markering, { marker: marker, onClick: data.onTrace ? () => data.onTrace?.(node.id) : undefined }), _jsxs("div", { className: "flex-1", children: [_jsx("p", { className: "text-[14px] font-semibold text-foreground", children: node.label }), node.sublabel && _jsx("p", { className: "text-[11px] text-muted-foreground", children: node.sublabel })] }), _jsx(VraagTeken, { onClick: () => data.onHelp({ titel: node.label, regels: node.help }) }), handles(richting)] }));
127
+ }
128
+ function SourceNode({ data }) {
129
+ const { node, richting } = data;
130
+ return (_jsxs("div", { className: "relative flex items-center justify-center", style: { width: node.w, height: node.h }, children: [_jsx("div", { className: "absolute inset-0 border-2 border-primary/70 bg-background", style: { clipPath: "polygon(50% 0, 100% 50%, 50% 100%, 0 50%)" } }), _jsxs("div", { className: "relative z-10 flex items-center gap-2 px-10 text-center", children: [_jsxs("div", { children: [_jsx("p", { className: "text-[12px] font-semibold leading-tight text-foreground", children: node.label }), _jsx("p", { className: "text-[10px] text-muted-foreground", children: node.sublabel })] }), _jsx(VraagTeken, { onClick: () => data.onHelp({ titel: node.label, regels: node.help }) })] }), handles(richting)] }));
131
+ }
132
+ /**
133
+ * Strook onder een vakje: wat er in dit vakje met de gekozen order gebeurde.
134
+ * Klikken opent het paneel met alle vastgelegde stappen van dat vakje.
135
+ */
136
+ function Markering({ marker, onClick }) {
137
+ if (!marker)
138
+ return null;
139
+ const kleur = marker.kind === "stuck"
140
+ ? "border-destructive bg-destructive text-destructive-foreground"
141
+ : marker.kind === "done"
142
+ ? "border-primary bg-primary text-primary-foreground"
143
+ : "border-amber-500 bg-amber-500 text-white";
144
+ const Icoon = marker.kind === "stuck" ? AlertTriangle : marker.kind === "done" ? CheckCircle2 : Clock;
145
+ const feiten = Array.isArray(marker.feiten) ? marker.feiten.slice(0, 3) : [];
146
+ const details = [
147
+ marker.at ? klok(marker.at) : null,
148
+ marker.durationMs !== null && marker.durationMs !== undefined ? duurTekst(marker.durationMs) : null,
149
+ marker.count && marker.count > 1 ? `${marker.count} gebeurtenissen` : null,
150
+ marker.attempts && marker.attempts > 1 ? `poging ${marker.attempts}` : null,
151
+ ].filter(Boolean);
152
+ return (_jsxs("button", { type: "button", className: `nodrag nopan absolute -bottom-6 left-3 right-3 z-20 flex items-start gap-1 rounded-md border px-2 py-1 text-left text-[10px] font-medium leading-tight shadow ${kleur} ${onClick ? "cursor-pointer hover:brightness-110" : "cursor-default"}`, title: onClick ? "Klik voor alles wat hier is vastgelegd" : marker.text, onClick: (e) => {
153
+ e.stopPropagation();
154
+ onClick?.();
155
+ }, children: [_jsx(Icoon, { className: "mt-0.5 h-3 w-3 shrink-0" }), _jsxs("span", { className: "min-w-0", children: [_jsxs("span", { className: "line-clamp-2 block", children: [marker.kind === "stuck" ? "Vastgelopen: " : "Beslissing: ", marker.beslissing ?? marker.text] }), marker.waarom ? (_jsxs("span", { className: "line-clamp-2 block font-normal opacity-95", children: ["Waarom: ", marker.waarom] })) : null, feiten.length > 0 && _jsx("span", { className: "line-clamp-2 block font-normal opacity-90", children: feiten.join(" · ") }), details.length > 0 && _jsx("span", { className: "block opacity-90", children: details.join(" · ") })] })] }));
156
+ }
157
+ function StepNode({ data }) {
158
+ const { node, richting, geselecteerd, marker, traceActief } = data;
159
+ const inactief = node.state !== "active";
160
+ return (_jsxs("div", { className: `relative flex cursor-pointer items-start justify-between gap-2 rounded-xl border-2 px-4 py-3 ${marker?.kind === "stuck"
161
+ ? "border-destructive bg-destructive/10 shadow-md ring-2 ring-destructive/40"
162
+ : traceActief
163
+ ? "border-primary bg-primary/10 shadow-md ring-2 ring-primary"
164
+ : geselecteerd
165
+ ? "border-primary bg-primary/10 shadow-md"
166
+ : inactief
167
+ ? "border-border bg-background hover:border-primary"
168
+ : "border-primary/50 bg-background hover:border-primary"}`, style: { width: node.w, height: node.h }, onClick: () => {
169
+ if (marker && data.onTrace)
170
+ data.onTrace(node.id);
171
+ else if (node.stepId)
172
+ data.onSelectStep(node.stepId);
173
+ }, children: [_jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "text-[15px] font-semibold text-foreground", children: node.label }), _jsx("p", { className: "line-clamp-2 text-[11px] leading-snug text-muted-foreground", children: node.sublabel })] }), _jsx(TaakTeken, { soort: "taak", actief: !inactief }), _jsx(VraagTeken, { onClick: () => data.onHelp({ titel: node.label, regels: node.help }) }), _jsx(Markering, { marker: marker, onClick: data.onTrace ? () => data.onTrace?.(node.id) : undefined }), handles(richting)] }));
174
+ }
175
+ function SettingNode({ data }) {
176
+ const { node, richting, bewerken } = data;
177
+ return (_jsxs("div", { className: `relative flex cursor-pointer items-start justify-between gap-2 rounded-lg border px-3 py-2 ${NODE_STATE_CLASS[node.state]}${routeClass(node)} ${bewerken ? "ring-2 ring-primary" : ""}`, style: { width: node.w, height: node.h }, onClick: () => node.stepId && data.onSelectStep(node.stepId), onDoubleClick: (e) => {
178
+ e.stopPropagation();
179
+ data.onBewerk(bewerken ? null : node.id);
180
+ }, title: "Klik voor het zijpaneel \u00B7 dubbelklik om hier aan te passen", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "truncate text-[11px] font-semibold leading-tight text-foreground", children: node.label }), Array.isArray(node.lines) && node.lines.length ? (_jsxs("ul", { className: "mt-1 space-y-0.5", children: [node.lines.slice(0, 6).map((l) => (_jsx("li", { className: "truncate text-[10px] leading-[16px] text-muted-foreground", children: l }, l))), node.lines.length > 6 && (_jsxs("li", { className: "text-[10px] text-muted-foreground", children: ["+", node.lines.length - 6, " meer\u2026"] }))] })) : (_jsx("p", { className: "truncate text-[11px] text-muted-foreground", children: node.sublabel }))] }), _jsx(TaakTeken, { soort: "regel", actief: node.state === "active" }), _jsx(VraagTeken, { onClick: () => data.onHelp({ titel: node.label, regels: node.help }) }), _jsx(BewerkHint, {}), bewerken && _jsx(VeldBewerker, { data: data }), handles(richting)] }));
181
+ }
182
+ function DecisionNode({ data }) {
183
+ const { node, richting, ingeklapt, bewerken } = data;
184
+ const options = Array.isArray(node.options) ? node.options : [];
185
+ return (_jsxs("div", { className: `relative flex flex-col justify-between rounded-2xl border-2 px-3 py-2 ${NODE_STATE_CLASS[node.state]}${routeClass(node)} ${bewerken ? "ring-2 ring-primary" : ""}`, style: { width: node.w, height: node.h }, onDoubleClick: (e) => {
186
+ e.stopPropagation();
187
+ data.onBewerk(bewerken ? null : node.id);
188
+ }, title: "Beslismoment \u00B7 dubbelklik om deze instelling hier aan te passen", children: [_jsx(GatewayTeken, { actief: node.state === "active" }), _jsxs("div", { className: "flex items-start justify-between gap-2", children: [_jsx("button", { type: "button", className: "nodrag nopan mt-0.5 shrink-0 rounded text-muted-foreground hover:text-foreground", "aria-label": ingeklapt ? "Tak uitklappen" : "Tak inklappen", title: ingeklapt ? "Tak uitklappen" : "Tak inklappen", onClick: (e) => {
189
+ e.stopPropagation();
190
+ data.onToggleCollapse(node.id);
191
+ }, children: ingeklapt ? _jsx(ChevronRight, { className: "h-4 w-4" }) : _jsx(ChevronDown, { className: "h-4 w-4" }) }), _jsxs("div", { className: "min-w-0 flex-1 text-center", children: [_jsx("p", { className: "line-clamp-2 text-[12px] font-semibold leading-tight text-foreground", children: node.label }), _jsx("p", { className: "truncate text-[10px] text-muted-foreground", children: node.sublabel })] }), _jsx(VraagTeken, { onClick: () => data.onHelp({ titel: node.label, regels: node.help }) })] }), _jsx("select", { className: "nodrag nopan w-full rounded-md border border-border bg-background px-2 py-1 text-[12px] text-foreground", value: node.currentValue ?? "", onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), onDoubleClick: (e) => {
192
+ e.stopPropagation();
193
+ data.onBewerk(bewerken ? null : node.id);
194
+ }, onChange: (e) => {
195
+ const raw = e.target.value;
196
+ const waarde = node.isBoolean ? raw === "true" : raw;
197
+ if (node.storeId && node.fieldKey)
198
+ data.onChangeChoice(node.storeId, node.fieldKey, waarde);
199
+ }, children: options.map((o) => (_jsx("option", { value: o.value, children: o.label }, o.value))) }), bewerken && _jsx(VeldBewerker, { data: data }), handles(richting)] }));
200
+ }
201
+ function MergeNode({ data }) {
202
+ const { node, richting } = data;
203
+ return (_jsx("div", { className: `rotate-45 border-2 ${node.state === "active" ? "border-primary bg-primary/20" : "border-muted-foreground/50 bg-background"}`, style: { width: node.w, height: node.h }, title: "Samenkomst (BPMN-gateway)", children: handles(richting) }));
204
+ }
205
+ function OptionNode({ data }) {
206
+ const { node, richting } = data;
207
+ return (_jsxs("div", { className: `flex items-center justify-center rounded-full border px-3 text-[11px] font-medium ${node.state === "active"
208
+ ? "border-primary bg-primary/10 text-primary"
209
+ : node.state === "possible"
210
+ ? "border-border bg-background text-muted-foreground"
211
+ : "border-dashed border-border bg-muted text-muted-foreground"}`, style: { width: node.w, height: node.h }, title: node.help.join(" "), children: [_jsx("span", { className: "truncate", children: node.label }), handles(richting)] }));
212
+ }
213
+ const NODE_TYPES = {
214
+ start: TerminalNode,
215
+ end: TerminalNode,
216
+ source: SourceNode,
217
+ step: StepNode,
218
+ decision: DecisionNode,
219
+ setting: SettingNode,
220
+ option: OptionNode,
221
+ merge: MergeNode,
222
+ };
223
+ // ── Pijlen ──────────────────────────────────────────────────────────────────
224
+ function StateEdge(props) {
225
+ const { sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition, data, markerEnd } = props;
226
+ const state = data?.state ?? "possible";
227
+ const isRef = data?.kind === "ref";
228
+ const [path, labelX, labelY] = getSmoothStepPath({
229
+ sourceX,
230
+ sourceY,
231
+ targetX,
232
+ targetY,
233
+ sourcePosition,
234
+ targetPosition,
235
+ borderRadius: 10,
236
+ });
237
+ const dashed = isRef || state === "disabled";
238
+ const opRoute = data?.route === true;
239
+ return (_jsxs(_Fragment, { children: [_jsx(BaseEdge, { path: path, markerEnd: markerEnd, style: {
240
+ stroke: opRoute || state === "active" ? "var(--primary)" : "var(--muted-foreground)",
241
+ strokeOpacity: opRoute || state === "active" ? 1 : state === "disabled" ? 0.35 : 0.55,
242
+ strokeWidth: opRoute ? 5 : state === "active" ? 2.5 : 1.6,
243
+ strokeDasharray: dashed && !opRoute ? (isRef ? "4 4" : "5 5") : undefined,
244
+ } }), data?.label && (_jsx(EdgeLabelRenderer, { children: _jsx("div", { className: `pointer-events-none absolute rounded-md border px-1.5 py-0.5 text-[11px] font-medium shadow-sm ${state === "active"
245
+ ? "border-primary/40 bg-background text-primary"
246
+ : state === "possible"
247
+ ? "border-muted-foreground/40 bg-background text-muted-foreground"
248
+ : "border-dashed border-muted-foreground/30 bg-background text-muted-foreground"}`, style: { transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)` }, children: isRef ? `hangt af van: ${data.label}` : data.label }) }))] }));
249
+ }
250
+ const EDGE_TYPES = { state: StateEdge };
251
+ // ── Canvas ──────────────────────────────────────────────────────────────────
252
+ function Binnen({ tree, selectedStepId, settings, markers, nodeTrace, routeLabel, routeSummary, traceNodeId, onTraceNode, onClearRoute, onSelectStep, onChangeChoice, }) {
253
+ const [debug, setDebug] = useState(false);
254
+ const [bewerktId, setBewerktId] = useState(null);
255
+ const [uitleg, setUitleg] = useState(null);
256
+ const [collapsed, setCollapsed] = useState(new Set());
257
+ const [richting, setRichting] = useState("TB");
258
+ const [groot, setGroot] = useState(false);
259
+ // Linkermuisknop ingedrukt houden en scrollen = in-/uitzoomen.
260
+ const [muisIngedrukt, setMuisIngedrukt] = useState(false);
261
+ const muisIngedruktRef = useRef(false);
262
+ const canvasRef = useRef(null);
263
+ const { fitView, getViewport, setViewport, zoomIn, zoomOut } = useReactFlow();
264
+ // Escape sluit eerst het bewerkpaneel, daarna het grote scherm.
265
+ useEffect(() => {
266
+ if (!bewerktId && !groot)
267
+ return;
268
+ const opToets = (e) => {
269
+ if (e.key !== "Escape")
270
+ return;
271
+ if (bewerktId)
272
+ setBewerktId(null);
273
+ else
274
+ setGroot(false);
275
+ };
276
+ window.addEventListener("keydown", opToets);
277
+ return () => window.removeEventListener("keydown", opToets);
278
+ }, [bewerktId, groot]);
279
+ // Na het wisselen naar/van groot scherm het schema passend maken.
280
+ useEffect(() => {
281
+ const t = setTimeout(() => void fitView({ padding: 0.1, duration: 300 }), 80);
282
+ return () => clearTimeout(t);
283
+ }, [groot, fitView]);
284
+ useEffect(() => {
285
+ const laatLos = () => {
286
+ muisIngedruktRef.current = false;
287
+ setMuisIngedrukt(false);
288
+ };
289
+ window.addEventListener("mouseup", laatLos);
290
+ window.addEventListener("blur", laatLos);
291
+ return () => {
292
+ window.removeEventListener("mouseup", laatLos);
293
+ window.removeEventListener("blur", laatLos);
294
+ };
295
+ }, []);
296
+ useEffect(() => {
297
+ const el = canvasRef.current;
298
+ if (!el)
299
+ return;
300
+ const zoomTijdensVasthouden = (event) => {
301
+ if (!muisIngedruktRef.current)
302
+ return;
303
+ event.preventDefault();
304
+ event.stopPropagation();
305
+ const rect = el.getBoundingClientRect();
306
+ const px = event.clientX - rect.left;
307
+ const py = event.clientY - rect.top;
308
+ const viewport = getViewport();
309
+ const factor = event.deltaMode === 1 ? 16 : event.deltaMode === 2 ? 100 : 1;
310
+ const delta = event.deltaY * factor;
311
+ const zoom = Math.min(2, Math.max(0.05, viewport.zoom * Math.exp(-delta * 0.0015)));
312
+ const verhouding = zoom / viewport.zoom;
313
+ void setViewport({
314
+ x: px - (px - viewport.x) * verhouding,
315
+ y: py - (py - viewport.y) * verhouding,
316
+ zoom,
317
+ }, { duration: 0 });
318
+ };
319
+ el.addEventListener("wheel", zoomTijdensVasthouden, { passive: false, capture: true });
320
+ return () => el.removeEventListener("wheel", zoomTijdensVasthouden, { capture: true });
321
+ }, [getViewport, setViewport]);
322
+ const toggleCollapse = useCallback((id) => {
323
+ setCollapsed((prev) => {
324
+ const next = new Set(prev);
325
+ if (next.has(id))
326
+ next.delete(id);
327
+ else
328
+ next.add(id);
329
+ return next;
330
+ });
331
+ }, []);
332
+ const { zichtbaar, edges, posities } = useMemo(() => layout(tree, collapsed, richting), [tree, collapsed, richting]);
333
+ const rfNodes = useMemo(() => zichtbaar.map((node) => ({
334
+ id: node.id,
335
+ type: node.kind,
336
+ position: posities.get(node.id) ?? { x: 0, y: 0 },
337
+ draggable: false,
338
+ // Selecteerbaar houden: anders zet React Flow pointer-events uit en
339
+ // zijn knoppen en keuzelijsten in het vakje niet klikbaar.
340
+ selectable: true,
341
+ // Het bewerkpaneel moet over andere vakjes heen komen te liggen.
342
+ // Vakjes altijd boven de lijnen, zodat tekst leesbaar blijft.
343
+ zIndex: bewerktId === node.id ? 1000 : node.kind === "merge" || node.kind === "option" ? 5 : 10,
344
+ data: {
345
+ node,
346
+ richting,
347
+ geselecteerd: !!node.stepId && node.kind === "step" && node.stepId === selectedStepId,
348
+ ingeklapt: collapsed.has(node.id),
349
+ bewerken: bewerktId === node.id,
350
+ marker: markers?.get(node.id),
351
+ traceActief: traceNodeId === node.id,
352
+ settings,
353
+ onSelectStep,
354
+ onChangeChoice,
355
+ onHelp: setUitleg,
356
+ onToggleCollapse: toggleCollapse,
357
+ onBewerk: setBewerktId,
358
+ onTrace: onTraceNode ? (id) => onTraceNode(id) : undefined,
359
+ },
360
+ })), [
361
+ zichtbaar,
362
+ posities,
363
+ richting,
364
+ selectedStepId,
365
+ collapsed,
366
+ bewerktId,
367
+ markers,
368
+ traceNodeId,
369
+ onTraceNode,
370
+ settings,
371
+ onSelectStep,
372
+ onChangeChoice,
373
+ toggleCollapse,
374
+ ]);
375
+ const rfEdges = useMemo(() => edges.map((e) => ({
376
+ id: e.id,
377
+ source: e.from,
378
+ target: e.to,
379
+ type: "state",
380
+ markerEnd: e.kind === "ref"
381
+ ? undefined
382
+ : e.route || e.state === "active"
383
+ ? "url(#flow-pijl-actief)"
384
+ : "url(#flow-pijl)",
385
+ animated: !!e.route,
386
+ data: { state: e.state, kind: e.kind, label: e.label, route: !!e.route },
387
+ })), [edges]);
388
+ // Bij richting- of inklapwissel opnieuw passend maken.
389
+ useEffect(() => {
390
+ const t = setTimeout(() => void fitView({ padding: 0.1, duration: 300 }), 50);
391
+ return () => clearTimeout(t);
392
+ }, [richting, collapsed, fitView]);
393
+ // Zodra er een orderroute gekozen is: inzoomen op precies die route.
394
+ useEffect(() => {
395
+ if (!routeLabel)
396
+ return;
397
+ const ids = zichtbaar.filter((n) => n.state === "active").map((n) => ({ id: n.id }));
398
+ const t = setTimeout(() => void fitView(ids.length ? { nodes: ids, padding: 0.25, duration: 500 } : { padding: 0.1, duration: 400 }), 80);
399
+ return () => clearTimeout(t);
400
+ }, [routeLabel, zichtbaar, fitView]);
401
+ // Vakje gekozen in de reisbalk of aangeklikt: dat vakje in beeld brengen.
402
+ useEffect(() => {
403
+ if (!traceNodeId)
404
+ return;
405
+ const t = setTimeout(() => void fitView({ nodes: [{ id: traceNodeId }], padding: 0.6, duration: 400, maxZoom: 1.2 }), 60);
406
+ return () => clearTimeout(t);
407
+ }, [traceNodeId, fitView]);
408
+ const traceGroep = traceNodeId ? nodeTrace?.get(traceNodeId) ?? null : null;
409
+ const aantalActief = zichtbaar.filter((n) => n.state === "active" && n.kind !== "merge" && n.kind !== "option").length;
410
+ return (_jsxs("div", { className: groot
411
+ ? "fixed inset-0 z-50 flex flex-col overflow-hidden border-0 bg-background"
412
+ : "relative overflow-hidden rounded-xl border border-border bg-muted/20", children: [routeLabel && (_jsxs("div", { className: "absolute left-3 top-3 z-10 flex max-w-[60%] items-start gap-2 rounded-md border border-primary bg-background/95 px-3 py-1.5 text-xs shadow", children: [_jsx("span", { className: "mt-1 inline-block h-2.5 w-6 shrink-0 rounded bg-primary" }), _jsxs("span", { children: ["Route van ", _jsx("strong", { children: routeLabel }), routeSummary && _jsx("span", { className: "block text-muted-foreground", children: routeSummary })] }), onClearRoute && (_jsx(Button, { variant: "ghost", size: "icon", className: "h-6 w-6 shrink-0", "aria-label": "Route verbergen", onClick: onClearRoute, children: _jsx(X, { className: "h-3.5 w-3.5" }) }))] })), _jsxs("div", { className: "absolute right-3 top-3 z-10 flex gap-1", children: [_jsx(Button, { variant: "outline", size: "icon", "aria-label": "Uitzoomen", onClick: () => void zoomOut(), children: _jsx(Minus, { className: "h-4 w-4" }) }), _jsx(Button, { variant: "outline", size: "icon", "aria-label": "Inzoomen", onClick: () => void zoomIn(), children: _jsx(Plus, { className: "h-4 w-4" }) }), _jsx(Button, { variant: "outline", size: "icon", "aria-label": "Alles passend maken", title: "Alles passend maken", onClick: () => void fitView({ padding: 0.1, duration: 300 }), children: _jsx(Maximize2, { className: "h-4 w-4" }) }), _jsx(Button, { variant: "outline", size: "icon", "aria-label": groot ? "Groot scherm sluiten" : "Groot scherm", title: groot ? "Groot scherm sluiten" : "Groot scherm", onClick: () => setGroot((g) => !g), children: groot ? _jsx(Minimize2, { className: "h-4 w-4" }) : _jsx(Expand, { className: "h-4 w-4" }) }), _jsx(Button, { variant: "outline", size: "icon", "aria-label": richting === "TB" ? "Van links naar rechts tekenen" : "Van boven naar beneden tekenen", title: richting === "TB" ? "Van links naar rechts tekenen" : "Van boven naar beneden tekenen", onClick: () => setRichting((r) => (r === "TB" ? "LR" : "TB")), children: richting === "TB" ? _jsx(ArrowLeftRight, { className: "h-4 w-4" }) : _jsx(ArrowDownUp, { className: "h-4 w-4" }) }), collapsed.size > 0 && (_jsx(Button, { variant: "outline", size: "sm", onClick: () => setCollapsed(new Set()), children: "Alles uitklappen" }))] }), _jsxs("div", { className: "absolute bottom-3 left-3 z-10 flex flex-wrap gap-2 rounded-lg border border-border bg-background/90 px-3 py-2 text-[11px] text-muted-foreground backdrop-blur", children: [_jsxs("span", { className: "flex items-center gap-1", children: [_jsx("span", { className: "inline-block h-2.5 w-5 rounded bg-primary" }), " actieve route (", aantalActief, " vakjes)"] }), _jsxs("span", { className: "flex items-center gap-1", children: [_jsx("span", { className: "inline-block h-2.5 w-5 rounded bg-muted-foreground/80" }), " mogelijk"] }), _jsxs("span", { className: "flex items-center gap-1", children: [_jsx("span", { className: "inline-block h-0 w-5 border-t-2 border-dashed border-muted-foreground/50" }), " niet bereikbaar"] }), _jsxs("span", { className: "flex items-center gap-1", children: [_jsx("span", { className: "inline-block h-3 w-3 rotate-45 border-2 border-muted-foreground/60" }), " beslismoment"] }), _jsxs("span", { className: "flex items-center gap-1", children: [_jsx("span", { className: "inline-block h-3 w-4 rounded-sm border border-muted-foreground/60" }), " taak"] }), _jsxs("span", { className: "flex items-center gap-1", children: [_jsx("span", { className: "inline-block h-3 w-3 rounded-full border-2 border-muted-foreground/60" }), " start"] }), _jsxs("span", { className: "flex items-center gap-1", children: [_jsx("span", { className: "inline-block h-3 w-3 rounded-full border-4 border-muted-foreground/60" }), " einde"] })] }), _jsxs("div", { ref: canvasRef, className: groot ? "h-full min-h-0 w-full flex-1" : "h-[75vh] min-h-[560px] w-full", onMouseDownCapture: (e) => {
413
+ if (e.button !== 0)
414
+ return;
415
+ muisIngedruktRef.current = true;
416
+ setMuisIngedrukt(true);
417
+ }, onMouseUp: () => {
418
+ muisIngedruktRef.current = false;
419
+ setMuisIngedrukt(false);
420
+ }, children: [_jsx("svg", { width: "0", height: "0", className: "absolute", children: _jsxs("defs", { children: [_jsx("marker", { id: "flow-pijl", viewBox: "0 0 10 10", refX: "9", refY: "5", markerWidth: "7", markerHeight: "7", orient: "auto-start-reverse", children: _jsx("path", { d: "M 0 0 L 10 5 L 0 10 z", className: "fill-muted-foreground/50" }) }), _jsx("marker", { id: "flow-pijl-actief", viewBox: "0 0 10 10", refX: "9", refY: "5", markerWidth: "7", markerHeight: "7", orient: "auto-start-reverse", children: _jsx("path", { d: "M 0 0 L 10 5 L 0 10 z", className: "fill-primary" }) })] }) }), _jsxs(ReactFlow, { nodes: rfNodes, edges: rfEdges, nodeTypes: NODE_TYPES, edgeTypes: EDGE_TYPES, fitView: true, fitViewOptions: { padding: 0.1 }, minZoom: 0.05, maxZoom: 2, nodesDraggable: false, nodesConnectable: false, elementsSelectable: true, selectNodesOnDrag: false, onPaneClick: () => setBewerktId(null), panOnScroll: !muisIngedrukt, zoomOnPinch: true, zoomOnScroll: false, "aria-label": "Kaart van de Logistiek Engine", children: [_jsx(Background, { variant: BackgroundVariant.Dots, gap: 24, size: 1 }), _jsx(MiniMap, { pannable: true, zoomable: true, className: "!bg-background", nodeColor: (n) => {
421
+ const d = n.data?.node;
422
+ if (!d)
423
+ return "transparent";
424
+ return d.state === "active" ? "hsl(var(--primary))" : "hsl(var(--muted-foreground) / 0.4)";
425
+ } })] })] }), traceGroep && (_jsx(NodeTracePanel, { groep: traceGroep, orderLabel: routeLabel ?? null, debug: debug, onDebug: setDebug, onClose: () => onTraceNode?.(null) })), uitleg && (_jsxs("div", { className: "absolute bottom-12 left-3 z-20 w-[22rem] max-w-[85%] rounded-xl border border-border bg-background p-4 shadow-lg", children: [_jsxs("div", { className: "flex items-start justify-between gap-2", children: [_jsx("p", { className: "text-sm font-semibold", children: uitleg.titel }), _jsx(Button, { variant: "ghost", size: "icon", "aria-label": "Uitleg sluiten", onClick: () => setUitleg(null), children: _jsx(X, { className: "h-4 w-4" }) })] }), _jsx("div", { className: "mt-2 space-y-2", children: uitleg.regels.map((regel, i) => (_jsx("p", { className: "text-xs leading-relaxed text-muted-foreground", children: regel }, i))) })] })), _jsx("p", { className: "border-t border-border px-3 py-2 text-xs text-muted-foreground", children: "Slepen om te verschuiven \u00B7 scrollen verschuift, linkermuisknop ingedrukt houden en scrollen zoomt in en uit \u00B7 dubbelklik op een vakje of keuzelijst om de instellingen daar meteen aan te passen (Escape sluit) \u00B7 klik een stap of instelling om alle regels in het zijpaneel te openen" })] }));
426
+ }
427
+ export function FlowCanvas(props) {
428
+ return (_jsx(ReactFlowProvider, { children: _jsx(Binnen, { ...props }) }));
429
+ }
@@ -0,0 +1,8 @@
1
+ import { type OrderRoute, type OrderVersie } from "../lib/flowRoute";
2
+ interface Props {
3
+ /** Wordt aangeroepen zodra een versie gekozen/losgelaten wordt. */
4
+ onRoute: (route: OrderRoute | null, label: string | null, orderRef: string | null, versie: OrderVersie | null) => void;
5
+ }
6
+ export declare function FlowOrders({ onRoute }: Props): import("react").JSX.Element;
7
+ export {};
8
+ //# sourceMappingURL=FlowOrders.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FlowOrders.d.ts","sourceRoot":"","sources":["../../src/components/FlowOrders.tsx"],"names":[],"mappings":"AAwBA,OAAO,EAA2B,KAAK,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAG9F,UAAU,KAAK;IACb,mEAAmE;IACnE,OAAO,EAAE,CACP,KAAK,EAAE,UAAU,GAAG,IAAI,EACxB,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,MAAM,EAAE,WAAW,GAAG,IAAI,KACvB,IAAI,CAAC;CACX;AAoCD,wBAAgB,UAAU,CAAC,EAAE,OAAO,EAAE,EAAE,KAAK,+BAgN5C"}