@flowselections/floriday-afhandeling-module 1.0.58 → 1.0.61
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/AutoSendStatus.d.ts +5 -0
- package/dist-lib/components/afhandeling/AutoSendStatus.d.ts.map +1 -0
- package/dist-lib/components/afhandeling/AutoSendStatus.js +22 -0
- package/dist-lib/components/afhandeling/DagDetailDialog.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/DagDetailDialog.js +2 -1
- package/dist-lib/components/afhandeling/NuVersturenButton.js +1 -1
- package/dist-lib/components/afhandeling/WeekAgendaView.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/WeekAgendaView.js +57 -10
- package/dist-lib/hooks/useConsistencyCheck.d.ts +1 -1
- package/dist-lib/hooks/useConsistencyCheck.d.ts.map +1 -1
- package/dist-lib/hooks/useConsistencyCheck.js +5 -3
- package/dist-lib/integrations/supabase/previewAuthStorage.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/previewAuthStorage.js +22 -4
- package/dist-lib/integrations/supabase/types.d.ts +384 -2
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.js +1 -0
- package/dist-lib/lib/floriday.functions.d.ts +2 -0
- package/dist-lib/lib/floriday.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday.functions.js +1 -0
- package/dist-lib/lib/floridayLoading.functions.d.ts +11 -0
- package/dist-lib/lib/floridayLoading.functions.d.ts.map +1 -1
- package/dist-lib/lib/floridayLoading.functions.js +17 -5
- package/dist-lib/lib/serverRoutes/autoSend.handler.d.ts +2 -0
- package/dist-lib/lib/serverRoutes/autoSend.handler.d.ts.map +1 -0
- package/dist-lib/lib/serverRoutes/autoSend.handler.js +102 -0
- package/dist-lib/moduleDefinition.d.ts.map +1 -1
- package/dist-lib/moduleDefinition.js +5 -2
- package/package.json +2 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AutoSendStatus.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/AutoSendStatus.tsx"],"names":[],"mappings":"AAGA,yFAAyF;AACzF,wBAAgB,cAAc,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,sCAe9D"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useQuery } from "@tanstack/react-query";
|
|
3
|
+
import { supabase } from "../../integrations/supabase/moduleClient";
|
|
4
|
+
/** Toont de tekst uit logistics_auto_send_status letterlijk; niets bij status "geen". */
|
|
5
|
+
export function AutoSendStatus({ orderId }) {
|
|
6
|
+
const { data } = useQuery({
|
|
7
|
+
queryKey: ["auto-send-status", orderId],
|
|
8
|
+
queryFn: async () => {
|
|
9
|
+
const { data, error } = await supabase.rpc("logistics_auto_send_status", { p_order_id: orderId });
|
|
10
|
+
if (error)
|
|
11
|
+
throw new Error(error.message);
|
|
12
|
+
const row = Array.isArray(data) ? data[0] : data;
|
|
13
|
+
return (row && typeof row === "object" ? row : null);
|
|
14
|
+
},
|
|
15
|
+
refetchInterval: 30000,
|
|
16
|
+
retry: 1,
|
|
17
|
+
});
|
|
18
|
+
if (!data?.tekst || !data.status || data.status === "geen")
|
|
19
|
+
return null;
|
|
20
|
+
const tone = data.status === "mislukt" ? "text-destructive" : "text-muted-foreground";
|
|
21
|
+
return _jsx("span", { className: `text-xs ${tone}`, children: data.tekst });
|
|
22
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DagDetailDialog.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/DagDetailDialog.tsx"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"DagDetailDialog.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/DagDetailDialog.tsx"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AA4JjE,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CACvC;AAQD,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,sCAG1D"}
|
|
@@ -12,6 +12,7 @@ import { isSameDay } from "../../lib/pickDate";
|
|
|
12
12
|
import { getOrderLines, getDayCartContents, getOrderCartIds, getPakbonCartInfo, } from "../../lib/cartAssignments.functions";
|
|
13
13
|
import { printLayoutDocument, printPdfDocument, printFoutMelding } from "../../lib/printing";
|
|
14
14
|
import { NuVersturenButton } from "./NuVersturenButton";
|
|
15
|
+
import { AutoSendStatus } from "./AutoSendStatus";
|
|
15
16
|
import { CapacityWarning } from "./CapacityWarning";
|
|
16
17
|
import { useCapacityCheck } from "../../hooks/useCapacityCheck";
|
|
17
18
|
import { toDayKey } from "../../lib/logisticsEngine/capacity";
|
|
@@ -632,7 +633,7 @@ function OrderRow({ planned: p, cartsToday, engineStatus, }) {
|
|
|
632
633
|
};
|
|
633
634
|
return (_jsxs("div", { className: "rounded-lg border bg-card", children: [_jsxs("div", { className: "flex flex-col gap-3 p-3", children: [_jsxs("button", { type: "button", onClick: () => setOpen((o) => !o), className: "-m-1 flex items-start gap-3 rounded-md p-1 text-left hover:bg-muted/50", children: [open ? (_jsx(ChevronDown, { className: "mt-1 h-4 w-4 shrink-0" })) : (_jsx(ChevronRight, { className: "mt-1 h-4 w-4 shrink-0" })), _jsxs("div", { className: "min-w-0 flex-1 space-y-2", children: [_jsxs("div", { className: "flex flex-wrap items-baseline gap-x-6 gap-y-1 text-sm", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "font-medium", children: p.order.orderNumber }), _jsx("div", { className: "truncate text-xs text-muted-foreground", children: p.order.customerName })] }), _jsxs("div", { className: "text-xs text-muted-foreground", children: ["Type ", _jsx("span", { className: "text-foreground", children: p.order.deliveryType })] }), _jsxs("div", { className: "text-xs text-muted-foreground", children: ["Leverdatum", " ", _jsx("span", { className: "text-foreground", children: p.order.deliveryDate
|
|
634
635
|
? new Date(p.order.deliveryDate).toLocaleDateString("nl-NL")
|
|
635
|
-
: "—" })] })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs(Badge, { variant: "secondary", children: [cartsToday, " karren vandaag"] }), _jsx(StatusBadge, { vg: vg })] }), _jsx(StickerMeldingen, { orderId: p.order.id })] })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2 border-t pt-3", children: [p.order.orderNumber && p.order.salesOrderId ? _jsx(NuVersturenButton, { orderRef: p.order.orderNumber }) : null, _jsx(AfhandelenButton, { orderRef: p.order.orderNumber, done: vg.done }), _jsxs(Button, { size: "sm", variant: "outline", onClick: handlePrintAll, disabled: printing, title: "Alle documenten van deze bestelling afdrukken (pakbonnen per kar en de verzendbrief)", children: [_jsx(Printer, { className: "mr-1 h-3.5 w-3.5" }), printing ? "In de Printwachtrij…" : "Alle documenten afdrukken"] }), _jsxs(Button, { size: "sm", variant: "outline", onClick: (e) => {
|
|
636
|
+
: "—" })] })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs(Badge, { variant: "secondary", children: [cartsToday, " karren vandaag"] }), _jsx(StatusBadge, { vg: vg })] }), _jsx(StickerMeldingen, { orderId: p.order.id })] })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2 border-t pt-3", children: [p.order.orderNumber && p.order.salesOrderId ? _jsx(NuVersturenButton, { orderRef: p.order.orderNumber }) : null, p.order.salesOrderId ? _jsx(AutoSendStatus, { orderId: p.order.id }) : null, _jsx(AfhandelenButton, { orderRef: p.order.orderNumber, done: vg.done }), _jsxs(Button, { size: "sm", variant: "outline", onClick: handlePrintAll, disabled: printing, title: "Alle documenten van deze bestelling afdrukken (pakbonnen per kar en de verzendbrief)", children: [_jsx(Printer, { className: "mr-1 h-3.5 w-3.5" }), printing ? "In de Printwachtrij…" : "Alle documenten afdrukken"] }), _jsxs(Button, { size: "sm", variant: "outline", onClick: (e) => {
|
|
636
637
|
e.stopPropagation();
|
|
637
638
|
setVerzetOpen(true);
|
|
638
639
|
}, title: "Ophaaldag of ophaaltijd van deze order wijzigen", children: [_jsx(CalendarClock, { className: "mr-1 h-3.5 w-3.5" }), "Verzetten"] })] })] }), _jsx(VerzetOrderDialog, { open: verzetOpen, orderRef: p.order.orderNumber, carrierTarget: {
|
|
@@ -72,7 +72,7 @@ export function NuVersturenButton({ orderRef, label = "Nu versturen" }) {
|
|
|
72
72
|
};
|
|
73
73
|
return (_jsxs(_Fragment, { children: [_jsxs(Button, { size: "sm", variant: "outline", onClick: open, disabled: loading, children: [loading ? _jsx(Loader2, { className: "h-3 w-3 mr-1 animate-spin" }) : _jsx(Send, { className: "h-3 w-3 mr-1" }), label] }), _jsx(AlertDialog, { open: !!preview, onOpenChange: (o) => !o && !sending && setPreview(null), children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Belading naar Floriday versturen?" }), _jsx(AlertDialogDescription, { children: "Een geaccepteerde opdracht start de verzendbrief, vooraankondiging, fust-administratie en afrekening, en is op productie niet terug te draaien." })] }), preview && (_jsxs("div", { className: "space-y-2 text-sm", children: [_jsxs("div", { children: [_jsx("span", { className: "text-muted-foreground", children: "Orders:" }), " ", preview.orderRefs.join(", ")] }), _jsxs("div", { children: [_jsx("span", { className: "text-muted-foreground", children: "Leveringen:" }), " ", preview.shipments.length] }), _jsxs("div", { children: [_jsx("span", { className: "text-muted-foreground", children: "Karren:" }), " ", preview.cartCount, preview.carriers.some((c) => c.shared) ? " (waarvan gedeeld)" : ""] }), Array.isArray(preview.carriers) && preview.carriers.length > 0 && (_jsxs("div", { children: [_jsx("span", { className: "text-muted-foreground", children: "Karkenmerk naar Floriday:" }), " ", _jsx("span", { className: "font-mono", children: preview.carriers.map((c) => c.loadCarrierReference || "—").join(", ") })] })), _jsxs("div", { children: [_jsx("span", { className: "text-muted-foreground", children: "Regels:" }), " ", preview.lineCount] }), _jsx("ul", { className: "space-y-1", children: preview.orders.map((o) => (_jsxs("li", { className: "flex items-center gap-2", children: [o.controlled
|
|
74
74
|
? _jsx(CheckCircle2, { className: "h-3.5 w-3.5 text-primary" })
|
|
75
|
-
: _jsx(XCircle, { className: "h-3.5 w-3.5 text-destructive" }), _jsxs("span", { children: [o.orderRef, ": ", o.cartsControlled, " van ", o.cartsTotal, " karren goedgekeurd op de Scan-controle", o.controlled ? " — klaar voor verzending" : "", o.pickStatus ? ` · pickstatus: ${PICK_LABEL[o.pickStatus] ?? o.pickStatus}` : "", o.autoStatus ? ` · auto-verwerking: ${o.autoStatus}` : ""] })] }, o.orderRef))) }), preview.shipments.filter((sh) => sh.floridayStatus || preview.lastAttempts.some((a) => a.fulfillmentOrderId === sh.fulfillmentOrderId && (a.code === "created" || a.code === "already_fulfilled"))).map((sh) => (_jsxs("div", { className: "rounded border border-border p-2", children: ["Verzonden naar Floriday", sh.floridayStatus ? ` (Floriday: ${sh.floridayStatus})` : "", " \u00B7", " ", sh.deliveryNoteReady === true ? "verzendbrief klaar" : "verzendbrief volgt via de Engine"] }, sh.fulfillmentOrderId))), preview.lastAttempts.length > 0 && (_jsxs("div", { className: "rounded border border-border p-2", children: [_jsx("div", { className: "text-muted-foreground mb-1", children: "Laatste pogingen" }), preview.lastAttempts.slice(0, 4).map((a) => (_jsxs("div", { children: [new Date(a.createdAt).toLocaleString("nl-NL"), " \u00B7 versturen \u00B7 ", a.code === "created" || a.code === "already_fulfilled" ? "geaccepteerd" : (a.message ?? a.code)?.replace(/\s*\[[^\]]+\]$/, "")] }, a.createdAt + a.step)))] })), preview.blockers.length > 0 && (_jsx("ul", { className: "list-disc pl-5 text-destructive", children: preview.blockers.map((b) => {
|
|
75
|
+
: _jsx(XCircle, { className: "h-3.5 w-3.5 text-destructive" }), _jsxs("span", { children: [o.orderRef, ": ", o.cartsControlled, " van ", o.cartsTotal, " karren goedgekeurd op de Scan-controle", o.controlled ? " — klaar voor verzending" : "", !o.controlled && o.pickStatus && o.pickStatus !== "geen_pickrij" ? ` · pickstatus: ${PICK_LABEL[o.pickStatus] ?? o.pickStatus}` : "", o.autoStatus ? ` · auto-verwerking: ${o.autoStatus}` : ""] })] }, o.orderRef))) }), preview.shipments.filter((sh) => sh.floridayStatus || preview.lastAttempts.some((a) => a.fulfillmentOrderId === sh.fulfillmentOrderId && (a.code === "created" || a.code === "already_fulfilled"))).map((sh) => (_jsxs("div", { className: "rounded border border-border p-2", children: ["Verzonden naar Floriday", sh.floridayStatus ? ` (Floriday: ${sh.floridayStatus})` : "", " \u00B7", " ", sh.deliveryNoteReady === true ? "verzendbrief klaar" : "verzendbrief volgt via de Engine"] }, sh.fulfillmentOrderId))), preview.lastAttempts.length > 0 && (_jsxs("div", { className: "rounded border border-border p-2", children: [_jsx("div", { className: "text-muted-foreground mb-1", children: "Laatste pogingen" }), preview.lastAttempts.slice(0, 4).map((a) => (_jsxs("div", { children: [new Date(a.createdAt).toLocaleString("nl-NL"), " \u00B7 versturen \u00B7 ", a.code === "created" || a.code === "already_fulfilled" ? "geaccepteerd" : (a.message ?? a.code)?.replace(/\s*\[[^\]]+\]$/, "")] }, a.createdAt + a.step)))] })), preview.blockers.length > 0 && (_jsx("ul", { className: "list-disc pl-5 text-destructive", children: preview.blockers.map((b) => {
|
|
76
76
|
// Begrijpelijke zin, technische code klein eronder.
|
|
77
77
|
const m = /^(.*?)\s*\[([^\]]+)\]$/s.exec(b);
|
|
78
78
|
return (_jsxs("li", { children: [m ? m[1] : b, m ? _jsx("div", { className: "text-xs text-muted-foreground", children: m[2] }) : null] }, b));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WeekAgendaView.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/WeekAgendaView.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"WeekAgendaView.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/WeekAgendaView.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAElE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAOjE,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,yEAAyE;IACzE,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,KAAK,IAAI,CAAC;IACxD,4EAA4E;IAC5E,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAiGD,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,IAAI,EACJ,SAAS,EACT,YAAY,EACZ,UAAU,EACV,KAAK,EACL,YAAY,EACZ,UAAU,GACX,EAAE,mBAAmB,+BAoMrB"}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { useQuery } from "@tanstack/react-query";
|
|
4
|
+
import { supabase } from "../../integrations/supabase/moduleClient";
|
|
5
|
+
import { isExcludedOrderStatus, normalizeOrderStatus } from "../../lib/orderStatus";
|
|
3
6
|
import { Button, Badge } from "@flowselections/core";
|
|
4
7
|
import { ChevronLeft, ChevronRight, Package, Truck, Sprout, Clock, CheckCircle2, Scale } from "lucide-react";
|
|
5
8
|
import { isSameDay, startOfWeek, addDays } from "../../lib/pickDate";
|
|
@@ -8,6 +11,20 @@ import { usePickAgenda } from "../../hooks/usePickAgenda";
|
|
|
8
11
|
import { useConsistencyCheck } from "../../hooks/useConsistencyCheck";
|
|
9
12
|
import { CapacityWarning } from "./CapacityWarning";
|
|
10
13
|
import { toDayKey } from "../../lib/logisticsEngine/capacity";
|
|
14
|
+
const SHIPPED = new Set(["verzonden", "gereed voor facturatie", "gefactureerd", "geleverd", "afgeleverd", "afgerond", "afgehandeld", "afgemeld", "shipped", "delivered", "completed", "invoiced"]);
|
|
15
|
+
/** Eén indeling per order. Floriday herkend op Floriday-id; afmeldstand uit de order. */
|
|
16
|
+
function classify(o, scanned) {
|
|
17
|
+
if (/annul|cancel/i.test(o.status ?? "") || isExcludedOrderStatus(o.status))
|
|
18
|
+
return "uit";
|
|
19
|
+
if (SHIPPED.has(normalizeOrderStatus(o.status))) {
|
|
20
|
+
if (o.salesOrderId && o.floridayFulfilled !== true)
|
|
21
|
+
return "niet_afgemeld";
|
|
22
|
+
return "afgehandeld";
|
|
23
|
+
}
|
|
24
|
+
if (scanned || isOrderReady(o))
|
|
25
|
+
return "verzendklaar";
|
|
26
|
+
return "voorbereiding";
|
|
27
|
+
}
|
|
11
28
|
const WEEKDAY_LABELS = ["MAANDAG", "DINSDAG", "WOENSDAG", "DONDERDAG", "VRIJDAG", "ZATERDAG", "ZONDAG"];
|
|
12
29
|
const MONTHS_NL_SHORT = ["jan", "feb", "mrt", "apr", "mei", "jun", "jul", "aug", "sep", "okt", "nov", "dec"];
|
|
13
30
|
function isOrderReady(o) {
|
|
@@ -87,6 +104,30 @@ export function WeekAgendaView({ planned, mode, isLoading, onOrderClick, onDayCl
|
|
|
87
104
|
// Pickagenda komt volledig uit de Engine-RPC; deze module rekent zelf geen
|
|
88
105
|
// pickdagen meer uit en doet geen datum-, werkdagen- of tijdzonelogica.
|
|
89
106
|
const pickAgenda = usePickAgenda(mode === "pick" ? toDayKey(weekStart) : null, mode === "pick" ? toDayKey(weekEnd) : null);
|
|
107
|
+
// Orders waarvan alle karren goedgekeurd gescand zijn (op order-id).
|
|
108
|
+
const orderIds = useMemo(() => (mode === "pick" ? [] : planned.map((p) => p.order.id).sort()), [planned, mode]);
|
|
109
|
+
const scanQuery = useQuery({
|
|
110
|
+
queryKey: ["afhandeling-scanned-orders", orderIds],
|
|
111
|
+
enabled: orderIds.length > 0,
|
|
112
|
+
staleTime: 30000,
|
|
113
|
+
refetchInterval: 60000,
|
|
114
|
+
queryFn: async () => {
|
|
115
|
+
const all = new Map();
|
|
116
|
+
for (let i = 0; i < orderIds.length; i += 100) {
|
|
117
|
+
const { data, error } = await supabase
|
|
118
|
+
.from("logistics_control_scan").select("order_id, approved_at").in("order_id", orderIds.slice(i, i + 100));
|
|
119
|
+
if (error)
|
|
120
|
+
throw new Error(error.message);
|
|
121
|
+
for (const r of Array.isArray(data) ? data : []) {
|
|
122
|
+
if (!r.order_id)
|
|
123
|
+
continue;
|
|
124
|
+
all.set(r.order_id, (all.get(r.order_id) ?? true) && !!r.approved_at);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return [...all].filter(([, ok]) => ok).map(([id]) => id);
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
const scannedIds = useMemo(() => new Set(scanQuery.data ?? []), [scanQuery.data]);
|
|
90
131
|
const days = useMemo(() => {
|
|
91
132
|
return Array.from({ length: 7 }, (_, i) => {
|
|
92
133
|
const date = addDays(weekStart, i);
|
|
@@ -104,6 +145,7 @@ export function WeekAgendaView({ planned, mode, isLoading, onOrderClick, onDayCl
|
|
|
104
145
|
voorbereiding: row?.inPreparation ?? 0,
|
|
105
146
|
// Uitsluitend uit de Engine (taak afgemeld); geannuleerde orders zijn al weggelaten.
|
|
106
147
|
afgehandeld: row?.doneCount ?? 0,
|
|
148
|
+
nietAfgemeld: 0,
|
|
107
149
|
};
|
|
108
150
|
}
|
|
109
151
|
const entries = [];
|
|
@@ -113,7 +155,7 @@ export function WeekAgendaView({ planned, mode, isLoading, onOrderClick, onDayCl
|
|
|
113
155
|
const d = dateForPlanned(p, mode);
|
|
114
156
|
if (!d || !isSameDay(d, date))
|
|
115
157
|
continue;
|
|
116
|
-
if (
|
|
158
|
+
if (classify(p.order, false) === "uit")
|
|
117
159
|
continue;
|
|
118
160
|
entries.push({
|
|
119
161
|
order: p.order,
|
|
@@ -126,8 +168,10 @@ export function WeekAgendaView({ planned, mode, isLoading, onOrderClick, onDayCl
|
|
|
126
168
|
}
|
|
127
169
|
const karren = entries.reduce((s, e) => s + e.cartCount, 0);
|
|
128
170
|
const planten = entries.reduce((s, e) => s + e.plantCount, 0);
|
|
129
|
-
const
|
|
130
|
-
const
|
|
171
|
+
const cls = entries.map((e) => classify(e.order, scannedIds.has(e.order.id)));
|
|
172
|
+
const cnt = (b) => cls.filter((c) => c === b).length;
|
|
173
|
+
const verzendklaar = cnt("verzendklaar");
|
|
174
|
+
const voorbereiding = cnt("voorbereiding");
|
|
131
175
|
return {
|
|
132
176
|
date,
|
|
133
177
|
entries,
|
|
@@ -137,16 +181,19 @@ export function WeekAgendaView({ planned, mode, isLoading, onOrderClick, onDayCl
|
|
|
137
181
|
planten,
|
|
138
182
|
verzendklaar,
|
|
139
183
|
voorbereiding,
|
|
140
|
-
afgehandeld:
|
|
184
|
+
afgehandeld: cnt("afgehandeld"),
|
|
185
|
+
nietAfgemeld: cnt("niet_afgemeld"),
|
|
141
186
|
};
|
|
142
187
|
});
|
|
143
|
-
}, [planned, weekStart, mode, pickAgenda.byDate]);
|
|
188
|
+
}, [planned, weekStart, mode, pickAgenda.byDate, scannedIds]);
|
|
144
189
|
const totals = useMemo(() => {
|
|
145
190
|
const leveringen = days.reduce((s, d) => s + d.bestellingen, 0);
|
|
146
191
|
const verzendklaar = days.reduce((s, d) => s + d.verzendklaar, 0);
|
|
147
192
|
const voorbereiding = days.reduce((s, d) => s + d.voorbereiding, 0);
|
|
148
193
|
const totaalStuks = days.reduce((s, d) => s + d.planten, 0);
|
|
149
|
-
|
|
194
|
+
const afgehandeld = days.reduce((s, d) => s + d.afgehandeld, 0);
|
|
195
|
+
const nietAfgemeld = days.reduce((s, d) => s + d.nietAfgemeld, 0);
|
|
196
|
+
return { leveringen, verzendklaar, voorbereiding, totaalStuks, afgehandeld, nietAfgemeld };
|
|
150
197
|
}, [days]);
|
|
151
198
|
const { misaligned } = useConsistencyCheck();
|
|
152
199
|
const capacity = useCapacityCheck(toDayKey(weekStart), toDayKey(weekEnd));
|
|
@@ -158,10 +205,10 @@ export function WeekAgendaView({ planned, mode, isLoading, onOrderClick, onDayCl
|
|
|
158
205
|
d.setHours(0, 0, 0, 0);
|
|
159
206
|
setCursor(startOfWeek(d));
|
|
160
207
|
};
|
|
161
|
-
return (_jsxs("div", { className: "flex h-full flex-col gap-4 overflow-hidden", 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" })] })] })] }), misaligned && (_jsx("div", { className: "rounded-md border bg-muted/40 px-3 py-2 text-xs text-muted-foreground", children: "Aantallen
|
|
208
|
+
return (_jsxs("div", { className: "flex h-full flex-col gap-4 overflow-hidden", 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" })] })] })] }), misaligned && (_jsx("div", { className: "rounded-md border bg-muted/40 px-3 py-2 text-xs text-muted-foreground", children: "Aantallen bij de Engine wijken af; de Engine lijnt ze uit." })), _jsxs("div", { className: "grid shrink-0 grid-cols-1 gap-3 sm:grid-cols-3 lg:grid-cols-6", 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: "Afgehandeld", value: totals.afgehandeld, tone: "success" }), _jsx(SummaryCard, { label: "Verzonden, nog niet afgemeld bij Floriday", value: totals.nietAfgemeld, tone: "warning" }), _jsx(SummaryCard, { label: "Totaal stuks", value: totals.totaalStuks, tone: "default" })] }), _jsx("div", { className: "min-h-0 flex-1 space-y-2 overflow-y-auto rounded-lg border bg-muted/20 p-2", children: isLoading || (mode === "pick" && pickAgenda.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, capacity: mode === "delivery" ? undefined : capacity.byDate.get(toDayKey(day.date)) }, day.date.toISOString())))) })] }));
|
|
162
209
|
}
|
|
163
210
|
function SummaryCard({ label, value, tone, }) {
|
|
164
|
-
const valueClass = tone === "success" ? "text-green-600" : "text-foreground";
|
|
211
|
+
const valueClass = tone === "success" ? "text-green-600" : tone === "warning" ? "text-amber-600" : "text-foreground";
|
|
165
212
|
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 })] }));
|
|
166
213
|
}
|
|
167
214
|
function DayRow({ day, onOrderClick, onDayClick, capacity, }) {
|
|
@@ -170,8 +217,8 @@ function DayRow({ day, onOrderClick, onDayClick, capacity, }) {
|
|
|
170
217
|
today.setHours(0, 0, 0, 0);
|
|
171
218
|
const isToday = isSameDay(day.date, today);
|
|
172
219
|
// Alles van deze dag is afgehandeld: toon een groene bevestiging.
|
|
173
|
-
const dagAfgesloten = day.bestellingen > 0 && day.afgehandeld >= day.bestellingen;
|
|
174
|
-
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: [dagAfgesloten && (_jsxs("div", { className: "flex items-center gap-2 rounded-md border border-green-600/30 bg-green-500/15 px-3 py-1.5 text-sm font-medium text-green-700", children: [_jsx(CheckCircle2, { className: "h-4 w-4" }), "Dag is afgesloten"] })), day.shiftedCount > 0 && (_jsxs(Badge, { className: "bg-purple-500/10 text-purple-700 hover:bg-purple-500/10", variant: "secondary", title: "Aantal orders dat te laat is aangemeld en door de Engine is verschoven.", children: [_jsx(Scale, { className: "mr-1 h-3.5 w-3.5" }), day.shiftedCount, " te laat aangemeld"] })), 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"] })), 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" })] }));
|
|
220
|
+
const dagAfgesloten = day.bestellingen > 0 && day.afgehandeld >= day.bestellingen && day.nietAfgemeld === 0;
|
|
221
|
+
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: [dagAfgesloten && (_jsxs("div", { className: "flex items-center gap-2 rounded-md border border-green-600/30 bg-green-500/15 px-3 py-1.5 text-sm font-medium text-green-700", children: [_jsx(CheckCircle2, { className: "h-4 w-4" }), "Dag is afgesloten"] })), day.shiftedCount > 0 && (_jsxs(Badge, { className: "bg-purple-500/10 text-purple-700 hover:bg-purple-500/10", variant: "secondary", title: "Aantal orders dat te laat is aangemeld en door de Engine is verschoven.", children: [_jsx(Scale, { className: "mr-1 h-3.5 w-3.5" }), day.shiftedCount, " te laat aangemeld"] })), day.nietAfgemeld > 0 && (_jsxs(Badge, { className: "bg-amber-500/15 text-amber-700 hover:bg-amber-500/15", variant: "secondary", children: [day.nietAfgemeld, " nog niet afgemeld bij Floriday"] })), 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"] })), 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" })] }));
|
|
175
222
|
}
|
|
176
223
|
function Stat({ icon, bg, value, label, }) {
|
|
177
224
|
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,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Controle van de Engine, elke 2 minuten opnieuw opgevraagd. Deze module corrigeert niets
|
|
3
3
|
* zelf: bij een afwijking tonen we alleen een discrete melding.
|
|
4
4
|
*/
|
|
5
5
|
export declare function useConsistencyCheck(): {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useConsistencyCheck.d.ts","sourceRoot":"","sources":["../../src/hooks/useConsistencyCheck.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAgB,mBAAmB;;
|
|
1
|
+
{"version":3,"file":"useConsistencyCheck.d.ts","sourceRoot":"","sources":["../../src/hooks/useConsistencyCheck.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAgB,mBAAmB;;EAiBlC"}
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import { useQuery } from "@tanstack/react-query";
|
|
2
2
|
import { supabase } from "../integrations/supabase/moduleClient";
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Controle van de Engine, elke 2 minuten opnieuw opgevraagd. Deze module corrigeert niets
|
|
5
5
|
* zelf: bij een afwijking tonen we alleen een discrete melding.
|
|
6
6
|
*/
|
|
7
7
|
export function useConsistencyCheck() {
|
|
8
8
|
const query = useQuery({
|
|
9
9
|
queryKey: ["logistics-consistency-check"],
|
|
10
|
-
staleTime:
|
|
10
|
+
staleTime: 60000,
|
|
11
|
+
refetchInterval: 120000,
|
|
12
|
+
refetchIntervalInBackground: false,
|
|
11
13
|
retry: false,
|
|
12
|
-
refetchOnWindowFocus:
|
|
14
|
+
refetchOnWindowFocus: true,
|
|
13
15
|
queryFn: async () => {
|
|
14
16
|
const { data, error } = await supabase.rpc("logistics_consistency_check");
|
|
15
17
|
if (error)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"previewAuthStorage.d.ts","sourceRoot":"","sources":["../../../src/integrations/supabase/previewAuthStorage.ts"],"names":[],"mappings":"AAIA,wBAAgB,sBAAsB;
|
|
1
|
+
{"version":3,"file":"previewAuthStorage.d.ts","sourceRoot":"","sources":["../../../src/integrations/supabase/previewAuthStorage.ts"],"names":[],"mappings":"AAIA,wBAAgB,sBAAsB;mBA6Db,MAAM;mBAwBZ,MAAM,SAAS,MAAM;sBASlB,MAAM;cAM3B"}
|
|
@@ -61,21 +61,38 @@ export function brokeredPreviewStorage() {
|
|
|
61
61
|
// The editor may not be listening yet at the first getItem, so retry once.
|
|
62
62
|
let firstGet = true;
|
|
63
63
|
const RETRY_DELAY = 250;
|
|
64
|
+
// Blocked third-party storage throws here; seeding must not break broker reads.
|
|
65
|
+
const readLocal = (key) => { try {
|
|
66
|
+
return localStorage.getItem(key);
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return undefined;
|
|
70
|
+
} };
|
|
64
71
|
return {
|
|
65
72
|
getItem: async (key) => {
|
|
66
|
-
|
|
73
|
+
// Sent with GET and REMOVE so an editor that never cached this session can check or revoke it.
|
|
74
|
+
const local = readLocal(key);
|
|
75
|
+
let res = await request('lovable-preview-auth:get', key, local ?? undefined);
|
|
67
76
|
if (!res && firstGet) {
|
|
68
77
|
await new Promise((r) => setTimeout(r, RETRY_DELAY));
|
|
69
|
-
res = await request('lovable-preview-auth:get', key);
|
|
78
|
+
res = await request('lovable-preview-auth:get', key, local ?? undefined);
|
|
70
79
|
}
|
|
71
80
|
firstGet = false;
|
|
72
81
|
// '' is the logout tombstone: clear the local copy too so it can't resurrect if
|
|
73
82
|
// the broker later goes silent. A null reply means never-synced -> keep local.
|
|
74
83
|
if (res && res.ok && typeof res.value === 'string') {
|
|
75
84
|
if (res.value === '') {
|
|
76
|
-
|
|
85
|
+
if (local !== undefined && readLocal(key) === local)
|
|
86
|
+
localStorage.removeItem(key);
|
|
77
87
|
return null;
|
|
78
88
|
}
|
|
89
|
+
// Seed this origin so a new tab, whose editor holds no session, stays signed in.
|
|
90
|
+
if (local === null && readLocal(key) === null) {
|
|
91
|
+
try {
|
|
92
|
+
localStorage.setItem(key, res.value);
|
|
93
|
+
}
|
|
94
|
+
catch { /* storage full: still return the editor's session */ }
|
|
95
|
+
}
|
|
79
96
|
return res.value;
|
|
80
97
|
}
|
|
81
98
|
return localStorage.getItem(key);
|
|
@@ -92,8 +109,9 @@ export function brokeredPreviewStorage() {
|
|
|
92
109
|
});
|
|
93
110
|
},
|
|
94
111
|
removeItem: (key) => {
|
|
112
|
+
const local = readLocal(key);
|
|
95
113
|
localStorage.removeItem(key);
|
|
96
|
-
return request('lovable-preview-auth:remove', key).then(() => undefined);
|
|
114
|
+
return request('lovable-preview-auth:remove', key, local ?? undefined).then(() => undefined);
|
|
97
115
|
},
|
|
98
116
|
};
|
|
99
117
|
}
|