@flowselections/logistiek-engine 1.0.1

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 (175) hide show
  1. package/README.md +24 -0
  2. package/dist-lib/_core-safelist.d.ts +2 -0
  3. package/dist-lib/_core-safelist.d.ts.map +1 -0
  4. package/dist-lib/_core-safelist.js +15 -0
  5. package/dist-lib/components/BeladingIndelingCard.d.ts +2 -0
  6. package/dist-lib/components/BeladingIndelingCard.d.ts.map +1 -0
  7. package/dist-lib/components/BeladingIndelingCard.js +77 -0
  8. package/dist-lib/components/BufferDebugCard.d.ts +19 -0
  9. package/dist-lib/components/BufferDebugCard.d.ts.map +1 -0
  10. package/dist-lib/components/BufferDebugCard.js +69 -0
  11. package/dist-lib/components/CapaciteitMeldingenCard.d.ts +2 -0
  12. package/dist-lib/components/CapaciteitMeldingenCard.d.ts.map +1 -0
  13. package/dist-lib/components/CapaciteitMeldingenCard.js +40 -0
  14. package/dist-lib/components/LadingdragersPage.d.ts +2 -0
  15. package/dist-lib/components/LadingdragersPage.d.ts.map +1 -0
  16. package/dist-lib/components/LadingdragersPage.js +102 -0
  17. package/dist-lib/components/LogistiekEngineLogo.d.ts +6 -0
  18. package/dist-lib/components/LogistiekEngineLogo.d.ts.map +1 -0
  19. package/dist-lib/components/LogistiekEngineLogo.js +4 -0
  20. package/dist-lib/components/LogistiekEnginePage.d.ts +2 -0
  21. package/dist-lib/components/LogistiekEnginePage.d.ts.map +1 -0
  22. package/dist-lib/components/LogistiekEnginePage.js +53 -0
  23. package/dist-lib/components/ModulePromptsPage.d.ts +2 -0
  24. package/dist-lib/components/ModulePromptsPage.d.ts.map +1 -0
  25. package/dist-lib/components/ModulePromptsPage.js +58 -0
  26. package/dist-lib/components/RegiePage.d.ts +2 -0
  27. package/dist-lib/components/RegiePage.d.ts.map +1 -0
  28. package/dist-lib/components/RegiePage.js +82 -0
  29. package/dist-lib/components/RuleEnginePage.d.ts +7 -0
  30. package/dist-lib/components/RuleEnginePage.d.ts.map +1 -0
  31. package/dist-lib/components/RuleEnginePage.js +114 -0
  32. package/dist-lib/components/StrategieDialog.d.ts +11 -0
  33. package/dist-lib/components/StrategieDialog.d.ts.map +1 -0
  34. package/dist-lib/components/StrategieDialog.js +38 -0
  35. package/dist-lib/components/VertragingMeldingenCard.d.ts +2 -0
  36. package/dist-lib/components/VertragingMeldingenCard.d.ts.map +1 -0
  37. package/dist-lib/components/VertragingMeldingenCard.js +32 -0
  38. package/dist-lib/components/VoorraadMeldingenCard.d.ts +2 -0
  39. package/dist-lib/components/VoorraadMeldingenCard.d.ts.map +1 -0
  40. package/dist-lib/components/VoorraadMeldingenCard.js +26 -0
  41. package/dist-lib/components/rule-engine/CollapsibleGroup.d.ts +12 -0
  42. package/dist-lib/components/rule-engine/CollapsibleGroup.d.ts.map +1 -0
  43. package/dist-lib/components/rule-engine/CollapsibleGroup.js +11 -0
  44. package/dist-lib/components/rule-engine/ConditionsEditor.d.ts +8 -0
  45. package/dist-lib/components/rule-engine/ConditionsEditor.d.ts.map +1 -0
  46. package/dist-lib/components/rule-engine/ConditionsEditor.js +31 -0
  47. package/dist-lib/components/rule-engine/FieldHelp.d.ts +14 -0
  48. package/dist-lib/components/rule-engine/FieldHelp.d.ts.map +1 -0
  49. package/dist-lib/components/rule-engine/FieldHelp.js +18 -0
  50. package/dist-lib/components/rule-engine/FloridayConnectionBanner.d.ts +2 -0
  51. package/dist-lib/components/rule-engine/FloridayConnectionBanner.d.ts.map +1 -0
  52. package/dist-lib/components/rule-engine/FloridayConnectionBanner.js +41 -0
  53. package/dist-lib/components/rule-engine/ImpactPanel.d.ts +5 -0
  54. package/dist-lib/components/rule-engine/ImpactPanel.d.ts.map +1 -0
  55. package/dist-lib/components/rule-engine/ImpactPanel.js +25 -0
  56. package/dist-lib/components/rule-engine/RuleFieldInput.d.ts +9 -0
  57. package/dist-lib/components/rule-engine/RuleFieldInput.d.ts.map +1 -0
  58. package/dist-lib/components/rule-engine/RuleFieldInput.js +80 -0
  59. package/dist-lib/components/rule-engine/VersionsPanel.d.ts +10 -0
  60. package/dist-lib/components/rule-engine/VersionsPanel.d.ts.map +1 -0
  61. package/dist-lib/components/rule-engine/VersionsPanel.js +16 -0
  62. package/dist-lib/components/settings/LogistiekEngineSettingsCard.d.ts +2 -0
  63. package/dist-lib/components/settings/LogistiekEngineSettingsCard.d.ts.map +1 -0
  64. package/dist-lib/components/settings/LogistiekEngineSettingsCard.js +22 -0
  65. package/dist-lib/hooks/useCapaciteitMeldingen.d.ts +20 -0
  66. package/dist-lib/hooks/useCapaciteitMeldingen.d.ts.map +1 -0
  67. package/dist-lib/hooks/useCapaciteitMeldingen.js +46 -0
  68. package/dist-lib/hooks/useDocumentLayouts.d.ts +13 -0
  69. package/dist-lib/hooks/useDocumentLayouts.d.ts.map +1 -0
  70. package/dist-lib/hooks/useDocumentLayouts.js +54 -0
  71. package/dist-lib/hooks/useLadingdragers.d.ts +16 -0
  72. package/dist-lib/hooks/useLadingdragers.d.ts.map +1 -0
  73. package/dist-lib/hooks/useLadingdragers.js +86 -0
  74. package/dist-lib/hooks/useLogistiekStrategieen.d.ts +29 -0
  75. package/dist-lib/hooks/useLogistiekStrategieen.d.ts.map +1 -0
  76. package/dist-lib/hooks/useLogistiekStrategieen.js +111 -0
  77. package/dist-lib/hooks/useLogistiekTaken.d.ts +89 -0
  78. package/dist-lib/hooks/useLogistiekTaken.d.ts.map +1 -0
  79. package/dist-lib/hooks/useLogistiekTaken.js +314 -0
  80. package/dist-lib/hooks/usePickstations.d.ts +10 -0
  81. package/dist-lib/hooks/usePickstations.d.ts.map +1 -0
  82. package/dist-lib/hooks/usePickstations.js +51 -0
  83. package/dist-lib/hooks/useRecalcJob.d.ts +24 -0
  84. package/dist-lib/hooks/useRecalcJob.d.ts.map +1 -0
  85. package/dist-lib/hooks/useRecalcJob.js +83 -0
  86. package/dist-lib/hooks/useRuleEngine.d.ts +48 -0
  87. package/dist-lib/hooks/useRuleEngine.d.ts.map +1 -0
  88. package/dist-lib/hooks/useRuleEngine.js +222 -0
  89. package/dist-lib/hooks/useVertragingMeldingen.d.ts +29 -0
  90. package/dist-lib/hooks/useVertragingMeldingen.d.ts.map +1 -0
  91. package/dist-lib/hooks/useVertragingMeldingen.js +68 -0
  92. package/dist-lib/hooks/useVoorraadMeldingen.d.ts +30 -0
  93. package/dist-lib/hooks/useVoorraadMeldingen.d.ts.map +1 -0
  94. package/dist-lib/hooks/useVoorraadMeldingen.js +70 -0
  95. package/dist-lib/index.d.ts +36 -0
  96. package/dist-lib/index.d.ts.map +1 -0
  97. package/dist-lib/index.js +52 -0
  98. package/dist-lib/integrations/supabase/auth-attacher.d.ts +2 -0
  99. package/dist-lib/integrations/supabase/auth-attacher.d.ts.map +1 -0
  100. package/dist-lib/integrations/supabase/auth-attacher.js +12 -0
  101. package/dist-lib/integrations/supabase/auth-middleware.d.ts +19781 -0
  102. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -0
  103. package/dist-lib/integrations/supabase/auth-middleware.js +52 -0
  104. package/dist-lib/integrations/supabase/client.d.ts +19777 -0
  105. package/dist-lib/integrations/supabase/client.d.ts.map +1 -0
  106. package/dist-lib/integrations/supabase/client.js +13 -0
  107. package/dist-lib/integrations/supabase/client.server.d.ts +19777 -0
  108. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -0
  109. package/dist-lib/integrations/supabase/client.server.js +30 -0
  110. package/dist-lib/integrations/supabase/types.d.ts +20355 -0
  111. package/dist-lib/integrations/supabase/types.d.ts.map +1 -0
  112. package/dist-lib/integrations/supabase/types.js +78 -0
  113. package/dist-lib/lib/bufferMatching.d.ts +26 -0
  114. package/dist-lib/lib/bufferMatching.d.ts.map +1 -0
  115. package/dist-lib/lib/bufferMatching.js +73 -0
  116. package/dist-lib/lib/bulkLoadCarrierPlanning.server.d.ts +23 -0
  117. package/dist-lib/lib/bulkLoadCarrierPlanning.server.d.ts.map +1 -0
  118. package/dist-lib/lib/bulkLoadCarrierPlanning.server.js +126 -0
  119. package/dist-lib/lib/floridayDocuments.functions.d.ts +59415 -0
  120. package/dist-lib/lib/floridayDocuments.functions.d.ts.map +1 -0
  121. package/dist-lib/lib/floridayDocuments.functions.js +68 -0
  122. package/dist-lib/lib/floridayDocuments.server.d.ts +51 -0
  123. package/dist-lib/lib/floridayDocuments.server.d.ts.map +1 -0
  124. package/dist-lib/lib/floridayDocuments.server.js +109 -0
  125. package/dist-lib/lib/loadCarrierPlanner.d.ts +71 -0
  126. package/dist-lib/lib/loadCarrierPlanner.d.ts.map +1 -0
  127. package/dist-lib/lib/loadCarrierPlanner.js +374 -0
  128. package/dist-lib/lib/loadCarrierPlanning.functions.d.ts +98984 -0
  129. package/dist-lib/lib/loadCarrierPlanning.functions.d.ts.map +1 -0
  130. package/dist-lib/lib/loadCarrierPlanning.functions.js +106 -0
  131. package/dist-lib/lib/loadCarrierPlanning.server.d.ts +46 -0
  132. package/dist-lib/lib/loadCarrierPlanning.server.d.ts.map +1 -0
  133. package/dist-lib/lib/loadCarrierPlanning.server.js +515 -0
  134. package/dist-lib/lib/loadCarrierTaskSync.server.d.ts +3 -0
  135. package/dist-lib/lib/loadCarrierTaskSync.server.d.ts.map +1 -0
  136. package/dist-lib/lib/loadCarrierTaskSync.server.js +56 -0
  137. package/dist-lib/lib/loadCarrierTypes.d.ts +19 -0
  138. package/dist-lib/lib/loadCarrierTypes.d.ts.map +1 -0
  139. package/dist-lib/lib/loadCarrierTypes.js +41 -0
  140. package/dist-lib/lib/logisticsPlanner.d.ts +56 -0
  141. package/dist-lib/lib/logisticsPlanner.d.ts.map +1 -0
  142. package/dist-lib/lib/logisticsPlanner.js +273 -0
  143. package/dist-lib/lib/logisticsRecalcJob.functions.d.ts +39575 -0
  144. package/dist-lib/lib/logisticsRecalcJob.functions.d.ts.map +1 -0
  145. package/dist-lib/lib/logisticsRecalcJob.functions.js +30 -0
  146. package/dist-lib/lib/logisticsRecalcJob.server.d.ts +32 -0
  147. package/dist-lib/lib/logisticsRecalcJob.server.d.ts.map +1 -0
  148. package/dist-lib/lib/logisticsRecalcJob.server.js +116 -0
  149. package/dist-lib/lib/logisticsRecalculate.functions.d.ts +19792 -0
  150. package/dist-lib/lib/logisticsRecalculate.functions.d.ts.map +1 -0
  151. package/dist-lib/lib/logisticsRecalculate.functions.js +35 -0
  152. package/dist-lib/lib/logisticsTasks.d.ts +23 -0
  153. package/dist-lib/lib/logisticsTasks.d.ts.map +1 -0
  154. package/dist-lib/lib/logisticsTasks.js +92 -0
  155. package/dist-lib/lib/modulePrompts.d.ts +31 -0
  156. package/dist-lib/lib/modulePrompts.d.ts.map +1 -0
  157. package/dist-lib/lib/modulePrompts.js +1195 -0
  158. package/dist-lib/lib/ruleEngineImpact.d.ts +27 -0
  159. package/dist-lib/lib/ruleEngineImpact.d.ts.map +1 -0
  160. package/dist-lib/lib/ruleEngineImpact.js +257 -0
  161. package/dist-lib/lib/ruleEngineSchema.d.ts +69 -0
  162. package/dist-lib/lib/ruleEngineSchema.d.ts.map +1 -0
  163. package/dist-lib/lib/ruleEngineSchema.js +899 -0
  164. package/dist-lib/lib/ruleEngineWizard.d.ts +17 -0
  165. package/dist-lib/lib/ruleEngineWizard.d.ts.map +1 -0
  166. package/dist-lib/lib/ruleEngineWizard.js +204 -0
  167. package/dist-lib/lib/utils.d.ts +3 -0
  168. package/dist-lib/lib/utils.d.ts.map +1 -0
  169. package/dist-lib/lib/utils.js +5 -0
  170. package/dist-lib/lib/validationSchemas.d.ts +15 -0
  171. package/dist-lib/lib/validationSchemas.d.ts.map +1 -0
  172. package/dist-lib/lib/validationSchemas.js +25 -0
  173. package/dist-lib/styles.css +1 -0
  174. package/package.json +113 -0
  175. package/public/logo.png +0 -0
@@ -0,0 +1,899 @@
1
+ // ============================================================================
2
+ // src/lib/ruleEngineSchema.ts
3
+ // ============================================================================
4
+ // Declaratief schema van de Rule Engine. ALLE logistieke werkwijzen worden
5
+ // hier als configuratie beschreven — nooit als klant-specifieke code.
6
+ // Een nieuwe instelling toevoegen = een veld toevoegen aan dit schema.
7
+ // ============================================================================
8
+ // ── Voorwaarden ─────────────────────────────────────────────────────────────
9
+ export const CONDITION_FIELDS = [
10
+ { value: "customer", label: "Klant" },
11
+ { value: "delivery_location", label: "Afleverlocatie" },
12
+ { value: "carrier", label: "Vervoerder" },
13
+ { value: "order_type", label: "Ordertype" },
14
+ { value: "product_group", label: "Productgroep" },
15
+ { value: "weekday", label: "Weekdag" },
16
+ { value: "delivery_date", label: "Leverdatum" },
17
+ ];
18
+ export const CONDITION_OPERATORS = [
19
+ { value: "in", label: "is één van" },
20
+ { value: "not_in", label: "is niet één van" },
21
+ { value: "equals", label: "is gelijk aan" },
22
+ { value: "contains", label: "bevat" },
23
+ ];
24
+ // ── Stappen ─────────────────────────────────────────────────────────────────
25
+ export const RULE_STEPS = [
26
+ {
27
+ id: "order_verwerking",
28
+ label: "Order verwerking",
29
+ icon: "inbox",
30
+ description: "Bepaal wanneer een order in een gecombineerde order (orderbuffer) terechtkomt.",
31
+ supportsConditions: true,
32
+ groups: [
33
+ {
34
+ id: "orderbuffer",
35
+ label: "Orderbuffer",
36
+ description: "Orders die aan onderstaande regels voldoen worden samengevoegd tot \u00e9\u00e9n gecombineerde order.",
37
+ fields: [
38
+ {
39
+ key: "buffer_enabled",
40
+ label: "Orderbuffer gebruiken",
41
+ type: "toggle",
42
+ default: true,
43
+ description: "Staat dit uit, dan wordt elke order los verwerkt.",
44
+ },
45
+ {
46
+ key: "buffer_include_reservations",
47
+ label: "Gereserveerde bestellingen meenemen in buffers",
48
+ type: "toggle",
49
+ default: false,
50
+ description: "Staat dit uit, dan blijven reserveringen altijd los. Staat het aan, dan gelden voor reserveringen exact dezelfde combineerregels; nog onbekende gegevens tellen niet mee.",
51
+ dependsOn: { key: "buffer_enabled", in: [true] },
52
+ },
53
+ {
54
+ key: "buffer_match",
55
+ label: "Orders combineren wanneer deze gegevens gelijk zijn",
56
+ type: "priority",
57
+ description: "Zet de criteria aan of uit.",
58
+ dependsOn: { key: "buffer_enabled", in: [true] },
59
+ options: [
60
+ { value: "customer_floriday", label: "Zelfde Floriday klant" },
61
+ { value: "customer_bvo", label: "Zelfde BVO klant" },
62
+ { value: "delivery_location", label: "Zelfde afleverlocatie" },
63
+ { value: "delivery_date", label: "Zelfde leverdatum" },
64
+ { value: "carrier", label: "Zelfde vervoerder" },
65
+ { value: "load_carrier", label: "Zelfde ladingdrager" },
66
+ { value: "order_type", label: "Zelfde ordertype" },
67
+ ],
68
+ },
69
+ {
70
+ key: "buffer_close_before_minutes",
71
+ label: "Buffer sluit ... minuten v\u00f3\u00f3r de aanmeldtijd",
72
+ type: "number",
73
+ unit: "minuten",
74
+ min: 0,
75
+ max: 1440,
76
+ default: 60,
77
+ description: "De gecombineerde order wordt zoveel minuten v\u00f3\u00f3r de aanmeldtijd gesloten.",
78
+ dependsOn: { key: "buffer_enabled", in: [true] },
79
+ },
80
+ ],
81
+ },
82
+ {
83
+ id: "voorraadcontrole",
84
+ label: "Voorraadcontrole",
85
+ description: "Bepaal wat er gebeurt als een order producten bevat die niet voldoende op voorraad zijn.",
86
+ fields: [
87
+ {
88
+ key: "allow_backorder",
89
+ label: "Bestellingen uitleveren wanneer een product niet op voorraad is",
90
+ type: "toggle",
91
+ default: false,
92
+ description: "Staat dit uit, dan blijft de huidige werkwijze ongewijzigd. Staat dit aan, dan controleert de Logistics Engine elke nieuwe order op voorraad.",
93
+ },
94
+ {
95
+ key: "shortage_notification_enabled",
96
+ label: "Melding aanmaken",
97
+ type: "toggle",
98
+ default: true,
99
+ description: "Maakt een melding aan in het centrale meldingenoverzicht met ordernummer, klant, producten, ontbrekend aantal, leverdatum, prioriteit en tijdstip. De melding blijft open tot hij is afgehandeld.",
100
+ dependsOn: { key: "allow_backorder", in: [true] },
101
+ },
102
+ {
103
+ key: "shortage_priority",
104
+ label: "Prioriteit van de melding",
105
+ type: "select",
106
+ default: "normal",
107
+ options: [
108
+ { value: "low", label: "Laag" },
109
+ { value: "normal", label: "Normaal" },
110
+ { value: "high", label: "Hoog" },
111
+ ],
112
+ dependsOn: [
113
+ { key: "allow_backorder", in: [true] },
114
+ { key: "shortage_notification_enabled", in: [true] },
115
+ ],
116
+ },
117
+ {
118
+ key: "shortage_email_enabled",
119
+ label: "E-mail versturen",
120
+ type: "toggle",
121
+ default: false,
122
+ description: "Verstuurt automatisch een e-mail met ordernummer, klant, producten zonder voorraad, ontbrekend aantal, gewenste leverdatum en het moment van de melding.",
123
+ dependsOn: { key: "allow_backorder", in: [true] },
124
+ },
125
+ {
126
+ key: "shortage_emails",
127
+ label: "E-mailadressen",
128
+ type: "textarea",
129
+ default: "",
130
+ description: "Eén of meerdere adressen, gescheiden door een komma.",
131
+ dependsOn: [
132
+ { key: "allow_backorder", in: [true] },
133
+ { key: "shortage_email_enabled", in: [true] },
134
+ ],
135
+ },
136
+ ],
137
+ },
138
+ ],
139
+ },
140
+ {
141
+ id: "karrenindeling",
142
+ label: "Beladingindeling",
143
+ icon: "layout-grid",
144
+ description: "Bepaal exact hoe een belading (kar, pallet, doos of container) ontstaat en wordt samengesteld.",
145
+ supportsConditions: true,
146
+ groups: [
147
+ {
148
+ id: "verzendmethode",
149
+ label: "Verzendmethodes (Floriday)",
150
+ description: "Alleen de ladingdragers die Floriday kent. Zet aan welke je gebruikt.",
151
+ fields: [
152
+ {
153
+ key: "load_carrier_types",
154
+ label: "Toegestane ladingdragers",
155
+ type: "priority",
156
+ options: [
157
+ { value: "trolley", label: "Stapelwagen" },
158
+ { value: "danish_container", label: "Deense container" },
159
+ { value: "euro_rolcontainer", label: "Euro-rolcontainer" },
160
+ { value: "pallet", label: "Pallet" },
161
+ { value: "euro_pallet", label: "Euro-pallet" },
162
+ { value: "loose", label: "Geen ladingdrager" },
163
+ ],
164
+ },
165
+ {
166
+ key: "mix_carrier_types",
167
+ label: "Verschillende ladingdragers binnen één order toestaan",
168
+ type: "toggle",
169
+ default: true,
170
+ },
171
+ ],
172
+ },
173
+ {
174
+ id: "werkwijze",
175
+ label: "Werkwijze",
176
+ fields: [
177
+ {
178
+ key: "mode",
179
+ label: "Hoe ontstaat een belading?",
180
+ type: "select",
181
+ default: "auto",
182
+ options: [
183
+ { value: "auto", label: "Systeem genereert automatisch de beladingen" },
184
+ { value: "scan", label: "Medewerker scant een bestaande belading" },
185
+ ],
186
+ },
187
+ ],
188
+ },
189
+ {
190
+ id: "samenstellen",
191
+ label: "Belading samenstellen",
192
+ fields: [
193
+ { key: "multi_order", label: "Meerdere orders op één ladingdrager toestaan", type: "toggle", default: true, dependsOn: { key: "mode", in: ["auto"] } },
194
+ {
195
+ key: "fill_orders_as_much_as_fits",
196
+ label: "Orders: zoveel als past",
197
+ type: "toggle",
198
+ default: false,
199
+ description: "Geen maximum aantal orders per ladingdrager.",
200
+ dependsOn: { key: "mode", in: ["auto"] },
201
+ },
202
+ { key: "max_orders", label: "Maximaal aantal orders per ladingdrager", type: "number", min: 1, max: 99, default: 6, dependsOn: [{ key: "mode", in: ["auto"] }, { key: "fill_orders_as_much_as_fits", in: [false] }] },
203
+ {
204
+ key: "fill_customers_as_much_as_fits",
205
+ label: "Klanten: zoveel als past",
206
+ type: "toggle",
207
+ default: false,
208
+ description: "Geen maximum aantal klanten per ladingdrager.",
209
+ dependsOn: { key: "mode", in: ["auto"] },
210
+ },
211
+ { key: "max_customers", label: "Maximaal aantal klanten per ladingdrager", type: "number", min: 1, max: 99, default: 2, dependsOn: [{ key: "mode", in: ["auto"] }, { key: "fill_customers_as_much_as_fits", in: [false] }] },
212
+ {
213
+ key: "fill_layers_as_much_as_fits",
214
+ label: "Lagen: zoveel als past",
215
+ type: "toggle",
216
+ default: false,
217
+ description: "Geen maximum aantal lagen — laden tot de ladingdrager fysiek vol is.",
218
+ dependsOn: { key: "mode", in: ["auto"] },
219
+ },
220
+ { key: "max_layers", label: "Maximaal aantal lagen (stapelwagen / container)", type: "number", min: 1, max: 20, default: 5, dependsOn: [{ key: "mode", in: ["auto"] }, { key: "fill_layers_as_much_as_fits", in: [false] }] },
221
+ {
222
+ key: "fill_boxes_as_much_as_fits",
223
+ label: "Dozen per pallet: zoveel als past",
224
+ type: "toggle",
225
+ default: false,
226
+ description: "Geen maximum aantal dozen per pallet.",
227
+ dependsOn: { key: "mode", in: ["auto"] },
228
+ },
229
+ { key: "max_boxes_per_pallet", label: "Maximaal aantal dozen per pallet", type: "number", min: 1, max: 200, default: 40, dependsOn: [{ key: "mode", in: ["auto"] }, { key: "fill_boxes_as_much_as_fits", in: [false] }] },
230
+ { key: "optimal_fill", label: "Optimale vulling berekenen", type: "toggle", default: true, dependsOn: { key: "mode", in: ["auto"] } },
231
+ ],
232
+ },
233
+ ],
234
+ },
235
+ {
236
+ id: "order_picken",
237
+ label: "Order picken",
238
+ icon: "scan-line",
239
+ description: "Bepaal hoe er gepickt wordt en in welke volgorde.",
240
+ supportsConditions: true,
241
+ groups: [
242
+ {
243
+ id: "methode",
244
+ label: "Pickmethode",
245
+ fields: [
246
+ {
247
+ key: "pick_mode",
248
+ label: "Picken op basis van",
249
+ type: "select",
250
+ default: "cart",
251
+ options: [
252
+ { value: "order", label: "Picken per order" },
253
+ { value: "cart", label: "Picken per kar" },
254
+ { value: "buffer", label: "Picken per orderbuffer" },
255
+ ],
256
+ },
257
+ {
258
+ key: "scanner",
259
+ label: "Scannergebruik",
260
+ type: "select",
261
+ default: "required",
262
+ options: [
263
+ { value: "required", label: "Scanner verplicht" },
264
+ { value: "none", label: "Zonder scanner" },
265
+ ],
266
+ },
267
+ ],
268
+ },
269
+ {
270
+ id: "werkdagen",
271
+ label: "Werkdagen",
272
+ description: "Op welke dagen mag er gepickt worden?",
273
+ fields: [
274
+ {
275
+ key: "pick_workdays",
276
+ label: "Pickdagen",
277
+ type: "days",
278
+ default: ["mon", "tue", "wed", "thu", "fri"],
279
+ description: "Alleen op de geselecteerde dagen worden pickopdrachten ingepland. Valt het werk op een niet-werkdag, dan schuift het naar de dichtstbijzijnde eerdere werkdag zodat de order op tijd blijft.",
280
+ options: [
281
+ { value: "mon", label: "Maandag" },
282
+ { value: "tue", label: "Dinsdag" },
283
+ { value: "wed", label: "Woensdag" },
284
+ { value: "thu", label: "Donderdag" },
285
+ { value: "fri", label: "Vrijdag" },
286
+ { value: "sat", label: "Zaterdag" },
287
+ { value: "sun", label: "Zondag" },
288
+ ],
289
+ },
290
+ ],
291
+ },
292
+ {
293
+ id: "capaciteit_ophaalmomenten",
294
+ label: "Capaciteit & ophaalmomenten",
295
+ description: "Bij meerdere ophaalmomenten op één dag berekent de Logistics Engine per ophaalmoment of alle karren op tijd gepickt kunnen worden. De personeelsplanning (aantal medewerkers en werktijden) en de verwerkingstijd per ladingdrager bepalen de beschikbare capaciteit.",
296
+ fields: [
297
+ {
298
+ key: "capacity_auto_shift",
299
+ label: "Karren automatisch doorschuiven naar een later ophaalmoment",
300
+ type: "toggle",
301
+ default: true,
302
+ description: "Past de planning niet, dan verplaatst de Engine automatisch complete karren naar het volgende ophaalmoment en rekent daarna opnieuw. Een kar wordt nooit gedeeltelijk verplaatst; ook karren uit een buffer mogen mee zolang de hele kar meegaat.",
303
+ },
304
+ {
305
+ key: "capacity_shift_priority",
306
+ label: "Welke karren schuiven als eerste door?",
307
+ type: "select",
308
+ default: "lowest_occupancy",
309
+ dependsOn: { key: "capacity_auto_shift", in: [true] },
310
+ options: [
311
+ { value: "lowest_occupancy", label: "Minst gevulde kar eerst" },
312
+ { value: "newest_order", label: "Laatst binnengekomen order eerst" },
313
+ ],
314
+ },
315
+ {
316
+ key: "capacity_alert_notification",
317
+ label: "Melding aanmaken in het Meldingencentrum",
318
+ type: "toggle",
319
+ default: true,
320
+ description: "Wanneer geen enkel ophaalmoment genoeg capaciteit heeft, komt er automatisch een melding in het centrale Meldingencentrum.",
321
+ },
322
+ {
323
+ key: "capacity_alert_email",
324
+ label: "E-mail versturen bij capaciteitstekort",
325
+ type: "toggle",
326
+ default: false,
327
+ },
328
+ {
329
+ key: "capacity_alert_emails",
330
+ label: "E-mailadressen",
331
+ type: "text",
332
+ default: "",
333
+ description: "Meerdere adressen scheiden met een komma.",
334
+ dependsOn: { key: "capacity_alert_email", in: [true] },
335
+ },
336
+ ],
337
+ },
338
+ {
339
+ id: "start_picken",
340
+ label: "Wanneer beginnen met picken?",
341
+ description: "Bepaalt wanneer een belading definitief wordt ingepland. Tot dat moment kunnen beladingen nog verder opgebouwd en samengevoegd worden.",
342
+ fields: [
343
+ {
344
+ key: "pick_start_mode",
345
+ label: "Wanneer mag een belading de buffer uit?",
346
+ type: "radio",
347
+ default: "as_late_as_possible",
348
+ options: [
349
+ {
350
+ value: "as_late_as_possible",
351
+ label: "Zo laat mogelijk",
352
+ description: "Het systeem berekent continu hoeveel capaciteit nog beschikbaar is. Beladingen worden pas vrijgegeven wanneer er nog precies voldoende tijd over is om alles vóór vertrek van de vervoerder te picken. Zo blijven beladingen zo lang mogelijk combineerbaar.",
353
+ },
354
+ {
355
+ value: "window_before_checkin",
356
+ label: "Binnen venster vóór aanmeldmoment",
357
+ description: "Een belading mag vanaf X werkuren of hele dagen vóór het aanmeldmoment naar het Pickstation, tot en met het aanmeldmoment zelf. Wordt het te druk, dan mogen beladingen ook eerder naar het Pickstation.",
358
+ },
359
+ ],
360
+ },
361
+ {
362
+ key: "pick_window_unit",
363
+ label: "Venster uitdrukken in",
364
+ type: "select",
365
+ default: "hours",
366
+ options: [
367
+ { value: "hours", label: "Werkuren" },
368
+ { value: "days", label: "Hele dagen" },
369
+ ],
370
+ dependsOn: { key: "pick_start_mode", in: ["window_before_checkin"] },
371
+ },
372
+ {
373
+ key: "pick_window_hours",
374
+ label: "Hoeveel werkuren vóór het aanmeldmoment?",
375
+ type: "number",
376
+ min: 0,
377
+ max: 72,
378
+ default: 4,
379
+ unit: "werkuren",
380
+ description: "Vanaf zoveel werkuren vóór het aanmeldmoment mogen beladingen naar het Pickstation gaan.",
381
+ dependsOn: [
382
+ { key: "pick_start_mode", in: ["window_before_checkin"] },
383
+ { key: "pick_window_unit", in: ["hours"] },
384
+ ],
385
+ },
386
+ {
387
+ key: "pick_window_days",
388
+ label: "Hoeveel hele dagen vóór het aanmeldmoment?",
389
+ type: "number",
390
+ min: 0,
391
+ max: 14,
392
+ default: 1,
393
+ unit: "dagen",
394
+ description: "Vanaf zoveel hele dagen vóór het aanmeldmoment mogen beladingen naar het Pickstation gaan.",
395
+ dependsOn: [
396
+ { key: "pick_start_mode", in: ["window_before_checkin"] },
397
+ { key: "pick_window_unit", in: ["days"] },
398
+ ],
399
+ },
400
+ {
401
+ key: "pick_partial_carriers",
402
+ label: "Niet-volle beladingen",
403
+ type: "radio",
404
+ default: "after_checkin",
405
+ dependsOn: { key: "pick_start_mode", in: ["window_before_checkin"] },
406
+ options: [
407
+ {
408
+ value: "after_checkin",
409
+ label: "Alleen na het aanmeldmoment",
410
+ description: "Niet-volle beladingen blijven open tot het aanmeldmoment en gaan pas daarna naar het Pickstation, zodat ze maximaal gecombineerd kunnen worden.",
411
+ },
412
+ {
413
+ value: "also_before",
414
+ label: "Ook vóór het aanmeldmoment",
415
+ description: "Niet-volle beladingen mogen ook binnen het venster vóór het aanmeldmoment al naar het Pickstation.",
416
+ },
417
+ ],
418
+ },
419
+ {
420
+ key: "pick_reserve_type",
421
+ label: "Buffer tussen laatste order en ophaalmoment",
422
+ type: "radio",
423
+ default: "fixed",
424
+ description: "Hoeveel reserve wil je altijd overhouden tussen de laatste order die nog verwerkt mag worden en het daadwerkelijke ophaalmoment van de vervoerder? Bedoeld voor spoedorders, wijzigingen, vertragingen en onverwachte extra beladingen.",
425
+ dependsOn: { key: "pick_start_mode", in: ["as_late_as_possible"] },
426
+ options: [
427
+ { value: "fixed", label: "Vaste tijd" },
428
+ { value: "percentage", label: "Percentage van picktijd" },
429
+ ],
430
+ },
431
+ {
432
+ key: "pick_reserve_minutes",
433
+ label: "Reservetijd",
434
+ type: "number",
435
+ min: 0,
436
+ max: 1440,
437
+ default: 30,
438
+ unit: "minuten",
439
+ dependsOn: [
440
+ { key: "pick_start_mode", in: ["as_late_as_possible"] },
441
+ { key: "pick_reserve_type", in: ["fixed"] },
442
+ ],
443
+ },
444
+ {
445
+ key: "pick_reserve_percentage",
446
+ label: "Reserve als percentage van de picktijd",
447
+ type: "number",
448
+ min: 0,
449
+ max: 100,
450
+ default: 20,
451
+ unit: "%",
452
+ dependsOn: [
453
+ { key: "pick_start_mode", in: ["as_late_as_possible"] },
454
+ { key: "pick_reserve_type", in: ["percentage"] },
455
+ ],
456
+ },
457
+ ],
458
+ },
459
+ {
460
+ id: "klant_informeren",
461
+ label: "Klant automatisch informeren bij vertraging",
462
+ description: "De Logistics Engine informeert de klant zodra een levering vertraagd raakt.",
463
+ fields: [
464
+ {
465
+ key: "notify_customer",
466
+ label: "Klant automatisch op de hoogte stellen",
467
+ type: "toggle",
468
+ default: false,
469
+ description: "De klant ontvangt automatisch een kort mailtje in zijn eigen taal dat (een deel van) de bestelling een dag later komt.",
470
+ },
471
+ ],
472
+ },
473
+ {
474
+ id: "herplannen",
475
+ label: "Herplannen van de order bij vertraging",
476
+ description: "Wat gebeurt er met de order of belading die niet meer op tijd geleverd kan worden?",
477
+ fields: [
478
+ {
479
+ key: "reschedule_mode",
480
+ label: "Herplanning",
481
+ type: "radio",
482
+ default: "next_delivery_day",
483
+ options: [
484
+ {
485
+ value: "next_delivery_day",
486
+ label: "Automatisch naar de eerstvolgende beschikbare leverdag",
487
+ description: "De Engine verplaatst de order naar de eerste leverdag die nog haalbaar is.",
488
+ },
489
+ {
490
+ value: "next_calendar_day",
491
+ label: "Automatisch naar de volgende kalenderdag",
492
+ description: "De order schuift altijd één dag door.",
493
+ },
494
+ {
495
+ value: "manual_approval",
496
+ label: "Wachten op handmatige goedkeuring",
497
+ description: "De Engine past de planning niet aan totdat een gebruiker de wijziging heeft goedgekeurd.",
498
+ },
499
+ ],
500
+ },
501
+ {
502
+ key: "delayed_order_status",
503
+ label: "Orderstatus bij vertraging",
504
+ type: "select",
505
+ default: "delayed",
506
+ options: [
507
+ { value: "delayed", label: "Vertraagd" },
508
+ { value: "awaiting_reschedule", label: "Wacht op herplanning" },
509
+ { value: "unchanged", label: "Status niet wijzigen" },
510
+ ],
511
+ },
512
+ ],
513
+ },
514
+ {
515
+ id: "interne_melding",
516
+ label: "Interne melding bij vertraging",
517
+ description: "Informeer de eigen organisatie zodra een levering vertraagd raakt.",
518
+ fields: [
519
+ {
520
+ key: "internal_alert_enabled",
521
+ label: "Interne melding aanmaken",
522
+ type: "toggle",
523
+ default: true,
524
+ },
525
+ {
526
+ key: "delay_email_enabled",
527
+ label: "E-mail versturen",
528
+ type: "toggle",
529
+ default: false,
530
+ description: "De e-mail bevat ordernummer, klant, geplande leverdatum, verwachte nieuwe leverdatum, reden van de vertraging en het moment van de melding.",
531
+ dependsOn: { key: "internal_alert_enabled", in: [true] },
532
+ },
533
+ {
534
+ key: "delay_emails",
535
+ label: "E-mailadressen",
536
+ type: "textarea",
537
+ default: "",
538
+ description: "Eén of meerdere adressen, gescheiden door een komma.",
539
+ dependsOn: [
540
+ { key: "internal_alert_enabled", in: [true] },
541
+ { key: "delay_email_enabled", in: [true] },
542
+ ],
543
+ },
544
+ {
545
+ key: "delay_notification_enabled",
546
+ label: "Melding aanmaken in het Meldingencentrum",
547
+ type: "toggle",
548
+ default: true,
549
+ description: "De melding bevat ordernummer, klant, huidige status, geplande leverdatum, verwachte nieuwe leverdatum, reden, prioriteit en tijdstip. De melding blijft zichtbaar tot hij is afgehandeld.",
550
+ dependsOn: { key: "internal_alert_enabled", in: [true] },
551
+ },
552
+ {
553
+ key: "delay_priority",
554
+ label: "Prioriteit van de melding",
555
+ type: "select",
556
+ default: "high",
557
+ options: [
558
+ { value: "low", label: "Laag" },
559
+ { value: "normal", label: "Normaal" },
560
+ { value: "high", label: "Hoog" },
561
+ ],
562
+ dependsOn: [
563
+ { key: "internal_alert_enabled", in: [true] },
564
+ { key: "delay_notification_enabled", in: [true] },
565
+ ],
566
+ },
567
+ ],
568
+ },
569
+ ],
570
+ },
571
+ {
572
+ id: "pickstations",
573
+ label: "Pickstations",
574
+ icon: "warehouse",
575
+ description: "Capaciteit, werktijden, picktijden en documentvrijgave per pickstation.",
576
+ supportsConditions: true,
577
+ groups: [
578
+ {
579
+ id: "capaciteit",
580
+ label: "Capaciteit & werktijden",
581
+ fields: [
582
+ { key: "start_time", label: "Starttijd", type: "time", default: "06:00" },
583
+ { key: "end_time", label: "Eindtijd", type: "time", default: "16:00" },
584
+ { key: "avg_staff", label: "Gemiddeld aantal medewerkers", type: "number", min: 1, max: 500, default: 5, unit: "personen" },
585
+ ],
586
+ },
587
+ {
588
+ id: "picktijden",
589
+ label: "Picktijd per ladingdrager",
590
+ description: "Hoe lang doet één medewerker gemiddeld over het picken van één ladingdrager.",
591
+ fields: [
592
+ { key: "pick_time_trolley", label: "Stapelwagen", type: "number", min: 0, max: 999, default: 15, unit: "min per drager" },
593
+ { key: "pick_time_danish_container", label: "Deense container", type: "number", min: 0, max: 999, default: 15, unit: "min per drager" },
594
+ { key: "pick_time_euro_rolcontainer", label: "Euro-rolcontainer", type: "number", min: 0, max: 999, default: 12, unit: "min per drager" },
595
+ { key: "pick_time_pallet", label: "Pallet", type: "number", min: 0, max: 999, default: 20, unit: "min per drager" },
596
+ { key: "pick_time_euro_pallet", label: "Euro-pallet", type: "number", min: 0, max: 999, default: 3, unit: "min per drager" },
597
+ { key: "pick_time_loose", label: "Geen ladingdrager", type: "number", min: 0, max: 999, default: 5, unit: "min per drager" },
598
+ ],
599
+ },
600
+ {
601
+ id: "pakbon",
602
+ label: "Pakbon",
603
+ fields: [
604
+ {
605
+ key: "packing_slip_release",
606
+ label: "Wanneer wordt de pakbon vrijgegeven?",
607
+ type: "radio",
608
+ default: "with_pick_release",
609
+ options: [
610
+ {
611
+ value: "with_pick_release",
612
+ label: "Zodra de order wordt vrijgegeven",
613
+ description: "Volgt exact de instellingen bij \u201c3. Order picken\u201d.",
614
+ },
615
+ {
616
+ value: "hours_before_pick",
617
+ label: "X uur v\u00f3\u00f3r het geplande pickmoment",
618
+ description: "De volledige order wordt automatisch vrijgegeven v\u00f3\u00f3r het pickmoment.",
619
+ },
620
+ ],
621
+ },
622
+ {
623
+ key: "packing_slip_hours_before",
624
+ label: "Uren v\u00f3\u00f3r het pickmoment",
625
+ type: "number",
626
+ min: 0,
627
+ max: 168,
628
+ default: 2,
629
+ unit: "uur",
630
+ dependsOn: { key: "packing_slip_release", in: ["hours_before_pick"] },
631
+ },
632
+ {
633
+ key: "packing_slip_group_identical_carts",
634
+ label: "Gelijke karren op één pakbon",
635
+ type: "toggle",
636
+ default: true,
637
+ description: "Bij picken per kar: karren met dezelfde inhoud komen op één pakbon, met bovenaan hoe vaak die kar gemaakt moet worden.",
638
+ dependsOn: { step: "order_picken", key: "pick_mode", in: ["cart"] },
639
+ },
640
+ {
641
+ key: "packing_slip_carts_per_print",
642
+ label: "Aantal karren dat je in één keer uitprint",
643
+ type: "number",
644
+ min: 1,
645
+ max: 99,
646
+ default: 1,
647
+ unit: "karren",
648
+ description: "Hoeveel karren je tegelijk uitprint. Elke afwijkende karindeling levert een eigen pakbon op: 3 karren met 3 verschillende indelingen worden dus 3 pakbonnen. Hoeveel karindelingen op één vel passen bepaalt de pakbon-layout.",
649
+ dependsOn: [
650
+ { step: "order_picken", key: "pick_mode", in: ["cart"] },
651
+ { key: "packing_slip_group_identical_carts", in: [true] },
652
+ ],
653
+ },
654
+ { key: "packing_slip_station", label: "Pickstation", type: "pickstation" },
655
+ {
656
+ key: "packing_slip_layout",
657
+ label: "Layout",
658
+ type: "layout",
659
+ layoutTypes: ["pakbon"],
660
+ description: "Welke layout uit de Layout maker gebruikt wordt voor de pakbon.",
661
+ },
662
+ ],
663
+ },
664
+ {
665
+ id: "stickers",
666
+ label: "Stickers",
667
+ fields: [
668
+ {
669
+ key: "sticker_release",
670
+ label: "Wanneer worden de stickers vrijgegeven?",
671
+ type: "radio",
672
+ default: "with_pick_release",
673
+ options: [
674
+ {
675
+ value: "with_pick_release",
676
+ label: "Zodra de order wordt vrijgegeven",
677
+ description: "Volgt exact de instellingen bij \u201c3. Order picken\u201d.",
678
+ },
679
+ {
680
+ value: "hours_before_pick",
681
+ label: "X uur v\u00f3\u00f3r het geplande pickmoment",
682
+ description: "De volledige order wordt automatisch vrijgegeven v\u00f3\u00f3r het pickmoment.",
683
+ },
684
+ ],
685
+ },
686
+ {
687
+ key: "sticker_hours_before",
688
+ label: "Uren v\u00f3\u00f3r het pickmoment",
689
+ type: "number",
690
+ min: 0,
691
+ max: 168,
692
+ default: 2,
693
+ unit: "uur",
694
+ dependsOn: { key: "sticker_release", in: ["hours_before_pick"] },
695
+ },
696
+ { key: "sticker_station", label: "Pickstation", type: "pickstation" },
697
+ {
698
+ key: "sticker_layout",
699
+ label: "Layout",
700
+ type: "layout",
701
+ layoutTypes: ["sticker", "etiket"],
702
+ description: "Welke layout uit de Layout maker gebruikt wordt voor stickers.",
703
+ },
704
+ ],
705
+ },
706
+ {
707
+ id: "verzendbrief",
708
+ label: "Verzendbrief",
709
+ fields: [
710
+ {
711
+ key: "shipping_note_release",
712
+ label: "Wanneer wordt de verzendbrief vrijgegeven?",
713
+ type: "radio",
714
+ default: "after_checkin",
715
+ options: [
716
+ { value: "after_checkin", label: "Na het aanmeldmoment van het transport" },
717
+ { value: "after_scan", label: "Automatisch afdrukken nadat een kar is gescand" },
718
+ { value: "with_packing_slip", label: "Gelijktijdig met de pakbon" },
719
+ { value: "manual_approval", label: "Pas na handmatige goedkeuring op een pickstation" },
720
+ ],
721
+ },
722
+ { key: "shipping_note_station", label: "Pickstation", type: "pickstation" },
723
+ {
724
+ key: "shipping_note_layout",
725
+ label: "Layout",
726
+ type: "layout",
727
+ layoutTypes: ["transportdocument", "verzendlabel"],
728
+ description: "Welke layout uit de Layout maker gebruikt wordt voor de verzendbrief.",
729
+ },
730
+ ],
731
+ },
732
+ {
733
+ id: "productiebon",
734
+ label: "Productiebon",
735
+ fields: [
736
+ {
737
+ key: "production_note_release",
738
+ label: "Wanneer wordt de productiebon vrijgegeven?",
739
+ type: "radio",
740
+ default: "on_stock_upgrade",
741
+ options: [
742
+ {
743
+ value: "on_stock_upgrade",
744
+ label: "Automatisch bij opwaardering van de voorraad",
745
+ description: "Zodra de voorraad van een samengesteld product wordt opgewaardeerd.",
746
+ },
747
+ {
748
+ value: "manual_approval_after_upgrade",
749
+ label: "Na opwaardering, maar pas na handmatige goedkeuring",
750
+ description: "De bon gaat pas naar het pickstation nadat iemand hem goedkeurt.",
751
+ },
752
+ ],
753
+ },
754
+ { key: "production_note_station", label: "Pickstation", type: "pickstation" },
755
+ {
756
+ key: "production_note_layout",
757
+ label: "Layout",
758
+ type: "layout",
759
+ layoutTypes: ["productiebon", "magazijnlijst"],
760
+ description: "Welke layout uit de Layout maker gebruikt wordt voor de productiebon.",
761
+ },
762
+ ],
763
+ },
764
+ ],
765
+ },
766
+ {
767
+ id: "controle",
768
+ label: "Controle",
769
+ icon: "shield-check",
770
+ description: "Bepaal of en hoe er gecontroleerd wordt.",
771
+ supportsConditions: true,
772
+ groups: [
773
+ {
774
+ id: "controle",
775
+ label: "Controlemoment",
776
+ fields: [
777
+ {
778
+ key: "control_notice_minutes",
779
+ label: "Melding karrencontrole",
780
+ type: "number",
781
+ min: 0,
782
+ max: 1440,
783
+ default: 60,
784
+ unit: "minuten vóór ophaalmoment",
785
+ description: "Zoveel minuten vóór het ophaalmoment krijgt de medewerker een melding dat er een karrencontrole moet plaatsvinden.",
786
+ },
787
+ {
788
+ key: "control_mode",
789
+ label: "Controlewijze",
790
+ type: "select",
791
+ default: "scanner",
792
+ options: [
793
+ { value: "scanner", label: "Scancontrole" },
794
+ { value: "paper", label: "Papieren controle" },
795
+ { value: "skip", label: "Controle overslaan" },
796
+ ],
797
+ },
798
+ {
799
+ key: "control_print_items",
800
+ label: "Wat wordt er uitgeprint?",
801
+ type: "select",
802
+ default: "control_note_and_shipping",
803
+ dependsOn: [{ key: "control_mode", in: ["paper"] }],
804
+ options: [
805
+ { value: "control_note", label: "Alleen controlebrief" },
806
+ { value: "control_note_and_shipping", label: "Controlebrief en verzendbrieven" },
807
+ ],
808
+ },
809
+ {
810
+ key: "sample_percentage",
811
+ label: "Steekproefpercentage",
812
+ type: "number",
813
+ min: 0,
814
+ max: 100,
815
+ default: 100,
816
+ unit: "%",
817
+ dependsOn: { key: "control_mode", in: ["scanner", "paper"] },
818
+ },
819
+ ],
820
+ },
821
+ ],
822
+ },
823
+ ];
824
+ export function defaultStepSettings(step) {
825
+ const fields = {};
826
+ for (const group of step.groups) {
827
+ for (const field of group.fields) {
828
+ if (field.type === "priority") {
829
+ fields[field.key] = (field.options ?? []).map((o) => ({ key: o.value, enabled: true }));
830
+ }
831
+ else if (field.default !== undefined) {
832
+ fields[field.key] = field.default;
833
+ }
834
+ else if (field.type === "toggle") {
835
+ fields[field.key] = false;
836
+ }
837
+ else {
838
+ fields[field.key] = "";
839
+ }
840
+ }
841
+ }
842
+ return { fields, conditions: [] };
843
+ }
844
+ export function defaultSettings() {
845
+ const result = {};
846
+ for (const step of RULE_STEPS) {
847
+ result[step.id] = defaultStepSettings(step);
848
+ }
849
+ return result;
850
+ }
851
+ export function normalizeSettings(raw) {
852
+ const base = defaultSettings();
853
+ if (!raw || typeof raw !== "object")
854
+ return base;
855
+ const source = raw;
856
+ for (const step of RULE_STEPS) {
857
+ const incoming = source[step.id];
858
+ if (!incoming || typeof incoming !== "object")
859
+ continue;
860
+ base[step.id] = {
861
+ fields: { ...base[step.id].fields, ...(incoming.fields ?? {}) },
862
+ conditions: Array.isArray(incoming.conditions) ? incoming.conditions : [],
863
+ };
864
+ }
865
+ return base;
866
+ }
867
+ export function fieldVisible(field, values, allSettings) {
868
+ if (!field.dependsOn)
869
+ return true;
870
+ const rules = Array.isArray(field.dependsOn) ? field.dependsOn : [field.dependsOn];
871
+ return rules.every((rule) => {
872
+ const source = rule.step
873
+ ? (allSettings?.[rule.step]?.fields ?? {})
874
+ : values;
875
+ let current = source[rule.key];
876
+ if (current === undefined && rule.step) {
877
+ current = defaultFieldValue(rule.step, rule.key);
878
+ }
879
+ return rule.in.includes(current);
880
+ });
881
+ }
882
+ function defaultFieldValue(stepId, key) {
883
+ const step = RULE_STEPS.find((s) => s.id === stepId);
884
+ for (const group of step?.groups ?? []) {
885
+ const field = group.fields.find((f) => f.key === key);
886
+ if (field)
887
+ return field.default;
888
+ }
889
+ return undefined;
890
+ }
891
+ export function optionLabel(field, value) {
892
+ return field.options?.find((o) => o.value === value)?.label ?? String(value ?? "—");
893
+ }
894
+ export function conditionFieldLabel(value) {
895
+ return CONDITION_FIELDS.find((f) => f.value === value)?.label ?? value;
896
+ }
897
+ export function conditionOperatorLabel(value) {
898
+ return CONDITION_OPERATORS.find((f) => f.value === value)?.label ?? value;
899
+ }