@flowselections/floriday-afhandeling-module 1.0.31 → 1.0.33
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/AutoProcessStatus.js +1 -1
- package/dist-lib/components/afhandeling/ControleLijstPrint.d.ts +6 -6
- package/dist-lib/components/afhandeling/ControleLijstPrint.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/ControleLijstPrint.js +73 -48
- package/dist-lib/components/afhandeling/ControlePage.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/ControlePage.js +47 -14
- package/dist-lib/components/afhandeling/DagDetailDialog.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/DagDetailDialog.js +48 -24
- package/dist-lib/components/afhandeling/VervoerdersPage.d.ts +8 -1
- package/dist-lib/components/afhandeling/VervoerdersPage.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/VervoerdersPage.js +17 -2
- package/dist-lib/components/afhandeling/WeekAgendaView.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/WeekAgendaView.js +16 -5
- package/dist-lib/components/afhandeling/WerkplekWaarschuwing.d.ts +10 -0
- package/dist-lib/components/afhandeling/WerkplekWaarschuwing.d.ts.map +1 -0
- package/dist-lib/components/afhandeling/WerkplekWaarschuwing.js +28 -0
- package/dist-lib/components/pickstation/PakbonTemplate.js +70 -70
- package/dist-lib/components/pickstation/printDocuments.d.ts +9 -2
- package/dist-lib/components/pickstation/printDocuments.d.ts.map +1 -1
- package/dist-lib/components/pickstation/printDocuments.js +8 -52
- package/dist-lib/components/settings/AfhandelingSettingsCard.d.ts.map +1 -1
- package/dist-lib/components/settings/AfhandelingSettingsCard.js +12 -1
- package/dist-lib/hooks/useEngineTasks.js +1 -1
- package/dist-lib/hooks/useOrderPlanning.d.ts.map +1 -1
- package/dist-lib/hooks/useOrderPlanning.js +32 -14
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +695 -128
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +695 -128
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/{client.d.ts → moduleClient.d.ts} +697 -129
- package/dist-lib/integrations/supabase/moduleClient.d.ts.map +1 -0
- package/dist-lib/integrations/supabase/moduleClient.js +38 -0
- package/dist-lib/integrations/supabase/types.d.ts +727 -155
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.js +3 -0
- package/dist-lib/lib/afhandelingSettings.functions.d.ts +1390 -256
- package/dist-lib/lib/afhandelingSettings.functions.d.ts.map +1 -1
- package/dist-lib/lib/carrierSignupMail.server.js +26 -26
- package/dist-lib/lib/carriers.functions.d.ts +3475 -640
- package/dist-lib/lib/carriers.functions.d.ts.map +1 -1
- package/dist-lib/lib/cartAssignments.functions.d.ts +3475 -640
- package/dist-lib/lib/cartAssignments.functions.d.ts.map +1 -1
- package/dist-lib/lib/cartControl.functions.d.ts +6950 -1280
- package/dist-lib/lib/cartControl.functions.d.ts.map +1 -1
- package/dist-lib/lib/deliveryNotePrint.d.ts +14 -6
- package/dist-lib/lib/deliveryNotePrint.d.ts.map +1 -1
- package/dist-lib/lib/deliveryNotePrint.js +61 -14
- package/dist-lib/lib/floriday.functions.d.ts +695 -128
- package/dist-lib/lib/floriday.functions.d.ts.map +1 -1
- package/dist-lib/lib/floridayAutoProcess.js +1 -1
- package/dist-lib/lib/logisticsEngine/capacity.js +1 -1
- package/dist-lib/lib/logisticsEngine/emitQueue.d.ts +20 -0
- package/dist-lib/lib/logisticsEngine/emitQueue.d.ts.map +1 -0
- package/dist-lib/lib/logisticsEngine/emitQueue.js +88 -0
- package/dist-lib/lib/logisticsEngine/engine.js +1 -1
- package/dist-lib/lib/logisticsEngine/reschedule.js +1 -1
- package/dist-lib/lib/logisticsEngine/staffPlanning.js +1 -1
- package/dist-lib/lib/printing.d.ts +56 -0
- package/dist-lib/lib/printing.d.ts.map +1 -0
- package/dist-lib/lib/printing.js +209 -0
- package/package.json +3 -7
- package/dist-lib/integrations/supabase/auth-attacher.d.ts +0 -2
- package/dist-lib/integrations/supabase/auth-attacher.d.ts.map +0 -1
- package/dist-lib/integrations/supabase/auth-attacher.js +0 -12
- package/dist-lib/integrations/supabase/client.d.ts.map +0 -1
- package/dist-lib/integrations/supabase/client.js +0 -13
|
@@ -2,6 +2,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
3
3
|
import { Button, Badge, toast } from "@flowselections/core";
|
|
4
4
|
import { ChevronLeft, ChevronRight, Package, Truck, Sprout, Clock, CheckCircle2, Scale, Printer } from "lucide-react";
|
|
5
|
+
import { printFoutMelding } from "../../lib/printing";
|
|
6
|
+
import { WerkplekWaarschuwing, useWerkplek } from "./WerkplekWaarschuwing";
|
|
5
7
|
import { isSameDay, startOfWeek, addDays } from "../../lib/pickDate";
|
|
6
8
|
import { printDeliveryNote, printDeliveryNotesMerged, } from "../../lib/deliveryNotePrint";
|
|
7
9
|
import { useCapacityCheck } from "../../hooks/useCapacityCheck";
|
|
@@ -144,12 +146,17 @@ export function WeekAgendaView({ planned, mode, isLoading, onOrderClick, onDayCl
|
|
|
144
146
|
d.setHours(0, 0, 0, 0);
|
|
145
147
|
setCursor(startOfWeek(d));
|
|
146
148
|
};
|
|
147
|
-
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" })] }),
|
|
149
|
+
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" })] }), _jsxs("div", { className: "max-h-[520px] space-y-2 overflow-y-auto rounded-lg border bg-muted/20 p-2", children: [showPrintButtons && _jsx(AgendaWerkplekBanner, {}), 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()))))] })] }));
|
|
148
150
|
}
|
|
149
151
|
function SummaryCard({ label, value, tone, }) {
|
|
150
152
|
const valueClass = tone === "success" ? "text-green-600" : "text-foreground";
|
|
151
153
|
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 })] }));
|
|
152
154
|
}
|
|
155
|
+
/** Eén melding bovenaan de agenda zolang de werkplek ontbreekt. */
|
|
156
|
+
function AgendaWerkplekBanner() {
|
|
157
|
+
const werkplek = useWerkplek();
|
|
158
|
+
return _jsx(WerkplekWaarschuwing, { werkplek: werkplek });
|
|
159
|
+
}
|
|
153
160
|
function DayRow({ day, onOrderClick, onDayClick, showPrintButtons, capacity, }) {
|
|
154
161
|
const weekdayIdx = (day.date.getDay() + 6) % 7;
|
|
155
162
|
const today = new Date();
|
|
@@ -160,6 +167,7 @@ function DayRow({ day, onOrderClick, onDayClick, showPrintButtons, capacity, })
|
|
|
160
167
|
const printableItems = day.entries
|
|
161
168
|
.filter((e) => e.order.source === "floriday" && e.order.salesOrderId)
|
|
162
169
|
.map((e) => ({ id: e.order.id, salesOrderId: e.order.salesOrderId ?? undefined }));
|
|
170
|
+
const werkplek = useWerkplek();
|
|
163
171
|
const handlePrintDay = async () => {
|
|
164
172
|
if (printingDay || printableItems.length === 0)
|
|
165
173
|
return;
|
|
@@ -174,7 +182,7 @@ function DayRow({ day, onOrderClick, onDayClick, showPrintButtons, capacity, })
|
|
|
174
182
|
}
|
|
175
183
|
}
|
|
176
184
|
catch (err) {
|
|
177
|
-
toast.error(err
|
|
185
|
+
toast.error(printFoutMelding(err));
|
|
178
186
|
}
|
|
179
187
|
finally {
|
|
180
188
|
setPrintingDay(false);
|
|
@@ -185,16 +193,19 @@ function DayRow({ day, onOrderClick, onDayClick, showPrintButtons, capacity, })
|
|
|
185
193
|
return;
|
|
186
194
|
setPrintingOrderId(order.id);
|
|
187
195
|
try {
|
|
188
|
-
await printDeliveryNote(order.salesOrderId
|
|
196
|
+
await printDeliveryNote(order.salesOrderId, {
|
|
197
|
+
documentnaam: `AFL-${order.orderNumber}`,
|
|
198
|
+
});
|
|
199
|
+
toast.success(`Afleverbrief ${order.orderNumber} naar de printer gestuurd.`);
|
|
189
200
|
}
|
|
190
201
|
catch (err) {
|
|
191
|
-
toast.error(err
|
|
202
|
+
toast.error(printFoutMelding(err));
|
|
192
203
|
}
|
|
193
204
|
finally {
|
|
194
205
|
setPrintingOrderId(null);
|
|
195
206
|
}
|
|
196
207
|
};
|
|
197
|
-
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" })] }));
|
|
208
|
+
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 || !werkplek, 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" })] }));
|
|
198
209
|
}
|
|
199
210
|
function Stat({ icon, bg, value, label, }) {
|
|
200
211
|
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 })] })] }));
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Leest de ingestelde werkplek client-side (SSR-veilig). */
|
|
2
|
+
export declare function useWerkplek(): string | null;
|
|
3
|
+
/**
|
|
4
|
+
* Eén melding bovenaan een printscherm zolang er geen werkplek is ingesteld.
|
|
5
|
+
* Printknoppen zijn dan uitgeschakeld, zodat er geen fout per printactie komt.
|
|
6
|
+
*/
|
|
7
|
+
export declare function WerkplekWaarschuwing({ werkplek }: {
|
|
8
|
+
werkplek: string | null;
|
|
9
|
+
}): import("react").JSX.Element | null;
|
|
10
|
+
//# sourceMappingURL=WerkplekWaarschuwing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WerkplekWaarschuwing.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/WerkplekWaarschuwing.tsx"],"names":[],"mappings":"AAIA,6DAA6D;AAC7D,wBAAgB,WAAW,IAAI,MAAM,GAAG,IAAI,CAa3C;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,sCAW7E"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import { AlertTriangle } from "lucide-react";
|
|
4
|
+
import { getWerkplek } from "../../lib/printing";
|
|
5
|
+
/** Leest de ingestelde werkplek client-side (SSR-veilig). */
|
|
6
|
+
export function useWerkplek() {
|
|
7
|
+
const [werkplek, setWerkplekState] = useState(null);
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
setWerkplekState(getWerkplek());
|
|
10
|
+
const onStorage = () => setWerkplekState(getWerkplek());
|
|
11
|
+
window.addEventListener("storage", onStorage);
|
|
12
|
+
window.addEventListener("focus", onStorage);
|
|
13
|
+
return () => {
|
|
14
|
+
window.removeEventListener("storage", onStorage);
|
|
15
|
+
window.removeEventListener("focus", onStorage);
|
|
16
|
+
};
|
|
17
|
+
}, []);
|
|
18
|
+
return werkplek;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Eén melding bovenaan een printscherm zolang er geen werkplek is ingesteld.
|
|
22
|
+
* Printknoppen zijn dan uitgeschakeld, zodat er geen fout per printactie komt.
|
|
23
|
+
*/
|
|
24
|
+
export function WerkplekWaarschuwing({ werkplek }) {
|
|
25
|
+
if (werkplek)
|
|
26
|
+
return null;
|
|
27
|
+
return (_jsxs("div", { className: "flex items-start gap-2 rounded-lg border border-amber-500/40 bg-amber-500/10 px-3 py-2 text-sm", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0 text-amber-600" }), _jsxs("div", { children: [_jsx("span", { className: "font-medium", children: "Geen werkplek ingesteld." }), " Printen is uitgeschakeld. Stel de werkplek (pickstation- of computernaam) in bij ", _jsx("strong", { children: "Instellingen" }), "."] })] }));
|
|
28
|
+
}
|
|
@@ -14,82 +14,82 @@ export function renderPakbonHtml(order, lang = "nl") {
|
|
|
14
14
|
};
|
|
15
15
|
const rows = order.lines.length
|
|
16
16
|
? order.lines
|
|
17
|
-
.map((l) => `
|
|
18
|
-
<tr>
|
|
19
|
-
<td>${escapeHtml(l.description)}</td>
|
|
20
|
-
<td style="text-align:right">${l.crates ?? ""}</td>
|
|
21
|
-
<td>${escapeHtml(l.unit ?? "")}</td>
|
|
17
|
+
.map((l) => `
|
|
18
|
+
<tr>
|
|
19
|
+
<td>${escapeHtml(l.description)}</td>
|
|
20
|
+
<td style="text-align:right">${l.crates ?? ""}</td>
|
|
21
|
+
<td>${escapeHtml(l.unit ?? "")}</td>
|
|
22
22
|
</tr>`)
|
|
23
23
|
.join("")
|
|
24
24
|
: `<tr><td colspan="3" style="text-align:center;color:#666">${escapeHtml(t.noLines)}</td></tr>`;
|
|
25
25
|
const cartLabel = t.cartOf(order.cartIndex, order.cartTotal);
|
|
26
26
|
const fustenLabel = order.cratesOnCart != null ? `${order.cratesOnCart}` : "—";
|
|
27
27
|
const cartNumber = order.cartNumber ?? `${order.orderNumber}-${order.cartIndex}`;
|
|
28
|
-
return `<!doctype html>
|
|
29
|
-
<html lang="${escapeHtml(htmlLang)}">
|
|
30
|
-
<head>
|
|
31
|
-
<meta charset="utf-8">
|
|
32
|
-
<title>${escapeHtml(t.packingSlip)} ${escapeHtml(order.orderNumber)} · ${escapeHtml(cartNumber)}</title>
|
|
33
|
-
<style>
|
|
34
|
-
@page {
|
|
35
|
-
size: A4;
|
|
36
|
-
margin: 16mm;
|
|
37
|
-
@top-center { content: element(cartno); }
|
|
38
|
-
}
|
|
39
|
-
body { font-family: -apple-system, "Segoe UI", Helvetica, Arial, sans-serif; color:#111; }
|
|
40
|
-
h1 { margin: 0 0 4px; font-size: 26px; }
|
|
41
|
-
.sub { color:#444; margin-bottom: 8px; }
|
|
42
|
-
|
|
43
|
-
.cart-number-hero {
|
|
44
|
-
position: running(cartno);
|
|
45
|
-
font-size: 88px;
|
|
46
|
-
font-weight: 900;
|
|
47
|
-
letter-spacing: 4px;
|
|
48
|
-
line-height: 1;
|
|
49
|
-
color: #000;
|
|
50
|
-
background: #fff;
|
|
51
|
-
border: 4px solid #000;
|
|
52
|
-
padding: 12px 20px;
|
|
53
|
-
display: inline-block;
|
|
54
|
-
margin: 6px 0 20px;
|
|
55
|
-
font-family: "Arial Black", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
.grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px 24px; margin: 8px 0 22px; }
|
|
59
|
-
.grid div { font-size: 14px; }
|
|
60
|
-
.label { color:#666; font-size: 11px; text-transform: uppercase; letter-spacing: .04em; }
|
|
61
|
-
.value { font-size: 16px; font-weight: 600; }
|
|
62
|
-
table { width:100%; border-collapse: collapse; font-size: 14px; }
|
|
63
|
-
th, td { padding: 8px 6px; border-bottom: 1px solid #ddd; vertical-align: top; }
|
|
64
|
-
th { text-align: left; background:#f5f5f5; }
|
|
65
|
-
.barcode-fallback { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 18px; letter-spacing: 4px; padding: 8px 12px; border: 2px solid #111; display: inline-block; margin-top: 18px; }
|
|
66
|
-
.footer { margin-top: 28px; font-size: 11px; color:#666; }
|
|
67
|
-
</style>
|
|
68
|
-
</head>
|
|
69
|
-
<body>
|
|
70
|
-
<h1>${escapeHtml(t.packingSlip)}</h1>
|
|
71
|
-
<div class="sub">${escapeHtml(t.order)} ${escapeHtml(order.orderNumber)} · ${escapeHtml(cartLabel)}</div>
|
|
72
|
-
|
|
73
|
-
<div class="cart-number-hero">${escapeHtml(cartNumber)}</div>
|
|
74
|
-
|
|
75
|
-
<div class="grid">
|
|
76
|
-
<div><div class="label">${escapeHtml(t.customer)}</div><div class="value">${escapeHtml(order.customerName)}</div></div>
|
|
77
|
-
<div><div class="label">${escapeHtml(t.country)}</div><div class="value">${escapeHtml(order.country ?? "—")}</div></div>
|
|
78
|
-
<div><div class="label">${escapeHtml(t.deliveryDate)}</div><div class="value">${fmt(order.deliveryDate)}</div></div>
|
|
79
|
-
<div><div class="label">${escapeHtml(t.pickDate)}</div><div class="value">${fmt(order.pickDate)}</div></div>
|
|
80
|
-
<div><div class="label">${escapeHtml(t.cart)}</div><div class="value">${escapeHtml(cartLabel)}</div></div>
|
|
81
|
-
<div><div class="label">${escapeHtml(t.cratesOnThisCart)}</div><div class="value">${escapeHtml(fustenLabel)}</div></div>
|
|
82
|
-
</div>
|
|
83
|
-
|
|
84
|
-
<table>
|
|
85
|
-
<thead><tr><th>${escapeHtml(t.description)}</th><th style="text-align:right;width:90px">${escapeHtml(t.crates)}</th><th style="width:90px">${escapeHtml(t.unit)}</th></tr></thead>
|
|
86
|
-
<tbody>${rows}</tbody>
|
|
87
|
-
</table>
|
|
88
|
-
|
|
89
|
-
<div class="barcode-fallback">*${escapeHtml(order.orderNumber)}*</div>
|
|
90
|
-
|
|
91
|
-
<div class="footer">${escapeHtml(t.printedVia)} · ${new Date().toLocaleString(locale)}</div>
|
|
92
|
-
</body>
|
|
28
|
+
return `<!doctype html>
|
|
29
|
+
<html lang="${escapeHtml(htmlLang)}">
|
|
30
|
+
<head>
|
|
31
|
+
<meta charset="utf-8">
|
|
32
|
+
<title>${escapeHtml(t.packingSlip)} ${escapeHtml(order.orderNumber)} · ${escapeHtml(cartNumber)}</title>
|
|
33
|
+
<style>
|
|
34
|
+
@page {
|
|
35
|
+
size: A4;
|
|
36
|
+
margin: 16mm;
|
|
37
|
+
@top-center { content: element(cartno); }
|
|
38
|
+
}
|
|
39
|
+
body { font-family: -apple-system, "Segoe UI", Helvetica, Arial, sans-serif; color:#111; }
|
|
40
|
+
h1 { margin: 0 0 4px; font-size: 26px; }
|
|
41
|
+
.sub { color:#444; margin-bottom: 8px; }
|
|
42
|
+
|
|
43
|
+
.cart-number-hero {
|
|
44
|
+
position: running(cartno);
|
|
45
|
+
font-size: 88px;
|
|
46
|
+
font-weight: 900;
|
|
47
|
+
letter-spacing: 4px;
|
|
48
|
+
line-height: 1;
|
|
49
|
+
color: #000;
|
|
50
|
+
background: #fff;
|
|
51
|
+
border: 4px solid #000;
|
|
52
|
+
padding: 12px 20px;
|
|
53
|
+
display: inline-block;
|
|
54
|
+
margin: 6px 0 20px;
|
|
55
|
+
font-family: "Arial Black", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px 24px; margin: 8px 0 22px; }
|
|
59
|
+
.grid div { font-size: 14px; }
|
|
60
|
+
.label { color:#666; font-size: 11px; text-transform: uppercase; letter-spacing: .04em; }
|
|
61
|
+
.value { font-size: 16px; font-weight: 600; }
|
|
62
|
+
table { width:100%; border-collapse: collapse; font-size: 14px; }
|
|
63
|
+
th, td { padding: 8px 6px; border-bottom: 1px solid #ddd; vertical-align: top; }
|
|
64
|
+
th { text-align: left; background:#f5f5f5; }
|
|
65
|
+
.barcode-fallback { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 18px; letter-spacing: 4px; padding: 8px 12px; border: 2px solid #111; display: inline-block; margin-top: 18px; }
|
|
66
|
+
.footer { margin-top: 28px; font-size: 11px; color:#666; }
|
|
67
|
+
</style>
|
|
68
|
+
</head>
|
|
69
|
+
<body>
|
|
70
|
+
<h1>${escapeHtml(t.packingSlip)}</h1>
|
|
71
|
+
<div class="sub">${escapeHtml(t.order)} ${escapeHtml(order.orderNumber)} · ${escapeHtml(cartLabel)}</div>
|
|
72
|
+
|
|
73
|
+
<div class="cart-number-hero">${escapeHtml(cartNumber)}</div>
|
|
74
|
+
|
|
75
|
+
<div class="grid">
|
|
76
|
+
<div><div class="label">${escapeHtml(t.customer)}</div><div class="value">${escapeHtml(order.customerName)}</div></div>
|
|
77
|
+
<div><div class="label">${escapeHtml(t.country)}</div><div class="value">${escapeHtml(order.country ?? "—")}</div></div>
|
|
78
|
+
<div><div class="label">${escapeHtml(t.deliveryDate)}</div><div class="value">${fmt(order.deliveryDate)}</div></div>
|
|
79
|
+
<div><div class="label">${escapeHtml(t.pickDate)}</div><div class="value">${fmt(order.pickDate)}</div></div>
|
|
80
|
+
<div><div class="label">${escapeHtml(t.cart)}</div><div class="value">${escapeHtml(cartLabel)}</div></div>
|
|
81
|
+
<div><div class="label">${escapeHtml(t.cratesOnThisCart)}</div><div class="value">${escapeHtml(fustenLabel)}</div></div>
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
<table>
|
|
85
|
+
<thead><tr><th>${escapeHtml(t.description)}</th><th style="text-align:right;width:90px">${escapeHtml(t.crates)}</th><th style="width:90px">${escapeHtml(t.unit)}</th></tr></thead>
|
|
86
|
+
<tbody>${rows}</tbody>
|
|
87
|
+
</table>
|
|
88
|
+
|
|
89
|
+
<div class="barcode-fallback">*${escapeHtml(order.orderNumber)}*</div>
|
|
90
|
+
|
|
91
|
+
<div class="footer">${escapeHtml(t.printedVia)} · ${new Date().toLocaleString(locale)}</div>
|
|
92
|
+
</body>
|
|
93
93
|
</html>`;
|
|
94
94
|
}
|
|
95
95
|
function escapeHtml(s) {
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
1
|
+
import { type AfhandelingDocumenttype } from "../../lib/printing";
|
|
2
|
+
export interface PrintDocumentOpties {
|
|
3
|
+
documenttype: AfhandelingDocumenttype;
|
|
4
|
+
documentnaam: string;
|
|
5
|
+
isHerprint?: boolean;
|
|
6
|
+
papierformaat?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function printHtml(html: string, opties: PrintDocumentOpties): Promise<void>;
|
|
9
|
+
export declare function printPdfBase64(base64: string, opties: PrintDocumentOpties): Promise<void>;
|
|
3
10
|
//# sourceMappingURL=printDocuments.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"printDocuments.d.ts","sourceRoot":"","sources":["../../../src/components/pickstation/printDocuments.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"printDocuments.d.ts","sourceRoot":"","sources":["../../../src/components/pickstation/printDocuments.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,uBAAuB,EAC7B,MAAM,oBAAoB,CAAC;AAE5B,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE,uBAAuB,CAAC;IACtC,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAExF;AAED,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAEf"}
|
|
@@ -1,53 +1,9 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
f.style.border = "0";
|
|
10
|
-
document.body.appendChild(f);
|
|
11
|
-
return f;
|
|
12
|
-
}
|
|
13
|
-
function waitForFrame(f) {
|
|
14
|
-
return new Promise((resolve) => {
|
|
15
|
-
const handler = () => {
|
|
16
|
-
f.removeEventListener("load", handler);
|
|
17
|
-
resolve();
|
|
18
|
-
};
|
|
19
|
-
f.addEventListener("load", handler);
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
function callPrint(win) {
|
|
23
|
-
return new Promise((resolve) => {
|
|
24
|
-
if (!win)
|
|
25
|
-
return resolve();
|
|
26
|
-
try {
|
|
27
|
-
win.focus();
|
|
28
|
-
win.print();
|
|
29
|
-
}
|
|
30
|
-
catch {
|
|
31
|
-
// ignored
|
|
32
|
-
}
|
|
33
|
-
setTimeout(resolve, 800);
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
export async function printHtml(html) {
|
|
37
|
-
const f = makeFrame();
|
|
38
|
-
const load = waitForFrame(f);
|
|
39
|
-
f.srcdoc = html;
|
|
40
|
-
await load;
|
|
41
|
-
await callPrint(f.contentWindow);
|
|
42
|
-
setTimeout(() => f.remove(), 2000);
|
|
43
|
-
}
|
|
44
|
-
export async function printPdfBase64(base64, mime = "application/pdf") {
|
|
45
|
-
const f = makeFrame();
|
|
46
|
-
const load = waitForFrame(f);
|
|
47
|
-
f.src = `data:${mime};base64,${base64}`;
|
|
48
|
-
await load;
|
|
49
|
-
// Give the browser a moment to render the PDF.
|
|
50
|
-
await new Promise((r) => setTimeout(r, 600));
|
|
51
|
-
await callPrint(f.contentWindow);
|
|
52
|
-
setTimeout(() => f.remove(), 2000);
|
|
1
|
+
// Dunne wrappers rond de centrale printlaag. De verborgen iframes zijn
|
|
2
|
+
// verwijderd: alles gaat via de printwachtrij van de Layout maker.
|
|
3
|
+
import { printHtmlDocument, printPdfDocument, } from "../../lib/printing";
|
|
4
|
+
export async function printHtml(html, opties) {
|
|
5
|
+
await printHtmlDocument({ ...opties, html });
|
|
6
|
+
}
|
|
7
|
+
export async function printPdfBase64(base64, opties) {
|
|
8
|
+
await printPdfDocument({ ...opties, pdfBase64: base64 });
|
|
53
9
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AfhandelingSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/components/settings/AfhandelingSettingsCard.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AfhandelingSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/components/settings/AfhandelingSettingsCard.tsx"],"names":[],"mappings":"AAuBA,wBAAgB,uBAAuB,gCAoVtC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useState } from "react";
|
|
3
|
+
import { getWerkplek, setWerkplek } from "../../lib/printing";
|
|
3
4
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
4
5
|
import { useServerFn } from "@tanstack/react-start";
|
|
5
6
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle, Button, Input, Label, Switch, toast, } from "@flowselections/core";
|
|
@@ -44,6 +45,11 @@ export function AfhandelingSettingsCard() {
|
|
|
44
45
|
const [extraStickerDays, setExtraStickerDays] = useState(0);
|
|
45
46
|
const [autoProcessPaused, setAutoProcessPaused] = useState(false);
|
|
46
47
|
const [autoPrintDelivery, setAutoPrintDelivery] = useState(false);
|
|
48
|
+
const [werkplek, setWerkplekState] = useState("");
|
|
49
|
+
// Werkplek staat per apparaat in localStorage (SSR-veilig inlezen).
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
setWerkplekState(getWerkplek() ?? "");
|
|
52
|
+
}, []);
|
|
47
53
|
useEffect(() => {
|
|
48
54
|
if (data) {
|
|
49
55
|
setPickDays(data.pickDaysBeforeTransport);
|
|
@@ -87,5 +93,10 @@ export function AfhandelingSettingsCard() {
|
|
|
87
93
|
},
|
|
88
94
|
});
|
|
89
95
|
const clamp = (n) => Math.max(0, Math.min(30, Math.floor(n) || 0));
|
|
90
|
-
return (_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CalendarClock, { className: "h-5 w-5 text-primary" }), _jsx(CardTitle, { children: "Afhandeling \u2014 Planning" })] }), _jsx(CardDescription, { children: "Centrale werkdag-instellingen voor de automatische order\u00ADplanning. Alle berekeningen tellen alleen werkdagen (ma\u2013vr)." })] }), _jsxs(CardContent, { className: "space-y-5", children: [_jsxs("div", { className: "space-y-2
|
|
96
|
+
return (_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CalendarClock, { className: "h-5 w-5 text-primary" }), _jsx(CardTitle, { children: "Afhandeling \u2014 Planning" })] }), _jsx(CardDescription, { children: "Centrale werkdag-instellingen voor de automatische order\u00ADplanning. Alle berekeningen tellen alleen werkdagen (ma\u2013vr)." })] }), _jsxs(CardContent, { className: "space-y-5", children: [_jsxs("div", { className: "space-y-2 rounded-lg border p-3", children: [_jsx(Label, { htmlFor: "werkplek", children: "Werkplek (voor printen)" }), _jsx(Input, { id: "werkplek", placeholder: "bijv. pickstation-1 of controle-pc", value: werkplek, onChange: (e) => {
|
|
97
|
+
setWerkplekState(e.target.value);
|
|
98
|
+
setWerkplek(e.target.value);
|
|
99
|
+
} }), _jsx("p", { className: "text-xs text-muted-foreground", children: werkplek.trim()
|
|
100
|
+
? "Printopdrachten gaan naar de printer die aan deze werkplek is gekoppeld."
|
|
101
|
+
: "Zolang dit leeg is kan er niet geprint worden. Deze instelling geldt alleen op dit apparaat." })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "pickDays", children: "Aantal pickdagen (werkdagen v\u00F3\u00F3r ophaaldag)" }), _jsx(Input, { id: "pickDays", type: "number", min: 0, max: 30, value: pickDays, onChange: (e) => setPickDays(clamp(Number(e.target.value))) }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Wordt vanaf de berekende ophaaldag teruggeteld om de uiterste pickdatum te bepalen." })] }), _jsxs("div", { className: "rounded-md border p-4 space-y-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Sticker, { className: "h-4 w-4 text-primary" }), _jsx(Label, { htmlFor: "extraStickerDays", className: "font-semibold", children: "Extra pickdagen bij stickers" })] }), _jsx("p", { className: "text-xs text-muted-foreground max-w-prose", children: "Wanneer een order stickers bevat, mag het picken langer duren. Deze werkdagen worden bovenop het standaard aantal pickdagen opgeteld, zodat de uiterste pickdatum vroeger valt." }), _jsx(Input, { id: "extraStickerDays", type: "number", min: 0, max: 30, value: extraStickerDays, onChange: (e) => setExtraStickerDays(clamp(Number(e.target.value))) }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Voorbeeld: standaard 1 pickdag + 2 extra = 3 werkdagen picktijd voor orders met stickers." })] }), _jsxs("div", { className: "rounded-md border p-4 space-y-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CalendarDays, { className: "h-4 w-4 text-primary" }), _jsx(Label, { className: "font-semibold", children: "Orderpickdagen" })] }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Selecteer de dagen waarop het systeem orders mag inplannen om te picken. Andere dagen worden automatisch overgeslagen." }), _jsx(WeekdayPicker, { value: pickWeekdays, onChange: setPickWeekdays, idPrefix: "pick-wd" }), pickWeekdays.length === 0 ? (_jsx("p", { className: "text-xs text-destructive", children: "Kies minimaal \u00E9\u00E9n pickdag; anders kan het systeem geen orders inplannen." })) : null] }), _jsxs("div", { className: "rounded-md border p-4 space-y-4", children: [_jsxs("div", { className: "flex items-start justify-between gap-4", children: [_jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Scale, { className: "h-4 w-4 text-primary" }), _jsx(Label, { htmlFor: "pickDistribution", className: "font-semibold", children: "Pickverdeling" })] }), _jsx("p", { className: "text-xs text-muted-foreground max-w-prose", children: "Verdeelt de pickbelasting automatisch over werkdagen. Orders mogen eerder ingepland worden, maar nooit later dan de berekende uiterste pickdatum \u2014 leverbetrouwbaarheid blijft gegarandeerd." })] }), _jsx(Switch, { id: "pickDistribution", checked: distributionEnabled, onCheckedChange: setDistributionEnabled })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "maxEarlier", children: "Maximaal aantal werkdagen eerder picken" }), _jsx(Input, { id: "maxEarlier", type: "number", min: 0, max: 30, value: maxEarlier, disabled: !distributionEnabled, onChange: (e) => setMaxEarlier(clamp(Number(e.target.value))) }), _jsx("p", { className: "text-xs text-muted-foreground", children: "0 = geen vervroeging. Bijv. 2 = orders mogen maximaal 2 werkdagen voor hun uiterste pickdatum ingepland worden." })] })] }), _jsxs("div", { className: "rounded-md border p-4 space-y-4", children: [_jsxs("div", { className: "flex items-start justify-between gap-4", children: [_jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Mail, { className: "h-4 w-4 text-primary" }), _jsx(Label, { htmlFor: "mailEnabled", className: "font-semibold", children: "Aanmeldmail vervoerders" })] }), _jsx("p", { className: "text-xs text-muted-foreground max-w-prose", children: "Verstuurt automatisch een mail met de te verwachten karren naar de vervoerder v\u00F3\u00F3r de aanmelddeadline van het ophaalmoment. E\u00E9n mail per ophaaldag/-moment per vervoerder." })] }), _jsx(Switch, { id: "mailEnabled", checked: mailEnabled, onCheckedChange: setMailEnabled })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "mailLead", children: "Minuten v\u00F3\u00F3r aanmelddeadline versturen" }), _jsx(Input, { id: "mailLead", type: "number", min: 0, max: 1440, value: mailLead, disabled: !mailEnabled, onChange: (e) => setMailLead(Math.max(0, Math.min(1440, Math.floor(Number(e.target.value)) || 0))) })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "mailPurpose", children: "E-mail-doeleinde (purpose)" }), _jsx(Input, { id: "mailPurpose", value: mailPurpose, disabled: !mailEnabled, onChange: (e) => setMailPurpose(e.target.value) }), _jsxs("p", { className: "text-xs text-muted-foreground", children: ["Koppel dit doeleinde in de module ", _jsx("strong", { children: "E-mailkoppeling" }), " aan een actief send-adres. Standaard: ", _jsx("code", { children: "afhandeling_carrier_signup" }), "."] })] })] }), _jsxs("div", { className: "rounded-md border p-4 space-y-4", children: [_jsxs("div", { className: "flex items-start justify-between gap-4", children: [_jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Pause, { className: "h-4 w-4 text-primary" }), _jsx(Label, { htmlFor: "autoPause", className: "font-semibold", children: "Automatische Floriday-verwerking pauzeren" })] }), _jsxs("p", { className: "text-xs text-muted-foreground max-w-prose", children: ["Zet dit aan om ", _jsx("strong", { children: "tijdelijk" }), " te stoppen met automatisch Sales Orders acknowledgen, delivery orders aanmaken en fulfillment orders versturen. Bestaande orders in de wachtrij blijven staan."] })] }), _jsx(Switch, { id: "autoPause", checked: autoProcessPaused, onCheckedChange: setAutoProcessPaused })] }), _jsxs("div", { className: "flex items-start justify-between gap-4", children: [_jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Printer, { className: "h-4 w-4 text-primary" }), _jsx(Label, { htmlFor: "autoPrint", className: "font-semibold", children: "Afleverbrief automatisch printen" })] }), _jsx("p", { className: "text-xs text-muted-foreground max-w-prose", children: "Wanneer een afleverbrief beschikbaar komt, wordt hij direct naar de standaardprinter gestuurd. Handig voor onbemande balies." })] }), _jsx(Switch, { id: "autoPrint", checked: autoPrintDelivery, onCheckedChange: setAutoPrintDelivery })] }), _jsxs("p", { className: "text-xs text-muted-foreground", children: ["Timing van de automatische verwerking wordt bepaald door", " ", _jsx("strong", { children: "Maximaal aantal werkdagen eerder picken" }), ",", " ", _jsx("strong", { children: "Orderpickdagen" }), " en", " ", _jsx("strong", { children: "Extra pickdagen bij stickers" }), " hierboven."] })] }), _jsxs("div", { className: "rounded-md bg-muted/50 p-3 text-xs text-muted-foreground", children: [_jsx("strong", { children: "Ophaaltijd" }), " en ", _jsx("strong", { children: "Aanmeldtijd" }), " stel je per vervoerder in (werkdagen v\u00F3\u00F3r bezorgdag). De vervoerder die als ", _jsx("strong", { children: "standaard" }), " is gemarkeerd, wordt gebruikt voor orders zonder eigen vervoerder."] }), _jsx(Button, { onClick: () => mutation.mutate(), disabled: mutation.isPending, children: mutation.isPending ? "Opslaan…" : "Opslaan" })] })] }));
|
|
91
102
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useEffect, useMemo, useRef } from "react";
|
|
2
2
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
3
|
-
import { supabase } from "../integrations/supabase/
|
|
3
|
+
import { supabase } from "../integrations/supabase/moduleClient";
|
|
4
4
|
import { listEngineTasks } from "../lib/logisticsEngine/engine";
|
|
5
5
|
export const ENGINE_TASKS_KEY = ["engine", "tasks"];
|
|
6
6
|
const OPEN_STATUSES = ["planned", "ready", "in_progress"];
|
|
@@ -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;AAM/D,OAAO,EAGL,KAAK,aAAa,EAEnB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAmB,KAAK,UAAU,EAAE,MAAM,+BAA+B,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;AASjF,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,iFAAiF;IACjF,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,wEAAwE;IACxE,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,mEAAmE;IACnE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAMD,MAAM,WAAW,uBAAuB;IACtC,8EAA8E;IAC9E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED,wBAAgB,gBAAgB,CAAC,KAAK,CAAC,EAAE,uBAAuB,GAAG,sBAAsB,CA6PxF"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEffect, useMemo,
|
|
1
|
+
import { useEffect, useMemo, useState } from "react";
|
|
2
2
|
import { useQuery } from "@tanstack/react-query";
|
|
3
3
|
import { useServerFn } from "@tanstack/react-start";
|
|
4
4
|
import { getFloridayOrders } from "../lib/floriday.functions";
|
|
@@ -9,6 +9,7 @@ import { normalizePickupMoments, sanitizeWeekdays, selectCarrierForTarget } from
|
|
|
9
9
|
import { calculateOrderPlanning, DEFAULT_PICKUP_MOMENT, } from "../lib/planning";
|
|
10
10
|
import { useEngineTasks } from "./useEngineTasks";
|
|
11
11
|
import { emitEngineEvent } from "../lib/logisticsEngine/engine";
|
|
12
|
+
import { MAX_ATTEMPTS, emitQueueStatus, markInFlight, markResult, selectEmitBatch, } from "../lib/logisticsEngine/emitQueue";
|
|
12
13
|
function orderRefsFor(order) {
|
|
13
14
|
return [order.orderNumber, order.id].filter((v) => Boolean(v));
|
|
14
15
|
}
|
|
@@ -161,21 +162,36 @@ export function useOrderPlanning(range) {
|
|
|
161
162
|
});
|
|
162
163
|
}, [ordersQuery.data, settingsQuery.data, carriersQuery.data, composeByRef, pickByRef]);
|
|
163
164
|
// Orders zonder taak melden we bij de Engine; zelf indelen mag niet.
|
|
164
|
-
//
|
|
165
|
-
//
|
|
166
|
-
const announced = useRef(new Set());
|
|
167
|
-
const inFlight = useRef(new Set());
|
|
165
|
+
// De meldlus is begrensd: max MAX_PER_ROUND orders per ronde, pogingenteller
|
|
166
|
+
// met backoff per order, en na MAX_ATTEMPTS definitief opgeven met melding.
|
|
168
167
|
const [signalError, setSignalError] = useState(null);
|
|
169
168
|
useEffect(() => {
|
|
170
169
|
let cancelled = false;
|
|
170
|
+
const byRef = new Map();
|
|
171
|
+
for (const item of planned) {
|
|
172
|
+
if (!item.awaitingEngine || item.engineTask)
|
|
173
|
+
continue;
|
|
174
|
+
const ref = item.order.orderNumber || item.order.id;
|
|
175
|
+
if (ref && !byRef.has(ref))
|
|
176
|
+
byRef.set(ref, item);
|
|
177
|
+
}
|
|
178
|
+
const refs = [...byRef.keys()];
|
|
179
|
+
const batch = selectEmitBatch(refs);
|
|
180
|
+
if (!batch.length) {
|
|
181
|
+
const status = emitQueueStatus(refs);
|
|
182
|
+
if (status.gaveUp.length) {
|
|
183
|
+
setSignalError(`Melden bij de Logistics Engine is na ${MAX_ATTEMPTS} pogingen gestopt voor ${status.gaveUp.length} order(s): ${status.gaveUp.slice(0, 5).join(", ")}${status.gaveUp.length > 5 ? "…" : ""}. ${status.lastError ?? ""}`.trim());
|
|
184
|
+
}
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
171
187
|
void (async () => {
|
|
172
|
-
for (const
|
|
173
|
-
if (
|
|
174
|
-
|
|
175
|
-
const
|
|
176
|
-
if (!
|
|
188
|
+
for (const ref of batch) {
|
|
189
|
+
if (cancelled)
|
|
190
|
+
return;
|
|
191
|
+
const item = byRef.get(ref);
|
|
192
|
+
if (!item)
|
|
177
193
|
continue;
|
|
178
|
-
|
|
194
|
+
markInFlight(ref);
|
|
179
195
|
const result = await emitEngineEvent({
|
|
180
196
|
eventType: "order.received",
|
|
181
197
|
orderRef: ref,
|
|
@@ -189,15 +205,17 @@ export function useOrderPlanning(range) {
|
|
|
189
205
|
hasStickers: Boolean(item.order.hasStickers),
|
|
190
206
|
},
|
|
191
207
|
});
|
|
192
|
-
|
|
208
|
+
markResult(ref, result.ok, result.error);
|
|
193
209
|
if (cancelled)
|
|
194
210
|
return;
|
|
195
211
|
if (result.ok) {
|
|
196
|
-
announced.current.add(ref);
|
|
197
212
|
setSignalError(null);
|
|
198
213
|
}
|
|
199
214
|
else {
|
|
200
|
-
|
|
215
|
+
const status = emitQueueStatus(refs);
|
|
216
|
+
setSignalError(status.gaveUp.length
|
|
217
|
+
? `Melden bij de Logistics Engine is na ${MAX_ATTEMPTS} pogingen gestopt voor ${status.gaveUp.length} order(s): ${status.gaveUp.slice(0, 5).join(", ")}${status.gaveUp.length > 5 ? "…" : ""}. ${status.lastError ?? ""}`.trim()
|
|
218
|
+
: `Melding aan de Logistics Engine is mislukt (nieuwe poging volgt automatisch). ${result.error ?? ""}`.trim());
|
|
201
219
|
}
|
|
202
220
|
}
|
|
203
221
|
})();
|