@flowselections/floriday-afhandeling-module 1.0.25 → 1.0.26
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.
- package/dist-lib/components/afhandeling/CapacityWarning.d.ts +10 -0
- package/dist-lib/components/afhandeling/CapacityWarning.d.ts.map +1 -0
- package/dist-lib/components/afhandeling/CapacityWarning.js +12 -0
- package/dist-lib/components/afhandeling/ControlePage.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/ControlePage.js +7 -2
- package/dist-lib/components/afhandeling/DagDetailDialog.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/DagDetailDialog.js +158 -62
- package/dist-lib/components/afhandeling/LeveringenPage.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/LeveringenPage.js +2 -10
- package/dist-lib/components/afhandeling/PickAgendaView.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/PickAgendaView.js +4 -11
- package/dist-lib/components/afhandeling/VervoerdersPage.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/VervoerdersPage.js +2 -5
- package/dist-lib/components/afhandeling/VerzetOrderDialog.d.ts +14 -0
- package/dist-lib/components/afhandeling/VerzetOrderDialog.d.ts.map +1 -0
- package/dist-lib/components/afhandeling/VerzetOrderDialog.js +203 -0
- package/dist-lib/components/afhandeling/WeekAgendaView.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/WeekAgendaView.js +7 -3
- package/dist-lib/components/pickstation/PickstationPage.d.ts.map +1 -1
- package/dist-lib/components/pickstation/PickstationPage.js +1 -0
- package/dist-lib/components/settings/VervoerdersSettingsCard.d.ts.map +1 -1
- package/dist-lib/components/settings/VervoerdersSettingsCard.js +2 -5
- package/dist-lib/hooks/useCapacityCheck.d.ts +10 -0
- package/dist-lib/hooks/useCapacityCheck.d.ts.map +1 -0
- package/dist-lib/hooks/useCapacityCheck.js +22 -0
- package/dist-lib/hooks/useEngineTasks.d.ts +14 -0
- package/dist-lib/hooks/useEngineTasks.d.ts.map +1 -0
- package/dist-lib/hooks/useEngineTasks.js +49 -0
- package/dist-lib/hooks/useOrderPlanning.d.ts +8 -2
- package/dist-lib/hooks/useOrderPlanning.d.ts.map +1 -1
- package/dist-lib/hooks/useOrderPlanning.js +95 -93
- package/dist-lib/index.d.ts +0 -3
- package/dist-lib/index.d.ts.map +1 -1
- package/dist-lib/index.js +1 -5
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +9010 -5433
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.d.ts +9010 -5433
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +9010 -5433
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +9184 -5544
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/afhandelingSettings.functions.d.ts +7186 -32
- package/dist-lib/lib/afhandelingSettings.functions.d.ts.map +1 -1
- package/dist-lib/lib/carriers.functions.d.ts +17965 -80
- package/dist-lib/lib/carriers.functions.d.ts.map +1 -1
- package/dist-lib/lib/cartAssignments.functions.d.ts +17965 -80
- package/dist-lib/lib/cartAssignments.functions.d.ts.map +1 -1
- package/dist-lib/lib/cartControl.functions.d.ts +35962 -170
- package/dist-lib/lib/cartControl.functions.d.ts.map +1 -1
- package/dist-lib/lib/cartControl.functions.js +103 -13
- package/dist-lib/lib/floriday.functions.d.ts +9010 -5433
- package/dist-lib/lib/floriday.functions.d.ts.map +1 -1
- package/dist-lib/lib/logisticsEngine/capacity.d.ts +19 -0
- package/dist-lib/lib/logisticsEngine/capacity.d.ts.map +1 -0
- package/dist-lib/lib/logisticsEngine/capacity.js +91 -0
- package/dist-lib/lib/logisticsEngine/engine.d.ts +99 -0
- package/dist-lib/lib/logisticsEngine/engine.d.ts.map +1 -0
- package/dist-lib/lib/logisticsEngine/engine.js +102 -0
- package/dist-lib/lib/logisticsEngine/reschedule.d.ts +65 -0
- package/dist-lib/lib/logisticsEngine/reschedule.d.ts.map +1 -0
- package/dist-lib/lib/logisticsEngine/reschedule.js +86 -0
- package/dist-lib/lib/logisticsEngine/staffPlanning.d.ts +37 -0
- package/dist-lib/lib/logisticsEngine/staffPlanning.d.ts.map +1 -0
- package/dist-lib/lib/logisticsEngine/staffPlanning.js +100 -0
- package/dist-lib/lib/serverFnError.d.ts +10 -0
- package/dist-lib/lib/serverFnError.d.ts.map +1 -0
- package/dist-lib/lib/serverFnError.js +29 -0
- package/dist-lib/lib/serverRoutes/index.d.ts +0 -1
- package/dist-lib/lib/serverRoutes/index.d.ts.map +1 -1
- package/dist-lib/lib/serverRoutes/index.js +0 -1
- package/dist-lib/lib/timezone.d.ts +27 -0
- package/dist-lib/lib/timezone.d.ts.map +1 -0
- package/dist-lib/lib/timezone.js +68 -0
- package/package.json +6 -21
- package/dist-lib/components/engine/CapaciteitsmaatregelenPage.d.ts +0 -2
- package/dist-lib/components/engine/CapaciteitsmaatregelenPage.d.ts.map +0 -1
- package/dist-lib/components/engine/CapaciteitsmaatregelenPage.js +0 -40
- package/dist-lib/components/engine/CapaciteitsmonitorPage.d.ts +0 -2
- package/dist-lib/components/engine/CapaciteitsmonitorPage.d.ts.map +0 -1
- package/dist-lib/components/engine/CapaciteitsmonitorPage.js +0 -49
- package/dist-lib/components/engine/FormulesPage.d.ts +0 -2
- package/dist-lib/components/engine/FormulesPage.d.ts.map +0 -1
- package/dist-lib/components/engine/FormulesPage.js +0 -252
- package/dist-lib/components/engine/PlaceholderPage.d.ts +0 -6
- package/dist-lib/components/engine/PlaceholderPage.d.ts.map +0 -1
- package/dist-lib/components/engine/PlaceholderPage.js +0 -6
- package/dist-lib/components/engine/StrategieenPage.d.ts +0 -2
- package/dist-lib/components/engine/StrategieenPage.d.ts.map +0 -1
- package/dist-lib/components/engine/StrategieenPage.js +0 -80
- package/dist-lib/lib/logisticsEngine/capacity.functions.d.ts +0 -64978
- package/dist-lib/lib/logisticsEngine/capacity.functions.d.ts.map +0 -1
- package/dist-lib/lib/logisticsEngine/capacity.functions.js +0 -90
- package/dist-lib/lib/logisticsEngine/config.d.ts +0 -140
- package/dist-lib/lib/logisticsEngine/config.d.ts.map +0 -1
- package/dist-lib/lib/logisticsEngine/config.js +0 -488
- package/dist-lib/lib/logisticsEngine/enginePickTime.d.ts +0 -29
- package/dist-lib/lib/logisticsEngine/enginePickTime.d.ts.map +0 -1
- package/dist-lib/lib/logisticsEngine/enginePickTime.js +0 -136
- package/dist-lib/lib/logisticsEngine/evaluate.functions.d.ts +0 -16278
- package/dist-lib/lib/logisticsEngine/evaluate.functions.d.ts.map +0 -1
- package/dist-lib/lib/logisticsEngine/evaluate.functions.js +0 -104
- package/dist-lib/lib/logisticsEngine/expression.d.ts +0 -21
- package/dist-lib/lib/logisticsEngine/expression.d.ts.map +0 -1
- package/dist-lib/lib/logisticsEngine/expression.js +0 -263
- package/dist-lib/lib/logisticsEngine/formulas.functions.d.ts +0 -32512
- package/dist-lib/lib/logisticsEngine/formulas.functions.d.ts.map +0 -1
- package/dist-lib/lib/logisticsEngine/formulas.functions.js +0 -58
- package/dist-lib/lib/logisticsEngine/safetyLayer.d.ts +0 -83
- package/dist-lib/lib/logisticsEngine/safetyLayer.d.ts.map +0 -1
- package/dist-lib/lib/logisticsEngine/safetyLayer.js +0 -182
- package/dist-lib/lib/logisticsEngine/strategies.functions.d.ts +0 -129961
- package/dist-lib/lib/logisticsEngine/strategies.functions.d.ts.map +0 -1
- package/dist-lib/lib/logisticsEngine/strategies.functions.js +0 -176
- package/dist-lib/lib/logisticsEngine/variables.d.ts +0 -29
- package/dist-lib/lib/logisticsEngine/variables.d.ts.map +0 -1
- package/dist-lib/lib/logisticsEngine/variables.js +0 -144
- package/dist-lib/lib/logisticsEngine/wizard.functions.d.ts +0 -64976
- package/dist-lib/lib/logisticsEngine/wizard.functions.d.ts.map +0 -1
- package/dist-lib/lib/logisticsEngine/wizard.functions.js +0 -175
- package/dist-lib/lib/serverRoutes/logisticsTick.handler.d.ts +0 -3
- package/dist-lib/lib/serverRoutes/logisticsTick.handler.d.ts.map +0 -1
- package/dist-lib/lib/serverRoutes/logisticsTick.handler.js +0 -42
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useMemo, useState } from "react";
|
|
3
|
+
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
4
|
+
import { useServerFn } from "@tanstack/react-start";
|
|
5
|
+
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, Badge, Button, Dialog, DialogTitle, DialogDescription, Input, Label, Textarea, toast, } from "@flowselections/core";
|
|
6
|
+
import { ResponsiveDialogBody, ResponsiveDialogContent, ResponsiveDialogFooter, ResponsiveDialogHeader, } from "../../components/ui/responsive-dialog";
|
|
7
|
+
import { applyReschedule, listRescheduleHistory, previewReschedule, getPickMoments, setPickDay, } from "../../lib/logisticsEngine/reschedule";
|
|
8
|
+
import { ENGINE_TASKS_KEY } from "../../hooks/useEngineTasks";
|
|
9
|
+
import { listCarriers } from "../../lib/carriers.functions";
|
|
10
|
+
import { normalizePickupMoments, selectCarrierForTarget } from "../../lib/carrierPlanning";
|
|
11
|
+
import { safeServerCall } from "../../lib/serverFnError";
|
|
12
|
+
import { amsterdamDay, formatAmsterdam } from "../../lib/timezone";
|
|
13
|
+
function fmtDateTime(v) {
|
|
14
|
+
if (!v)
|
|
15
|
+
return "—";
|
|
16
|
+
return formatAmsterdam(v, { dateStyle: "full", timeStyle: "short" }) || v;
|
|
17
|
+
}
|
|
18
|
+
function toDayInput(v) {
|
|
19
|
+
if (!v)
|
|
20
|
+
return "";
|
|
21
|
+
return amsterdamDay(v);
|
|
22
|
+
}
|
|
23
|
+
function fmtDate(v) {
|
|
24
|
+
if (!v)
|
|
25
|
+
return "—";
|
|
26
|
+
return formatAmsterdam(v, { dateStyle: "short" }) || v;
|
|
27
|
+
}
|
|
28
|
+
export function VerzetOrderDialog({ open, orderRef, carrierTarget, onOpenChange }) {
|
|
29
|
+
const qc = useQueryClient();
|
|
30
|
+
const fetchCarriers = useServerFn(listCarriers);
|
|
31
|
+
const [pickupDate, setPickupDate] = useState("");
|
|
32
|
+
const [pickupFrom, setPickupFrom] = useState("");
|
|
33
|
+
const [pickupTo, setPickupTo] = useState("");
|
|
34
|
+
const [confirmOpen, setConfirmOpen] = useState(false);
|
|
35
|
+
const [reason, setReason] = useState("");
|
|
36
|
+
const [preview, setPreview] = useState(null);
|
|
37
|
+
const [loadingPreview, setLoadingPreview] = useState(false);
|
|
38
|
+
const [saving, setSaving] = useState(false);
|
|
39
|
+
const [history, setHistory] = useState([]);
|
|
40
|
+
const [pickMoments, setPickMoments] = useState([]);
|
|
41
|
+
const [pickDay, setPickDayValue] = useState("");
|
|
42
|
+
const carriersQuery = useQuery({
|
|
43
|
+
queryKey: ["carriers"],
|
|
44
|
+
enabled: open,
|
|
45
|
+
queryFn: () => safeServerCall(() => fetchCarriers(), { carriers: [] }, "listCarriers"),
|
|
46
|
+
retry: false,
|
|
47
|
+
});
|
|
48
|
+
const carrier = useMemo(() => {
|
|
49
|
+
const carriers = carriersQuery.data?.carriers ?? [];
|
|
50
|
+
if (carriers.length === 0)
|
|
51
|
+
return null;
|
|
52
|
+
return selectCarrierForTarget(carriers, {
|
|
53
|
+
carrierId: carrierTarget?.carrierId ?? null,
|
|
54
|
+
country: carrierTarget?.country ?? null,
|
|
55
|
+
province: carrierTarget?.province ?? null,
|
|
56
|
+
city: carrierTarget?.city ?? null,
|
|
57
|
+
});
|
|
58
|
+
}, [carriersQuery.data, carrierTarget?.carrierId, carrierTarget?.country, carrierTarget?.province, carrierTarget?.city]);
|
|
59
|
+
const carrierMoments = useMemo(() => (carrier ? normalizePickupMoments(carrier.pickup_moments) : []), [carrier]);
|
|
60
|
+
// Voorvertoning live opvragen bij elke wijziging in het formulier.
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
if (!open)
|
|
63
|
+
return;
|
|
64
|
+
let cancelled = false;
|
|
65
|
+
setLoadingPreview(true);
|
|
66
|
+
const timer = setTimeout(async () => {
|
|
67
|
+
const res = await previewReschedule({
|
|
68
|
+
orderRef,
|
|
69
|
+
pickupDate: pickupDate || null,
|
|
70
|
+
pickupFrom: pickupFrom || null,
|
|
71
|
+
pickupTo: pickupTo || null,
|
|
72
|
+
});
|
|
73
|
+
if (cancelled)
|
|
74
|
+
return;
|
|
75
|
+
setPreview(res);
|
|
76
|
+
setLoadingPreview(false);
|
|
77
|
+
}, 250);
|
|
78
|
+
return () => {
|
|
79
|
+
cancelled = true;
|
|
80
|
+
clearTimeout(timer);
|
|
81
|
+
};
|
|
82
|
+
}, [open, orderRef, pickupDate, pickupFrom, pickupTo]);
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
if (!open)
|
|
85
|
+
return;
|
|
86
|
+
void listRescheduleHistory(orderRef).then(setHistory);
|
|
87
|
+
void getPickMoments(orderRef).then((m) => {
|
|
88
|
+
setPickMoments(m);
|
|
89
|
+
setPickDayValue(toDayInput(m[0]?.plannedStart));
|
|
90
|
+
});
|
|
91
|
+
}, [open, orderRef]);
|
|
92
|
+
// Aanmeldmoment voorvullen met de huidige waarden, zodat het zichtbaar én wijzigbaar is.
|
|
93
|
+
useEffect(() => {
|
|
94
|
+
if (!open || !preview?.current)
|
|
95
|
+
return;
|
|
96
|
+
setPickupDate((v) => v || (preview.current.pickupDate ?? "").slice(0, 10));
|
|
97
|
+
setPickupFrom((v) => v || (preview.current.pickupFrom ?? "").slice(0, 5));
|
|
98
|
+
setPickupTo((v) => v || (preview.current.pickupTo ?? "").slice(0, 5));
|
|
99
|
+
}, [open, preview?.current?.pickupDate, preview?.current?.pickupFrom, preview?.current?.pickupTo]);
|
|
100
|
+
const currentPickDay = toDayInput(pickMoments[0]?.plannedStart);
|
|
101
|
+
const pickDayChanged = !!pickDay && pickDay !== currentPickDay;
|
|
102
|
+
const transportChanged = (!!pickupDate && pickupDate !== (preview?.current?.pickupDate ?? "").slice(0, 10)) ||
|
|
103
|
+
(!!pickupFrom && pickupFrom !== (preview?.current?.pickupFrom ?? "").slice(0, 5)) ||
|
|
104
|
+
(!!pickupTo && pickupTo !== (preview?.current?.pickupTo ?? "").slice(0, 5));
|
|
105
|
+
const direction = preview?.direction;
|
|
106
|
+
const needsCustomerChoice = direction === "later" && preview?.customerNoticeRequired !== false;
|
|
107
|
+
const blocked = !preview || preview.ok === false;
|
|
108
|
+
const summary = useMemo(() => {
|
|
109
|
+
if (!preview || preview.ok === false)
|
|
110
|
+
return null;
|
|
111
|
+
const shift = Math.abs(preview.dayShift ?? 0);
|
|
112
|
+
if (direction === "earlier") {
|
|
113
|
+
return `Order wordt ${shift} dag(en) eerder opgehaald. Het aflevermoment blijft ${fmtDate(preview.current?.deliveryDate)}.`;
|
|
114
|
+
}
|
|
115
|
+
if (direction === "later") {
|
|
116
|
+
return `Order wordt ${shift} dag(en) later. Nieuwe leverdatum: ${fmtDate(preview.next?.deliveryDate)}.`;
|
|
117
|
+
}
|
|
118
|
+
if (preview.timeChanged) {
|
|
119
|
+
return `Nieuwe ophaaltijd: ${preview.next?.pickupFrom ?? "—"} – ${preview.next?.pickupTo ?? "—"}.`;
|
|
120
|
+
}
|
|
121
|
+
return "Nog geen wijziging ingevuld.";
|
|
122
|
+
}, [preview, direction]);
|
|
123
|
+
const submit = async (notifyCustomer) => {
|
|
124
|
+
if (saving)
|
|
125
|
+
return;
|
|
126
|
+
setConfirmOpen(false);
|
|
127
|
+
setSaving(true);
|
|
128
|
+
try {
|
|
129
|
+
// Alleen de pickdag gewijzigd? Dan niets naar de Engine sturen: die zou
|
|
130
|
+
// "geen wijziging" terugmelden en de pickdag zou nooit worden opgeslagen.
|
|
131
|
+
if (!transportChanged) {
|
|
132
|
+
const res = await setPickDay(orderRef, pickDay);
|
|
133
|
+
if (!res.ok) {
|
|
134
|
+
toast.error(res.error ?? "Pickdag verzetten mislukt");
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
await Promise.all([
|
|
138
|
+
qc.invalidateQueries({ queryKey: ENGINE_TASKS_KEY }),
|
|
139
|
+
qc.invalidateQueries({ queryKey: ["floriday", "orders"] }),
|
|
140
|
+
]);
|
|
141
|
+
toast.success(`Pickdag verzet naar ${fmtDate(pickDay)} (${res.updated} pick-taak/taken).`);
|
|
142
|
+
onOpenChange(false);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const result = await applyReschedule({
|
|
146
|
+
orderRef,
|
|
147
|
+
pickupDate: pickupDate || null,
|
|
148
|
+
pickupFrom: pickupFrom || null,
|
|
149
|
+
pickupTo: pickupTo || null,
|
|
150
|
+
notifyCustomer,
|
|
151
|
+
reason: reason.trim() || undefined,
|
|
152
|
+
});
|
|
153
|
+
if (result.ok === false || !result.applied) {
|
|
154
|
+
toast.error(result.error ?? "Verzetten mislukt");
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
if (pickDayChanged) {
|
|
158
|
+
const res = await setPickDay(orderRef, pickDay);
|
|
159
|
+
if (!res.ok)
|
|
160
|
+
toast.error(res.error ?? "Pickdag verzetten mislukt");
|
|
161
|
+
}
|
|
162
|
+
// Taken opnieuw laden — de Engine heeft alles al herpland.
|
|
163
|
+
await Promise.all([
|
|
164
|
+
qc.invalidateQueries({ queryKey: ENGINE_TASKS_KEY }),
|
|
165
|
+
qc.invalidateQueries({ queryKey: ["floriday", "orders"] }),
|
|
166
|
+
]);
|
|
167
|
+
if (notifyCustomer && !result.customerNotified) {
|
|
168
|
+
toast.error(`Verzet gelukt, maar de klantmail is niet verstuurd${result.customerNotifyError ? `: ${result.customerNotifyError}` : "."}`);
|
|
169
|
+
}
|
|
170
|
+
toast.success(`Verzet naar ${fmtDate(result.next?.pickupDate ?? result.current?.pickupDate)} ` +
|
|
171
|
+
`(${result.next?.pickupFrom ?? "—"}–${result.next?.pickupTo ?? "—"}), ` +
|
|
172
|
+
`leverdatum ${fmtDate(result.next?.deliveryDate)}. ` +
|
|
173
|
+
(result.customerNotified
|
|
174
|
+
? "De Logistiek Engine heeft de klant per e-mail geïnformeerd."
|
|
175
|
+
: "Klant is niet geïnformeerd."));
|
|
176
|
+
onOpenChange(false);
|
|
177
|
+
}
|
|
178
|
+
finally {
|
|
179
|
+
setSaving(false);
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
const handleVerzetten = () => {
|
|
183
|
+
if (saving)
|
|
184
|
+
return;
|
|
185
|
+
if (!pickDayChanged && !transportChanged)
|
|
186
|
+
return;
|
|
187
|
+
if (transportChanged && blocked)
|
|
188
|
+
return;
|
|
189
|
+
// Alleen bij een latere leverdatum vragen we of de klant geïnformeerd moet worden.
|
|
190
|
+
if (needsCustomerChoice) {
|
|
191
|
+
setConfirmOpen(true);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
void submit(false);
|
|
195
|
+
};
|
|
196
|
+
return (_jsxs(Dialog, { open: open, onOpenChange: onOpenChange, children: [_jsxs(ResponsiveDialogContent, { size: "lg", children: [_jsxs(ResponsiveDialogHeader, { children: [_jsxs(DialogTitle, { children: ["Order verzetten \u2014 ", orderRef] }), _jsx(DialogDescription, { children: "De Logistics Engine bepaalt de gevolgen; deze module past niets zelf aan." })] }), _jsxs(ResponsiveDialogBody, { className: "space-y-4", children: [_jsxs("div", { className: "rounded-md border p-3 space-y-2", children: [_jsx("p", { className: "text-xs font-medium text-muted-foreground", children: "Pickmoment (dag waarop de order gemaakt wordt)" }), pickMoments.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Nog geen pickmoment van de Engine." })) : (_jsxs(_Fragment, { children: [_jsxs("p", { className: "text-sm", children: ["Nu ingepland: ", fmtDateTime(pickMoments[0]?.plannedStart)] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Nieuwe pickdag" }), _jsx(Input, { type: "date", value: pickDay, onChange: (e) => setPickDayValue(e.target.value), className: "sm:w-56" })] }), pickDayChanged && (_jsxs("p", { className: "text-xs text-muted-foreground", children: [pickMoments.length, " pick-taak(en) worden naar deze dag verzet; het tijdstip blijft gelijk."] }))] }))] }), _jsxs("div", { className: "rounded-md border p-3 space-y-2", children: [_jsxs("p", { className: "text-xs font-medium text-muted-foreground", children: ["Vervoerder", carrier ? `: ${carrier.name}` : ""] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Vervoersdatum" }), _jsx(Input, { type: "date", value: pickupDate, onChange: (e) => setPickupDate(e.target.value), className: "sm:w-56" })] }), carrierMoments.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Geen vervoerder gekoppeld \u2014 aanmeldtijden zijn niet bekend." })) : (_jsxs(_Fragment, { children: [_jsxs("p", { className: "text-sm", children: ["Huidige aanmeldtijd: ", pickupFrom || "—", pickupTo && pickupTo !== pickupFrom ? `–${pickupTo}` : ""] }), _jsx("div", { className: "flex flex-wrap gap-2", children: carrierMoments.map((m, i) => {
|
|
197
|
+
const active = pickupFrom === m.signup_time;
|
|
198
|
+
return (_jsxs(Button, { type: "button", size: "sm", variant: active ? "default" : "outline", onClick: () => {
|
|
199
|
+
setPickupFrom(m.signup_time);
|
|
200
|
+
setPickupTo(m.signup_time);
|
|
201
|
+
}, children: [m.signup_time, _jsxs("span", { className: "ml-1 text-xs opacity-70", children: ["(", m.signup_days, " wd vooraf \u00B7 ophalen ", m.pickup_time, ")"] })] }, `${m.signup_time}-${i}`));
|
|
202
|
+
}) }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Kies een aanmeldtijd van deze vervoerder." })] }))] }), preview?.current && (_jsxs("p", { className: "text-xs text-muted-foreground", children: ["Huidig aanmeldmoment: ", fmtDate(preview.current.pickupDate), " ", preview.current.pickupFrom ?? "—", "\u2013", preview.current.pickupTo ?? "—", " \u00B7 leverdatum ", fmtDate(preview.current.deliveryDate)] })), loadingPreview && _jsx("p", { className: "text-sm text-muted-foreground", children: "Voorvertoning ophalen\u2026" }), preview && preview.ok === false && (_jsx("div", { className: "rounded-md border border-destructive/30 bg-destructive/5 p-3 text-sm text-destructive", children: preview.error ?? "De Engine kan deze wijziging niet verwerken." })), preview && preview.ok !== false && (_jsxs("div", { className: "rounded-md border bg-muted/40 p-3 text-sm space-y-2", children: [_jsx("p", { children: summary }), _jsxs("p", { className: "text-xs text-muted-foreground", children: [preview.affectedTasks ?? 0, " taken worden opnieuw ingepland."] }), preview.planLocked && (_jsx(Badge, { variant: "outline", children: "Planning is vergrendeld door de Engine" }))] })), needsCustomerChoice && (_jsxs("div", { className: "space-y-1 rounded-md border p-3", children: [_jsx(Label, { children: "Reden voor de klantmelding (optioneel)" }), _jsx(Textarea, { value: reason, onChange: (e) => setReason(e.target.value), maxLength: 500, placeholder: "Bijv. Aanvoer bloemen vertraagd" })] })), history.length > 0 && (_jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-xs font-medium text-muted-foreground", children: "Verzetgeschiedenis" }), _jsx("ul", { className: "space-y-1 text-xs text-muted-foreground", children: history.map((h) => (_jsxs("li", { children: [new Date(h.created_at).toLocaleString("nl-NL"), " \u2014", " ", String(h.direction ?? ""), " ", String(h.reason ?? "")] }, h.id))) })] }))] }), _jsxs(ResponsiveDialogFooter, { children: [_jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), children: "Annuleren" }), _jsx(Button, { onClick: handleVerzetten, disabled: saving || loadingPreview || (!pickDayChanged && (!transportChanged || blocked)), children: saving ? "Opslaan…" : "Verzetten" })] })] }), _jsx(AlertDialog, { open: confirmOpen, onOpenChange: setConfirmOpen, children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Klant informeren?" }), _jsx(AlertDialogDescription, { children: "De leverdatum is gewijzigd naar een latere datum. Wilt u de klant hierover informeren?" })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { onClick: () => void submit(false), children: "Nee" }), _jsx(AlertDialogAction, { onClick: () => void submit(true), children: "Ja" })] })] }) })] }));
|
|
203
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WeekAgendaView.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/WeekAgendaView.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAElE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"WeekAgendaView.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/WeekAgendaView.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAElE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAUjE,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,QAAQ,GAAG,oBAAoB,GAAG,MAAM,CAAC;AAEnF,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,IAAI,EAAE,cAAc,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC9C,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+DAA+D;IAC/D,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AA8DD,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,IAAI,EACJ,SAAS,EACT,YAAY,EACZ,UAAU,EACV,KAAK,EACL,gBAAgB,GACjB,EAAE,mBAAmB,+BAiKrB"}
|
|
@@ -4,6 +4,9 @@ import { Button, Badge, toast } from "@flowselections/core";
|
|
|
4
4
|
import { ChevronLeft, ChevronRight, Package, Truck, Sprout, Clock, CheckCircle2, Scale, Printer } from "lucide-react";
|
|
5
5
|
import { isSameDay, startOfWeek, addDays } from "../../lib/pickDate";
|
|
6
6
|
import { printDeliveryNote, printDeliveryNotesMerged, } from "../../lib/deliveryNotePrint";
|
|
7
|
+
import { useCapacityCheck } from "../../hooks/useCapacityCheck";
|
|
8
|
+
import { CapacityWarning } from "./CapacityWarning";
|
|
9
|
+
import { toDayKey } from "../../lib/logisticsEngine/capacity";
|
|
7
10
|
const WEEKDAY_LABELS = ["MAANDAG", "DINSDAG", "WOENSDAG", "DONDERDAG", "VRIJDAG", "ZATERDAG", "ZONDAG"];
|
|
8
11
|
const MONTHS_NL_SHORT = ["jan", "feb", "mrt", "apr", "mei", "jun", "jul", "aug", "sep", "okt", "nov", "dec"];
|
|
9
12
|
function isOrderReady(o) {
|
|
@@ -142,6 +145,7 @@ export function WeekAgendaView({ planned, mode, isLoading, onOrderClick, onDayCl
|
|
|
142
145
|
const totaalStuks = days.reduce((s, d) => s + d.planten, 0);
|
|
143
146
|
return { leveringen, verzendklaar, voorbereiding, totaalStuks };
|
|
144
147
|
}, [days]);
|
|
148
|
+
const capacity = useCapacityCheck(toDayKey(weekStart), toDayKey(weekEnd));
|
|
145
149
|
const headerLabel = `Week ${weekNumber} - ${weekStart.getDate()} ${MONTHS_NL_SHORT[weekStart.getMonth()]}. t/m ${weekEnd.getDate()} ${MONTHS_NL_SHORT[weekEnd.getMonth()]}. ${weekEnd.getFullYear()}`;
|
|
146
150
|
const goPrev = () => setCursor((c) => addDays(c, -7));
|
|
147
151
|
const goNext = () => setCursor((c) => addDays(c, 7));
|
|
@@ -150,13 +154,13 @@ export function WeekAgendaView({ planned, mode, isLoading, onOrderClick, onDayCl
|
|
|
150
154
|
d.setHours(0, 0, 0, 0);
|
|
151
155
|
setCursor(startOfWeek(d));
|
|
152
156
|
};
|
|
153
|
-
return (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsx("h2", { className: "text-xl font-semibold tracking-tight", children: title ?? headerLabel }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: goPrev, children: [_jsx(ChevronLeft, { className: "mr-1 h-4 w-4" }), " Vorige week"] }), _jsx(Button, { variant: "outline", size: "sm", onClick: goToday, children: "Deze week" }), _jsxs(Button, { variant: "outline", size: "sm", onClick: goNext, children: ["Volgende week ", _jsx(ChevronRight, { className: "ml-1 h-4 w-4" })] })] })] }), _jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4", children: [_jsx(SummaryCard, { label: MODE_LABELS[mode].summary, value: totals.leveringen, tone: "default" }), _jsx(SummaryCard, { label: "Verzendklaar", value: totals.verzendklaar, tone: "success" }), _jsx(SummaryCard, { label: "In voorbereiding", value: totals.voorbereiding, tone: "muted" }), _jsx(SummaryCard, { label: "Totaal stuks", value: totals.totaalStuks, tone: "default" })] }), _jsx("div", { className: "space-y-2", children: isLoading ? (_jsx("div", { className: "rounded-lg border bg-card p-10 text-center text-sm text-muted-foreground", children: "Laden\u2026" })) : (days.map((day) => (_jsx(DayRow, { day: day, onOrderClick: onOrderClick, onDayClick: onDayClick, showPrintButtons: showPrintButtons }, day.date.toISOString())))) })] }));
|
|
157
|
+
return (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsx("h2", { className: "text-xl font-semibold tracking-tight", children: title ?? headerLabel }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: goPrev, children: [_jsx(ChevronLeft, { className: "mr-1 h-4 w-4" }), " Vorige week"] }), _jsx(Button, { variant: "outline", size: "sm", onClick: goToday, children: "Deze week" }), _jsxs(Button, { variant: "outline", size: "sm", onClick: goNext, children: ["Volgende week ", _jsx(ChevronRight, { className: "ml-1 h-4 w-4" })] })] })] }), _jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4", children: [_jsx(SummaryCard, { label: MODE_LABELS[mode].summary, value: totals.leveringen, tone: "default" }), _jsx(SummaryCard, { label: "Verzendklaar", value: totals.verzendklaar, tone: "success" }), _jsx(SummaryCard, { label: "In voorbereiding", value: totals.voorbereiding, tone: "muted" }), _jsx(SummaryCard, { label: "Totaal stuks", value: totals.totaalStuks, tone: "default" })] }), _jsx("div", { className: "max-h-[520px] space-y-2 overflow-y-auto rounded-lg border bg-muted/20 p-2", children: isLoading ? (_jsx("div", { className: "rounded-lg border bg-card p-10 text-center text-sm text-muted-foreground", children: "Laden\u2026" })) : (days.map((day) => (_jsx(DayRow, { day: day, onOrderClick: onOrderClick, onDayClick: onDayClick, showPrintButtons: showPrintButtons, capacity: mode === "delivery" ? undefined : capacity.byDate.get(toDayKey(day.date)) }, day.date.toISOString())))) })] }));
|
|
154
158
|
}
|
|
155
159
|
function SummaryCard({ label, value, tone, }) {
|
|
156
160
|
const valueClass = tone === "success" ? "text-green-600" : "text-foreground";
|
|
157
161
|
return (_jsxs("div", { className: "rounded-lg border bg-card p-4", children: [_jsx("div", { className: `text-3xl font-bold ${valueClass}`, children: value }), _jsx("div", { className: "mt-1 text-sm text-muted-foreground", children: label })] }));
|
|
158
162
|
}
|
|
159
|
-
function DayRow({ day, onOrderClick, onDayClick, showPrintButtons, }) {
|
|
163
|
+
function DayRow({ day, onOrderClick, onDayClick, showPrintButtons, capacity, }) {
|
|
160
164
|
const weekdayIdx = (day.date.getDay() + 6) % 7;
|
|
161
165
|
const today = new Date();
|
|
162
166
|
today.setHours(0, 0, 0, 0);
|
|
@@ -200,7 +204,7 @@ function DayRow({ day, onOrderClick, onDayClick, showPrintButtons, }) {
|
|
|
200
204
|
setPrintingOrderId(null);
|
|
201
205
|
}
|
|
202
206
|
};
|
|
203
|
-
return (
|
|
207
|
+
return (_jsxs("div", { className: `rounded-lg border bg-card ${isToday ? "ring-1 ring-primary" : ""}`, children: [_jsxs("div", { className: "flex flex-wrap items-center gap-6 px-4 py-4 sm:px-6", children: [_jsxs("div", { className: "w-20 shrink-0", children: [_jsx("div", { className: "text-xs font-medium tracking-wider text-muted-foreground", children: WEEKDAY_LABELS[weekdayIdx] }), _jsx("div", { className: "mt-1 text-3xl font-bold leading-none", children: day.date.getDate() }), _jsxs("div", { className: "text-sm text-muted-foreground", children: [MONTHS_NL_SHORT[day.date.getMonth()], "."] })] }), _jsxs("div", { className: "flex flex-1 flex-wrap items-center gap-6", children: [_jsx(Stat, { icon: _jsx(Package, { className: "h-4 w-4 text-slate-500" }), bg: "bg-slate-100", value: day.bestellingen, label: "bestellingen" }), _jsx(Stat, { icon: _jsx(Truck, { className: "h-4 w-4 text-amber-600" }), bg: "bg-amber-100", value: day.karren, label: "karren" }), _jsx(Stat, { icon: _jsx(Sprout, { className: "h-4 w-4 text-green-600" }), bg: "bg-green-100", value: day.planten, label: "planten" })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [day.movedEarlierOrderIds.size > 0 && (_jsxs(Badge, { className: "bg-purple-500/10 text-purple-700 hover:bg-purple-500/10", variant: "secondary", title: "Aantal orders dat vervroegd is ingepland door Pickverdeling.", children: [_jsx(Scale, { className: "mr-1 h-3.5 w-3.5" }), day.movedEarlierOrderIds.size, " vervroegd"] })), day.verzendklaar > 0 && (_jsxs(Badge, { className: "bg-green-500/15 text-green-700 hover:bg-green-500/15", variant: "secondary", children: [_jsx(CheckCircle2, { className: "mr-1 h-3.5 w-3.5" }), day.verzendklaar, " verzendklaar"] })), day.voorbereiding > 0 && (_jsxs(Badge, { className: "bg-blue-500/10 text-blue-700 hover:bg-blue-500/10", variant: "secondary", children: [_jsx(Clock, { className: "mr-1 h-3.5 w-3.5" }), day.voorbereiding, " in voorbereiding"] })), showPrintButtons && printableItems.length > 0 && (_jsxs(Button, { size: "sm", variant: "outline", onClick: handlePrintDay, disabled: printingDay, children: [_jsx(Printer, { className: "mr-1 h-4 w-4" }), printingDay ? "Printen…" : `Print afleverbrieven (${printableItems.length})`] })), onDayClick && day.bestellingen > 0 && (_jsx(Button, { size: "sm", variant: "outline", onClick: () => onDayClick(day.date), children: "Dagdetails" }))] })] }), _jsx(CapacityWarning, { day: capacity, className: "mx-4 mb-4 sm:mx-6" })] }));
|
|
204
208
|
}
|
|
205
209
|
function Stat({ icon, bg, value, label, }) {
|
|
206
210
|
return (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: `grid h-8 w-8 place-items-center rounded-full ${bg}`, children: icon }), _jsxs("div", { children: [_jsx("div", { className: "text-lg font-bold leading-none", children: value }), _jsx("div", { className: "text-xs text-muted-foreground", children: label })] })] }));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PickstationPage.d.ts","sourceRoot":"","sources":["../../../src/components/pickstation/PickstationPage.tsx"],"names":[],"mappings":"AA2EA,wBAAgB,eAAe,
|
|
1
|
+
{"version":3,"file":"PickstationPage.d.ts","sourceRoot":"","sources":["../../../src/components/pickstation/PickstationPage.tsx"],"names":[],"mappings":"AA2EA,wBAAgB,eAAe,gCA6Z9B"}
|
|
@@ -264,6 +264,7 @@ export function PickstationPage() {
|
|
|
264
264
|
orderSource: o.orderSource,
|
|
265
265
|
cartIndex: o.cartIndex - 1,
|
|
266
266
|
orderNumber: o.orderNumber,
|
|
267
|
+
orderNumbers: [o.orderNumber].filter(Boolean),
|
|
267
268
|
customerName: o.customerName,
|
|
268
269
|
carrierName: o.carrierName,
|
|
269
270
|
deliveryDate: o.deliveryDate,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VervoerdersSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/components/settings/VervoerdersSettingsCard.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"VervoerdersSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/components/settings/VervoerdersSettingsCard.tsx"],"names":[],"mappings":"AAeA,wBAAgB,uBAAuB,gCAoDtC"}
|
|
@@ -4,6 +4,7 @@ import { Building2, Truck, ArrowRight } from "lucide-react";
|
|
|
4
4
|
import { Link } from "@tanstack/react-router";
|
|
5
5
|
import { useServerFn } from "@tanstack/react-start";
|
|
6
6
|
import { useQuery } from "@tanstack/react-query";
|
|
7
|
+
import { toServerFnError } from "../../lib/serverFnError";
|
|
7
8
|
import { listCarriers } from "../../lib/carriers.functions";
|
|
8
9
|
export function VervoerdersSettingsCard() {
|
|
9
10
|
const fetchList = useServerFn(listCarriers);
|
|
@@ -14,11 +15,7 @@ export function VervoerdersSettingsCard() {
|
|
|
14
15
|
return await fetchList();
|
|
15
16
|
}
|
|
16
17
|
catch (e) {
|
|
17
|
-
|
|
18
|
-
const text = await e.text().catch(() => e.statusText);
|
|
19
|
-
throw new Error(`${e.status}: ${text}`);
|
|
20
|
-
}
|
|
21
|
-
throw e;
|
|
18
|
+
throw await toServerFnError(e);
|
|
22
19
|
}
|
|
23
20
|
},
|
|
24
21
|
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type CapacityDay } from "../lib/logisticsEngine/capacity";
|
|
2
|
+
/**
|
|
3
|
+
* Capaciteitscontrole van de Logistics Engine voor een periode.
|
|
4
|
+
* Wordt opnieuw opgehaald bij elke periodewissel (from/to zitten in de queryKey).
|
|
5
|
+
*/
|
|
6
|
+
export declare function useCapacityCheck(from: string | null, to: string | null): {
|
|
7
|
+
byDate: Map<string, CapacityDay>;
|
|
8
|
+
isLoading: boolean;
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=useCapacityCheck.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCapacityCheck.d.ts","sourceRoot":"","sources":["../../src/hooks/useCapacityCheck.ts"],"names":[],"mappings":"AACA,OAAO,EAAmB,KAAK,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAEpF;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI;;;EAgBtE"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useQuery } from "@tanstack/react-query";
|
|
2
|
+
import { getCapacityDays } from "../lib/logisticsEngine/capacity";
|
|
3
|
+
/**
|
|
4
|
+
* Capaciteitscontrole van de Logistics Engine voor een periode.
|
|
5
|
+
* Wordt opnieuw opgehaald bij elke periodewissel (from/to zitten in de queryKey).
|
|
6
|
+
*/
|
|
7
|
+
export function useCapacityCheck(from, to) {
|
|
8
|
+
const query = useQuery({
|
|
9
|
+
queryKey: ["logistics-capacity-day-check", from, to],
|
|
10
|
+
queryFn: () => getCapacityDays(from, to),
|
|
11
|
+
enabled: !!from && !!to,
|
|
12
|
+
staleTime: 60000,
|
|
13
|
+
retry: false,
|
|
14
|
+
refetchInterval: 60000,
|
|
15
|
+
refetchIntervalInBackground: false,
|
|
16
|
+
refetchOnWindowFocus: true,
|
|
17
|
+
});
|
|
18
|
+
const byDate = new Map();
|
|
19
|
+
for (const d of query.data ?? [])
|
|
20
|
+
byDate.set(d.date, d);
|
|
21
|
+
return { byDate, isLoading: query.isLoading };
|
|
22
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type EngineTask } from "../lib/logisticsEngine/engine";
|
|
2
|
+
export declare const ENGINE_TASKS_KEY: string[];
|
|
3
|
+
export declare function useEngineTasks(options?: {
|
|
4
|
+
taskTypes?: string[];
|
|
5
|
+
statuses?: string[];
|
|
6
|
+
}): {
|
|
7
|
+
tasks: EngineTask[];
|
|
8
|
+
isLoading: boolean;
|
|
9
|
+
error: string | undefined;
|
|
10
|
+
refetch: () => undefined;
|
|
11
|
+
};
|
|
12
|
+
/** Taken per order_ref, gesorteerd op sequence. */
|
|
13
|
+
export declare function groupTasksByOrder(tasks: EngineTask[]): Map<string, EngineTask[]>;
|
|
14
|
+
//# sourceMappingURL=useEngineTasks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useEngineTasks.d.ts","sourceRoot":"","sources":["../../src/hooks/useEngineTasks.ts"],"names":[],"mappings":"AAGA,OAAO,EAAmB,KAAK,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAEjF,eAAO,MAAM,gBAAgB,UAAsB,CAAC;AAIpD,wBAAgB,cAAc,CAAC,OAAO,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE;;;;;EAsCrF;AAED,mDAAmD;AACnD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,CAAC,CAShF"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { useEffect, useMemo } from "react";
|
|
2
|
+
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
3
|
+
import { supabase } from "../integrations/supabase/client";
|
|
4
|
+
import { listEngineTasks } from "../lib/logisticsEngine/engine";
|
|
5
|
+
export const ENGINE_TASKS_KEY = ["engine", "tasks"];
|
|
6
|
+
const OPEN_STATUSES = ["planned", "ready", "in_progress"];
|
|
7
|
+
export function useEngineTasks(options) {
|
|
8
|
+
const qc = useQueryClient();
|
|
9
|
+
const taskTypes = options?.taskTypes;
|
|
10
|
+
const statuses = options?.statuses ?? OPEN_STATUSES;
|
|
11
|
+
const query = useQuery({
|
|
12
|
+
queryKey: [...ENGINE_TASKS_KEY, taskTypes ?? "all", statuses],
|
|
13
|
+
queryFn: () => listEngineTasks({ taskTypes, statuses }),
|
|
14
|
+
retry: false,
|
|
15
|
+
refetchInterval: 60000,
|
|
16
|
+
refetchIntervalInBackground: false,
|
|
17
|
+
refetchOnWindowFocus: true,
|
|
18
|
+
});
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
const channel = supabase
|
|
21
|
+
.channel("engine-tasks")
|
|
22
|
+
.on("postgres_changes", { event: "*", schema: "public", table: "logistics_tasks" }, () => {
|
|
23
|
+
void qc.invalidateQueries({ queryKey: ENGINE_TASKS_KEY });
|
|
24
|
+
})
|
|
25
|
+
.subscribe();
|
|
26
|
+
return () => {
|
|
27
|
+
void supabase.removeChannel(channel);
|
|
28
|
+
};
|
|
29
|
+
}, [qc]);
|
|
30
|
+
const tasks = useMemo(() => query.data ?? [], [query.data]);
|
|
31
|
+
return {
|
|
32
|
+
tasks,
|
|
33
|
+
isLoading: query.isLoading,
|
|
34
|
+
error: query.error instanceof Error ? query.error.message : undefined,
|
|
35
|
+
refetch: () => void query.refetch(),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/** Taken per order_ref, gesorteerd op sequence. */
|
|
39
|
+
export function groupTasksByOrder(tasks) {
|
|
40
|
+
const map = new Map();
|
|
41
|
+
for (const t of tasks) {
|
|
42
|
+
const list = map.get(t.order_ref) ?? [];
|
|
43
|
+
list.push(t);
|
|
44
|
+
map.set(t.order_ref, list);
|
|
45
|
+
}
|
|
46
|
+
for (const list of map.values())
|
|
47
|
+
list.sort((a, b) => a.sequence - b.sequence);
|
|
48
|
+
return map;
|
|
49
|
+
}
|
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
import type { FloridayOrder } from "../lib/floriday.functions";
|
|
2
|
-
import { type CartOverride } from "../lib/cartAssignments.functions";
|
|
3
2
|
import { type OrderPlanning } from "../lib/planning";
|
|
3
|
+
import { type EngineTask } from "../lib/logisticsEngine/engine";
|
|
4
4
|
export interface PlannedOrder {
|
|
5
5
|
order: FloridayOrder;
|
|
6
|
+
/** Planning volgens de Logistics Engine. Null zolang er geen taak is. */
|
|
6
7
|
planning: OrderPlanning | null;
|
|
8
|
+
/** De leidende load.compose-taak uit de Engine (indien aanwezig). */
|
|
9
|
+
engineTask: EngineTask | null;
|
|
10
|
+
/** True zolang de Engine nog geen load.compose-taak heeft geleverd. */
|
|
11
|
+
awaitingEngine: boolean;
|
|
7
12
|
}
|
|
8
13
|
export interface UseOrderPlanningResult {
|
|
9
14
|
planned: PlannedOrder[];
|
|
10
|
-
|
|
15
|
+
/** Orders die op een Engine-taak wachten en dus niet zijn ingedeeld. */
|
|
16
|
+
awaiting: PlannedOrder[];
|
|
11
17
|
isLoading: boolean;
|
|
12
18
|
error?: string;
|
|
13
19
|
warning?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useOrderPlanning.d.ts","sourceRoot":"","sources":["../../src/hooks/useOrderPlanning.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"useOrderPlanning.d.ts","sourceRoot":"","sources":["../../src/hooks/useOrderPlanning.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAM/D,OAAO,EAGL,KAAK,aAAa,EAEnB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAmB,KAAK,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAEjF,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,aAAa,CAAC;IACrB,yEAAyE;IACzE,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;IAC/B,qEAAqE;IACrE,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,uEAAuE;IACvE,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,wEAAwE;IACxE,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAMD,wBAAgB,gBAAgB,IAAI,sBAAsB,CA6LzD"}
|