@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.
Files changed (28) hide show
  1. package/dist-lib/components/afhandeling/AutoSendStatus.d.ts +5 -0
  2. package/dist-lib/components/afhandeling/AutoSendStatus.d.ts.map +1 -0
  3. package/dist-lib/components/afhandeling/AutoSendStatus.js +22 -0
  4. package/dist-lib/components/afhandeling/DagDetailDialog.d.ts.map +1 -1
  5. package/dist-lib/components/afhandeling/DagDetailDialog.js +2 -1
  6. package/dist-lib/components/afhandeling/NuVersturenButton.js +1 -1
  7. package/dist-lib/components/afhandeling/WeekAgendaView.d.ts.map +1 -1
  8. package/dist-lib/components/afhandeling/WeekAgendaView.js +57 -10
  9. package/dist-lib/hooks/useConsistencyCheck.d.ts +1 -1
  10. package/dist-lib/hooks/useConsistencyCheck.d.ts.map +1 -1
  11. package/dist-lib/hooks/useConsistencyCheck.js +5 -3
  12. package/dist-lib/integrations/supabase/previewAuthStorage.d.ts.map +1 -1
  13. package/dist-lib/integrations/supabase/previewAuthStorage.js +22 -4
  14. package/dist-lib/integrations/supabase/types.d.ts +384 -2
  15. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  16. package/dist-lib/integrations/supabase/types.js +1 -0
  17. package/dist-lib/lib/floriday.functions.d.ts +2 -0
  18. package/dist-lib/lib/floriday.functions.d.ts.map +1 -1
  19. package/dist-lib/lib/floriday.functions.js +1 -0
  20. package/dist-lib/lib/floridayLoading.functions.d.ts +11 -0
  21. package/dist-lib/lib/floridayLoading.functions.d.ts.map +1 -1
  22. package/dist-lib/lib/floridayLoading.functions.js +17 -5
  23. package/dist-lib/lib/serverRoutes/autoSend.handler.d.ts +2 -0
  24. package/dist-lib/lib/serverRoutes/autoSend.handler.d.ts.map +1 -0
  25. package/dist-lib/lib/serverRoutes/autoSend.handler.js +102 -0
  26. package/dist-lib/moduleDefinition.d.ts.map +1 -1
  27. package/dist-lib/moduleDefinition.js +5 -2
  28. package/package.json +2 -2
@@ -0,0 +1,5 @@
1
+ /** Toont de tekst uit logistics_auto_send_status letterlijk; niets bij status "geen". */
2
+ export declare function AutoSendStatus({ orderId }: {
3
+ orderId: string;
4
+ }): import("react").JSX.Element | null;
5
+ //# sourceMappingURL=AutoSendStatus.d.ts.map
@@ -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;AA2JjE,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"}
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":"AAGA,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;AAgFD,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,IAAI,EACJ,SAAS,EACT,YAAY,EACZ,UAAU,EACV,KAAK,EACL,YAAY,EACZ,UAAU,GACX,EAAE,mBAAmB,+BAqKrB"}
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 (/annul|cancel/i.test(p.order.status ?? ""))
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 verzendklaar = entries.filter((e) => isOrderReady(e.order)).length;
130
- const voorbereiding = entries.length - verzendklaar;
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: entries.filter((e) => isDoneStatus(e.order.status)).length,
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
- return { leveringen, verzendklaar, voorbereiding, totaalStuks };
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 worden opnieuw uitgelijnd." })), _jsxs("div", { className: "grid shrink-0 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: "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())))) })] }));
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
- * Eenmalige controle bij het laden van de agenda. Deze module corrigeert niets
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;;EAelC"}
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
- * Eenmalige controle bij het laden van de agenda. Deze module corrigeert niets
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: Infinity,
10
+ staleTime: 60000,
11
+ refetchInterval: 120000,
12
+ refetchIntervalInBackground: false,
11
13
  retry: false,
12
- refetchOnWindowFocus: false,
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;mBA2Db,MAAM;mBAeZ,MAAM,SAAS,MAAM;sBASlB,MAAM;cAK3B"}
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
- let res = await request('lovable-preview-auth:get', key);
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
- localStorage.removeItem(key);
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
  }