@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,9 @@
1
+ import type { RuleField } from "../../lib/ruleEngineSchema";
2
+ interface Props {
3
+ field: RuleField;
4
+ value: unknown;
5
+ onChange: (value: unknown) => void;
6
+ }
7
+ export declare function RuleFieldInput({ field, value, onChange }: Props): import("react").JSX.Element;
8
+ export {};
9
+ //# sourceMappingURL=RuleFieldInput.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RuleFieldInput.d.ts","sourceRoot":"","sources":["../../../src/components/rule-engine/RuleFieldInput.tsx"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAM3E,UAAU,KAAK;IACb,KAAK,EAAE,SAAS,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACpC;AAED,wBAAgB,cAAc,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,KAAK,+BAyP/D"}
@@ -0,0 +1,80 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // ============================================================================
3
+ // src/components/rule-engine/RuleFieldInput.tsx — rendert één schemaveld
4
+ // ============================================================================
5
+ // De uitleg staat niet meer onder het veld maar achter het vraagteken naast
6
+ // het label, zodat het scherm rustig blijft.
7
+ // ============================================================================
8
+ import { Input, Label, Switch, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Textarea, } from "@flowselections/core";
9
+ import { fieldHelp } from "../../lib/ruleEngineWizard";
10
+ import { FieldHelp, FieldLabel } from "./FieldHelp";
11
+ import { usePickstations } from "../../hooks/usePickstations";
12
+ import { useDocumentLayouts } from "../../hooks/useDocumentLayouts";
13
+ export function RuleFieldInput({ field, value, onChange }) {
14
+ const { stations, types, loading } = usePickstations();
15
+ const { layouts: matching, allLayouts, loading: layoutsLoading } = useDocumentLayouts(field.layoutTypes);
16
+ const help = fieldHelp(field);
17
+ if (field.type === "layout") {
18
+ const others = allLayouts.filter((l) => !matching.some((m) => m.id === l.id));
19
+ const options = [...matching, ...others];
20
+ return (_jsxs("div", { className: "space-y-2", children: [_jsx(FieldLabel, { label: field.label, help: help }), _jsxs(Select, { value: String(value ?? ""), onValueChange: (v) => onChange(v === "__none__" ? "" : v), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: layoutsLoading ? "Laden…" : "Kies een layout" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "__none__", children: "Automatisch kiezen" }), options.map((layout) => (_jsxs(SelectItem, { value: layout.id, children: [layout.name, " \u00B7 ", layout.documentType, " \u00B7 v", layout.version, layout.active ? "" : " (inactief)"] }, layout.id)))] })] }), !layoutsLoading && options.length === 0 && (_jsx("p", { className: "text-xs text-muted-foreground", children: "Er staan nog geen layouts klaar om te kiezen." }))] }));
21
+ }
22
+ if (field.type === "pickstation") {
23
+ const options = field.type === "pickstation" ? stations : types;
24
+ return (_jsxs("div", { className: "space-y-2", children: [_jsx(FieldLabel, { label: field.label, help: help }), _jsxs(Select, { value: String(value ?? ""), onValueChange: (v) => onChange(v === "__none__" ? "" : v), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: loading ? "Laden…" : "Kies een pickstation" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "__none__", children: "Niet instellen" }), options.map((option) => (_jsx(SelectItem, { value: option.id, children: option.name }, option.id)))] })] }), !loading && options.length === 0 && (_jsx("p", { className: "text-xs text-muted-foreground", children: "Er zijn nog geen pickstations ingericht." }))] }));
25
+ }
26
+ if (field.type === "days") {
27
+ const selected = Array.isArray(value) ? value : [];
28
+ const days = field.options ?? [];
29
+ const toggleDay = (day) => {
30
+ onChange(selected.includes(day) ? selected.filter((d) => d !== day) : [...selected, day]);
31
+ };
32
+ return (_jsxs("div", { className: "space-y-2", children: [_jsx(FieldLabel, { label: field.label, help: help }), _jsx("div", { className: "flex flex-wrap gap-2", children: days.map((day) => {
33
+ const active = selected.includes(day.value);
34
+ return (_jsx("button", { type: "button", onClick: () => toggleDay(day.value), className: `rounded-lg border px-3 py-2 text-sm font-medium transition-colors ${active
35
+ ? "border-primary bg-primary/10 text-primary"
36
+ : "border-border text-muted-foreground hover:bg-muted"}`, children: day.label }, day.value));
37
+ }) })] }));
38
+ }
39
+ if (field.type === "toggle") {
40
+ return (_jsxs("div", { className: "flex items-center justify-between gap-4 rounded-lg border border-border p-3", children: [_jsx(FieldLabel, { label: field.label, help: help }), _jsx(Switch, { checked: value === true, onCheckedChange: (checked) => onChange(checked) })] }));
41
+ }
42
+ if (field.type === "radio") {
43
+ const options = Array.isArray(field.options) ? field.options : [];
44
+ return (_jsxs("div", { className: "space-y-2", children: [_jsx(FieldLabel, { label: field.label, help: help }), _jsx("div", { className: "space-y-2", children: options.map((option) => {
45
+ const selected = String(value ?? "") === option.value;
46
+ return (_jsx("button", { type: "button", onClick: () => onChange(option.value), className: `w-full rounded-lg border p-3 text-left transition-colors ${selected ? "border-primary bg-primary/5 ring-1 ring-primary" : "border-border hover:bg-muted/50"}`, children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("span", { className: `flex h-4 w-4 shrink-0 items-center justify-center rounded-full border ${selected ? "border-primary" : "border-muted-foreground/40"}`, children: selected && _jsx("span", { className: "h-2 w-2 rounded-full bg-primary" }) }), _jsxs("span", { className: "flex items-center gap-2 text-sm font-medium", children: [option.label, option.badge && (_jsx("span", { className: "rounded bg-primary/10 px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide text-primary", children: option.badge })), _jsx(FieldHelp, { text: option.description })] })] }) }, option.value));
47
+ }) })] }));
48
+ }
49
+ if (field.type === "select") {
50
+ const options = Array.isArray(field.options) ? field.options : [];
51
+ return (_jsxs("div", { className: "space-y-2", children: [_jsx(FieldLabel, { label: field.label, help: help }), _jsxs(Select, { value: String(value ?? ""), onValueChange: (v) => onChange(v), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Kies een optie" }) }), _jsx(SelectContent, { children: options.map((option) => (_jsx(SelectItem, { value: option.value, children: option.label }, option.value))) })] })] }));
52
+ }
53
+ if (field.type === "number") {
54
+ return (_jsxs("div", { className: "space-y-2", children: [_jsx(FieldLabel, { label: field.label, help: help, unit: field.unit }), _jsx(Input, { type: "number", min: field.min, max: field.max, value: value === undefined || value === null ? "" : String(value), onChange: (e) => onChange(e.target.value === "" ? "" : Number(e.target.value)) })] }));
55
+ }
56
+ if (field.type === "textarea") {
57
+ return (_jsxs("div", { className: "space-y-2", children: [_jsx(FieldLabel, { label: field.label, help: help }), _jsx(Textarea, { rows: 3, value: String(value ?? ""), onChange: (e) => onChange(e.target.value), placeholder: "naam@bedrijf.nl, inkoop@bedrijf.nl" })] }));
58
+ }
59
+ if (field.type === "time") {
60
+ return (_jsxs("div", { className: "space-y-2", children: [_jsx(FieldLabel, { label: field.label, help: help }), _jsx(Input, { type: "time", value: String(value ?? ""), onChange: (e) => onChange(e.target.value) })] }));
61
+ }
62
+ if (field.type === "priority") {
63
+ const options = Array.isArray(field.options) ? field.options : [];
64
+ const stored = Array.isArray(value) ? value : [];
65
+ // Altijd op de actuele optielijst baseren, zodat nieuwe/hernoemde criteria verschijnen
66
+ const entries = options.map((o) => {
67
+ const found = stored.find((e) => e && e.key === o.value);
68
+ return { key: o.value, enabled: found ? found.enabled : true };
69
+ });
70
+ const toggle = (index) => {
71
+ const next = entries.map((entry, i) => (i === index ? { ...entry, enabled: !entry.enabled } : entry));
72
+ onChange(next);
73
+ };
74
+ return (_jsxs("div", { className: "space-y-2", children: [_jsx(FieldLabel, { label: field.label, help: help }), _jsx("ul", { className: "grid gap-2 sm:grid-cols-2", children: entries.map((entry, index) => {
75
+ const label = options.find((o) => o.value === entry.key)?.label ?? entry.key;
76
+ return (_jsxs("li", { className: "flex items-center gap-3 rounded-lg border border-border p-2.5", children: [_jsx("span", { className: entry.enabled ? "flex-1 text-sm" : "flex-1 text-sm text-muted-foreground line-through", children: label }), _jsx(Switch, { checked: entry.enabled, onCheckedChange: () => toggle(index) })] }, entry.key));
77
+ }) })] }));
78
+ }
79
+ return (_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { className: "text-sm font-medium", children: field.label }), _jsx(Input, { value: String(value ?? ""), onChange: (e) => onChange(e.target.value) })] }));
80
+ }
@@ -0,0 +1,10 @@
1
+ import type { RuleEngineSettings } from "../../lib/ruleEngineSchema";
2
+ import type { RuleVersion } from "../../hooks/useRuleEngine";
3
+ interface Props {
4
+ versions: RuleVersion[];
5
+ currentSettings: RuleEngineSettings;
6
+ onRestore: (version: RuleVersion) => void;
7
+ }
8
+ export declare function VersionsPanel({ versions, currentSettings, onRestore }: Props): import("react").JSX.Element;
9
+ export {};
10
+ //# sourceMappingURL=VersionsPanel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VersionsPanel.d.ts","sourceRoot":"","sources":["../../../src/components/rule-engine/VersionsPanel.tsx"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAErE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAE7D,UAAU,KAAK;IACb,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,eAAe,EAAE,kBAAkB,CAAC;IACpC,SAAS,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;CAC3C;AAED,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,eAAe,EAAE,SAAS,EAAE,EAAE,KAAK,+BAiF5E"}
@@ -0,0 +1,16 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // ============================================================================
3
+ // src/components/rule-engine/VersionsPanel.tsx — versiebeheer + vergelijking
4
+ // ============================================================================
5
+ import { useState } from "react";
6
+ import { Badge, Button, Card, CardContent, CardHeader, CardTitle, Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from "@flowselections/core";
7
+ import { GitCompare, History, RotateCcw } from "lucide-react";
8
+ import { diffSettings } from "../../lib/ruleEngineImpact";
9
+ export function VersionsPanel({ versions, currentSettings, onRestore }) {
10
+ const [compare, setCompare] = useState(null);
11
+ const safeVersions = Array.isArray(versions) ? versions : [];
12
+ const diff = compare ? diffSettings(compare.settings, currentSettings) : [];
13
+ return (_jsxs(Card, { children: [_jsx(CardHeader, { className: "pb-3", children: _jsxs(CardTitle, { className: "flex items-center gap-2 text-base", children: [_jsx(History, { className: "h-4 w-4" }), "Versies"] }) }), _jsx(CardContent, { children: safeVersions.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Nog geen versies opgeslagen. Gebruik \"Versie vastleggen\" om een momentopname te bewaren." })) : (_jsx("ul", { className: "space-y-2", children: safeVersions.map((version) => (_jsxs("li", { className: "flex items-center justify-between gap-2 rounded-lg border border-border p-2.5", children: [_jsxs("div", { className: "min-w-0", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("span", { className: "text-sm font-medium", children: ["Versie ", version.version] }), _jsx(Badge, { variant: "secondary", className: "text-[10px]", children: version.status })] }), _jsx("p", { className: "truncate text-xs text-muted-foreground", children: version.note ?? new Date(version.created_at).toLocaleString("nl-NL") })] }), _jsxs("div", { className: "flex shrink-0 gap-1", children: [_jsx(Button, { size: "icon", variant: "ghost", onClick: () => setCompare(version), "aria-label": "Vergelijken", children: _jsx(GitCompare, { className: "h-4 w-4" }) }), _jsx(Button, { size: "icon", variant: "ghost", onClick: () => onRestore(version), "aria-label": "Herstellen", children: _jsx(RotateCcw, { className: "h-4 w-4" }) })] })] }, version.id))) })) }), _jsx(Dialog, { open: !!compare, onOpenChange: (open) => !open && setCompare(null), children: _jsxs(DialogContent, { className: "max-h-[80vh] overflow-y-auto sm:max-w-2xl", children: [_jsxs(DialogHeader, { children: [_jsxs(DialogTitle, { children: ["Versie ", compare?.version, " vergelijken met huidig"] }), _jsx(DialogDescription, { children: diff.length === 0
14
+ ? "Geen verschillen gevonden."
15
+ : `${diff.length} verschil${diff.length === 1 ? "" : "len"} gevonden.` })] }), _jsx("ul", { className: "space-y-2", children: diff.map((line, index) => (_jsxs("li", { className: "rounded-lg border border-border p-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Badge, { variant: "secondary", className: "text-[10px]", children: line.step }), _jsx("span", { className: "text-sm font-medium", children: line.label })] }), _jsxs("p", { className: "mt-1 text-xs", children: [_jsx("span", { className: "text-muted-foreground line-through", children: line.before }), _jsx("span", { className: "mx-2", children: "\u2192" }), _jsx("span", { className: "font-medium", children: line.after })] })] }, index))) })] }) })] }));
16
+ }
@@ -0,0 +1,2 @@
1
+ export declare function LogistiekEngineSettingsCard(): import("react").JSX.Element;
2
+ //# sourceMappingURL=LogistiekEngineSettingsCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LogistiekEngineSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/components/settings/LogistiekEngineSettingsCard.tsx"],"names":[],"mappings":"AAcA,wBAAgB,2BAA2B,gCA2C1C"}
@@ -0,0 +1,22 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from "react";
3
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle, Button, Input, Label, toast, } from "@flowselections/core";
4
+ import { Settings2 } from "lucide-react";
5
+ export function LogistiekEngineSettingsCard() {
6
+ const [standaardVertrektijd, setStandaardVertrektijd] = useState("");
7
+ const [saving, setSaving] = useState(false);
8
+ const handleSave = async () => {
9
+ setSaving(true);
10
+ try {
11
+ // TODO: opslaan naar de instellingen-tabel van deze module
12
+ toast.success("Instellingen opgeslagen");
13
+ }
14
+ catch {
15
+ toast.error("Fout bij het opslaan");
16
+ }
17
+ finally {
18
+ setSaving(false);
19
+ }
20
+ };
21
+ return (_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Settings2, { className: "h-5 w-5 text-primary" }), _jsx(CardTitle, { children: "Logistiek Engine" })] }), _jsx(CardDescription, { children: "Instellingen voor het logistieke gedeelte van de orderflow" })] }), _jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "vertrektijd", children: "Standaard vertrektijd" }), _jsx(Input, { id: "vertrektijd", type: "time", value: standaardVertrektijd, onChange: (e) => setStandaardVertrektijd(e.target.value) })] }), _jsx(Button, { onClick: handleSave, disabled: saving, children: saving ? "Opslaan..." : "Opslaan" })] })] }));
22
+ }
@@ -0,0 +1,20 @@
1
+ export interface CapaciteitMelding {
2
+ id: string;
3
+ plan_date: string;
4
+ pickup_label: string | null;
5
+ required_minutes: number | null;
6
+ available_minutes: number | null;
7
+ shortage_minutes: number | null;
8
+ carts_total: number | null;
9
+ carts_short: number | null;
10
+ status: string;
11
+ created_at: string;
12
+ }
13
+ export declare function useCapaciteitMeldingen(): {
14
+ meldingen: CapaciteitMelding[];
15
+ staff: number;
16
+ loading: boolean;
17
+ reload: () => Promise<void>;
18
+ controleer: () => Promise<void>;
19
+ };
20
+ //# sourceMappingURL=useCapaciteitMeldingen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCapaciteitMeldingen.d.ts","sourceRoot":"","sources":["../../src/hooks/useCapaciteitMeldingen.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB;AAID,wBAAgB,sBAAsB;;;;;;EA4CrC"}
@@ -0,0 +1,46 @@
1
+ // ============================================================================
2
+ // src/hooks/useCapaciteitMeldingen.ts — capaciteitswaarschuwingen per pickdag
3
+ // ============================================================================
4
+ import { useCallback, useEffect, useState } from "react";
5
+ import { supabase, toast } from "@flowselections/core";
6
+ const db = () => supabase;
7
+ export function useCapaciteitMeldingen() {
8
+ const [meldingen, setMeldingen] = useState([]);
9
+ const [staff, setStaff] = useState(0);
10
+ const [loading, setLoading] = useState(true);
11
+ const load = useCallback(async () => {
12
+ setLoading(true);
13
+ const { data, error } = await db()
14
+ .from("logistics_capacity_alerts")
15
+ .select("*")
16
+ .eq("status", "open")
17
+ .order("plan_date", { ascending: true })
18
+ .limit(50);
19
+ setMeldingen(error || !Array.isArray(data) ? [] : data);
20
+ const { data: configs } = await db()
21
+ .from("logistics_rule_configs")
22
+ .select("settings, is_active, updated_at")
23
+ .order("is_active", { ascending: false })
24
+ .order("updated_at", { ascending: false })
25
+ .limit(1);
26
+ const settings = Array.isArray(configs) && configs[0] ? configs[0].settings : null;
27
+ const value = Number(settings?.order_picken?.fields?.avg_staff ?? 0);
28
+ setStaff(Number.isFinite(value) && value > 0 ? value : 0);
29
+ setLoading(false);
30
+ }, []);
31
+ const controleer = useCallback(async () => {
32
+ const today = new Date();
33
+ const from = today.toISOString().slice(0, 10);
34
+ const to = new Date(today.getTime() + 21 * 86400000).toISOString().slice(0, 10);
35
+ const { error } = await db().rpc("logistics_capacity_day_check", { p_from: from, p_to: to });
36
+ if (error)
37
+ toast.error(`Capaciteitscontrole mislukt: ${error.message}`);
38
+ else
39
+ toast.success("Capaciteit opnieuw berekend");
40
+ await load();
41
+ }, [load]);
42
+ useEffect(() => {
43
+ void load();
44
+ }, [load]);
45
+ return { meldingen, staff, loading, reload: load, controleer };
46
+ }
@@ -0,0 +1,13 @@
1
+ export interface DocumentLayoutOption {
2
+ id: string;
3
+ name: string;
4
+ documentType: string;
5
+ version: number;
6
+ active: boolean;
7
+ }
8
+ export declare function useDocumentLayouts(documentTypes?: string[]): {
9
+ layouts: DocumentLayoutOption[];
10
+ allLayouts: DocumentLayoutOption[];
11
+ loading: boolean;
12
+ };
13
+ //# sourceMappingURL=useDocumentLayouts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDocumentLayouts.d.ts","sourceRoot":"","sources":["../../src/hooks/useDocumentLayouts.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;CACjB;AA4BD,wBAAgB,kBAAkB,CAAC,aAAa,CAAC,EAAE,MAAM,EAAE;;;;EAyB1D"}
@@ -0,0 +1,54 @@
1
+ // ============================================================================
2
+ // src/hooks/useDocumentLayouts.ts — layouts uit de Layout maker, zodat je per
3
+ // documentsoort in de Logistiek Engine een layout kunt koppelen.
4
+ // ============================================================================
5
+ import { useEffect, useState } from "react";
6
+ import { supabase } from "@flowselections/core";
7
+ const db = () => supabase;
8
+ let cache = null;
9
+ let inflight = null;
10
+ async function load() {
11
+ if (cache)
12
+ return cache;
13
+ if (!inflight) {
14
+ inflight = (async () => {
15
+ const { data } = await db()
16
+ .from("document_layouts")
17
+ .select("id, naam, documenttype, huidige_versie, actief")
18
+ .order("naam", { ascending: true });
19
+ cache = (Array.isArray(data) ? data : []).map((r) => ({
20
+ id: String(r.id),
21
+ name: String(r.naam ?? r.id),
22
+ documentType: String(r.documenttype ?? ""),
23
+ version: Number(r.huidige_versie ?? 1),
24
+ active: r.actief !== false,
25
+ }));
26
+ return cache;
27
+ })();
28
+ }
29
+ return inflight;
30
+ }
31
+ export function useDocumentLayouts(documentTypes) {
32
+ const [layouts, setLayouts] = useState(cache ?? []);
33
+ const [loading, setLoading] = useState(!cache);
34
+ useEffect(() => {
35
+ let active = true;
36
+ load()
37
+ .then((rows) => {
38
+ if (active)
39
+ setLayouts(rows);
40
+ })
41
+ .catch(() => undefined)
42
+ .finally(() => {
43
+ if (active)
44
+ setLoading(false);
45
+ });
46
+ return () => {
47
+ active = false;
48
+ };
49
+ }, []);
50
+ const filtered = documentTypes && documentTypes.length > 0
51
+ ? layouts.filter((l) => documentTypes.includes(l.documentType))
52
+ : layouts;
53
+ return { layouts: filtered, allLayouts: layouts, loading };
54
+ }
@@ -0,0 +1,16 @@
1
+ import type { LoadCarrierTypeRow } from "../lib/loadCarrierTypes";
2
+ export type LadingdragerInput = Partial<Omit<LoadCarrierTypeRow, "id">> & {
3
+ code: string;
4
+ name: string;
5
+ };
6
+ export declare function useLadingdragers(): {
7
+ items: LoadCarrierTypeRow[];
8
+ loading: boolean;
9
+ saving: boolean;
10
+ error: string | null;
11
+ reload: () => Promise<void>;
12
+ create: (input: LadingdragerInput) => Promise<boolean>;
13
+ update: (id: string, input: Partial<LoadCarrierTypeRow>) => Promise<boolean>;
14
+ remove: (id: string) => Promise<boolean>;
15
+ };
16
+ //# sourceMappingURL=useLadingdragers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useLadingdragers.d.ts","sourceRoot":"","sources":["../../src/hooks/useLadingdragers.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAIlE,MAAM,MAAM,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,kBAAkB,EAAE,IAAI,CAAC,CAAC,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzG,wBAAgB,gBAAgB;;;;;;oBAkDW,iBAAiB;iBArBpB,MAAM,SAAS,OAAO,CAAC,kBAAkB,CAAC;iBAuC1C,MAAM;EAY7C"}
@@ -0,0 +1,86 @@
1
+ // src/hooks/useLadingdragers.ts — beheer van ladingdragertypen
2
+ import { useCallback, useEffect, useState } from "react";
3
+ import { supabase, toast } from "@flowselections/core";
4
+ const db = () => supabase;
5
+ export function useLadingdragers() {
6
+ const [items, setItems] = useState([]);
7
+ const [loading, setLoading] = useState(true);
8
+ const [saving, setSaving] = useState(false);
9
+ const [error, setError] = useState(null);
10
+ const load = useCallback(async () => {
11
+ setLoading(true);
12
+ setError(null);
13
+ try {
14
+ const { data, error } = await db()
15
+ .from("load_carrier_types")
16
+ .select("*")
17
+ .order("sort_order", { ascending: true })
18
+ .limit(100);
19
+ if (error)
20
+ throw error;
21
+ setItems(Array.isArray(data) ? data : []);
22
+ }
23
+ catch {
24
+ setError("Fout bij het laden van ladingdragers");
25
+ toast.error("Fout bij het laden van ladingdragers");
26
+ }
27
+ finally {
28
+ setLoading(false);
29
+ }
30
+ }, []);
31
+ useEffect(() => {
32
+ load();
33
+ }, [load]);
34
+ const update = useCallback(async (id, input) => {
35
+ setSaving(true);
36
+ try {
37
+ const { data, error } = await db()
38
+ .from("load_carrier_types")
39
+ .update(input)
40
+ .eq("id", id)
41
+ .select()
42
+ .single();
43
+ if (error)
44
+ throw error;
45
+ setItems((prev) => prev.map((i) => (i.id === id ? data : i)));
46
+ toast.success("Ladingdrager bijgewerkt");
47
+ return true;
48
+ }
49
+ catch (e) {
50
+ toast.error(e?.message ?? "Bijwerken mislukt");
51
+ return false;
52
+ }
53
+ finally {
54
+ setSaving(false);
55
+ }
56
+ }, []);
57
+ const create = useCallback(async (input) => {
58
+ setSaving(true);
59
+ try {
60
+ const { data, error } = await db().from("load_carrier_types").insert(input).select().single();
61
+ if (error)
62
+ throw error;
63
+ setItems((prev) => [...prev, data].sort((a, b) => a.sort_order - b.sort_order));
64
+ toast.success("Ladingdrager toegevoegd");
65
+ return true;
66
+ }
67
+ catch (e) {
68
+ toast.error(e?.message ?? "Toevoegen mislukt");
69
+ return false;
70
+ }
71
+ finally {
72
+ setSaving(false);
73
+ }
74
+ }, []);
75
+ const remove = useCallback(async (id) => {
76
+ const { error } = await db().from("load_carrier_types").delete().eq("id", id);
77
+ if (error) {
78
+ toast.error("Verwijderen mislukt");
79
+ return false;
80
+ }
81
+ setItems((prev) => prev.filter((i) => i.id !== id));
82
+ toast.success("Ladingdrager verwijderd");
83
+ return true;
84
+ }, []);
85
+ return { items, loading, saving, error, reload: load, create, update, remove };
86
+ }
@@ -0,0 +1,29 @@
1
+ export interface LogistiekStrategie {
2
+ id: string;
3
+ name: string;
4
+ description: string | null;
5
+ priority: number;
6
+ color: string;
7
+ is_active: boolean;
8
+ version: number;
9
+ [key: string]: unknown;
10
+ }
11
+ export interface StrategieInput {
12
+ name: string;
13
+ description: string;
14
+ priority: number;
15
+ color: string;
16
+ is_active: boolean;
17
+ }
18
+ export declare function useLogistiekStrategieen(): {
19
+ items: LogistiekStrategie[];
20
+ loading: boolean;
21
+ saving: boolean;
22
+ error: string | null;
23
+ reload: () => Promise<void>;
24
+ create: (input: StrategieInput) => Promise<boolean>;
25
+ update: (id: string, input: Partial<StrategieInput>) => Promise<boolean>;
26
+ remove: (id: string) => Promise<boolean>;
27
+ toggleActive: (item: LogistiekStrategie) => Promise<boolean>;
28
+ };
29
+ //# sourceMappingURL=useLogistiekStrategieen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useLogistiekStrategieen.d.ts","sourceRoot":"","sources":["../../src/hooks/useLogistiekStrategieen.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;CACpB;AAID,wBAAgB,uBAAuB;;;;;;oBA6CI,cAAc;iBA0BjB,MAAM,SAAS,OAAO,CAAC,cAAc,CAAC;iBA2BtC,MAAM;yBAYnC,kBAAkB;EAK5B"}
@@ -0,0 +1,111 @@
1
+ // src/hooks/useLogistiekStrategieen.ts — data hook voor logistieke strategieën
2
+ import { useState, useEffect, useCallback } from "react";
3
+ import { supabase, toast } from "@flowselections/core";
4
+ const db = () => supabase;
5
+ export function useLogistiekStrategieen() {
6
+ const [items, setItems] = useState([]);
7
+ const [loading, setLoading] = useState(true);
8
+ const [saving, setSaving] = useState(false);
9
+ const [error, setError] = useState(null);
10
+ const load = useCallback(async () => {
11
+ setLoading(true);
12
+ setError(null);
13
+ try {
14
+ const { data, error } = await db()
15
+ .from("logistics_strategies")
16
+ .select("*")
17
+ .order("priority", { ascending: true })
18
+ .limit(100);
19
+ if (error)
20
+ throw error;
21
+ setItems(Array.isArray(data) ? data : []);
22
+ }
23
+ catch {
24
+ setError("Fout bij het laden van logistieke strategieën");
25
+ toast.error("Fout bij het laden van gegevens");
26
+ }
27
+ finally {
28
+ setLoading(false);
29
+ }
30
+ }, []);
31
+ useEffect(() => {
32
+ load();
33
+ const channel = supabase
34
+ .channel("logistics_strategies_changes")
35
+ .on("postgres_changes", { event: "*", schema: "public", table: "logistics_strategies" }, () => {
36
+ load();
37
+ })
38
+ .subscribe();
39
+ return () => {
40
+ supabase.removeChannel(channel);
41
+ };
42
+ }, [load]);
43
+ const create = useCallback(async (input) => {
44
+ setSaving(true);
45
+ try {
46
+ const { data: userData } = await supabase.auth.getUser();
47
+ const userId = userData?.user?.id;
48
+ if (!userId) {
49
+ toast.error("Je bent niet ingelogd");
50
+ return false;
51
+ }
52
+ const { data, error } = await db()
53
+ .from("logistics_strategies")
54
+ .insert({ ...input, description: input.description || null, user_id: userId })
55
+ .select()
56
+ .single();
57
+ if (error)
58
+ throw error;
59
+ setItems((prev) => [...prev, data].sort((a, b) => a.priority - b.priority));
60
+ toast.success("Strategie aangemaakt");
61
+ return true;
62
+ }
63
+ catch (e) {
64
+ toast.error(e?.message ?? "Aanmaken mislukt");
65
+ return false;
66
+ }
67
+ finally {
68
+ setSaving(false);
69
+ }
70
+ }, []);
71
+ const update = useCallback(async (id, input) => {
72
+ setSaving(true);
73
+ try {
74
+ const payload = { ...input };
75
+ if ("description" in payload)
76
+ payload.description = input.description || null;
77
+ const { data, error } = await db()
78
+ .from("logistics_strategies")
79
+ .update(payload)
80
+ .eq("id", id)
81
+ .select()
82
+ .single();
83
+ if (error)
84
+ throw error;
85
+ setItems((prev) => prev
86
+ .map((item) => (item.id === id ? data : item))
87
+ .sort((a, b) => a.priority - b.priority));
88
+ toast.success("Strategie bijgewerkt");
89
+ return true;
90
+ }
91
+ catch (e) {
92
+ toast.error(e?.message ?? "Bijwerken mislukt");
93
+ return false;
94
+ }
95
+ finally {
96
+ setSaving(false);
97
+ }
98
+ }, []);
99
+ const remove = useCallback(async (id) => {
100
+ const { error } = await db().from("logistics_strategies").delete().eq("id", id);
101
+ if (error) {
102
+ toast.error("Verwijderen mislukt");
103
+ return false;
104
+ }
105
+ setItems((prev) => prev.filter((item) => item.id !== id));
106
+ toast.success("Strategie verwijderd");
107
+ return true;
108
+ }, []);
109
+ const toggleActive = useCallback((item) => update(item.id, { is_active: !item.is_active }), [update]);
110
+ return { items, loading, saving, error, reload: load, create, update, remove, toggleActive };
111
+ }