@flowselections/floriday-afhandeling-module 1.0.56 → 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 (34) 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 +51 -20
  6. package/dist-lib/components/afhandeling/NuVersturenButton.d.ts.map +1 -1
  7. package/dist-lib/components/afhandeling/NuVersturenButton.js +18 -5
  8. package/dist-lib/components/afhandeling/WeekAgendaView.d.ts.map +1 -1
  9. package/dist-lib/components/afhandeling/WeekAgendaView.js +57 -10
  10. package/dist-lib/hooks/useConsistencyCheck.d.ts +1 -1
  11. package/dist-lib/hooks/useConsistencyCheck.d.ts.map +1 -1
  12. package/dist-lib/hooks/useConsistencyCheck.js +5 -3
  13. package/dist-lib/hooks/useOrderMeldingen.d.ts +12 -0
  14. package/dist-lib/hooks/useOrderMeldingen.d.ts.map +1 -0
  15. package/dist-lib/hooks/useOrderMeldingen.js +38 -0
  16. package/dist-lib/integrations/supabase/previewAuthStorage.d.ts.map +1 -1
  17. package/dist-lib/integrations/supabase/previewAuthStorage.js +22 -4
  18. package/dist-lib/integrations/supabase/types.d.ts +1067 -2
  19. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  20. package/dist-lib/integrations/supabase/types.js +1 -0
  21. package/dist-lib/lib/cartControl.functions.d.ts.map +1 -1
  22. package/dist-lib/lib/cartControl.functions.js +3 -1
  23. package/dist-lib/lib/floriday.functions.d.ts +2 -0
  24. package/dist-lib/lib/floriday.functions.d.ts.map +1 -1
  25. package/dist-lib/lib/floriday.functions.js +1 -0
  26. package/dist-lib/lib/floridayLoading.functions.d.ts +11 -0
  27. package/dist-lib/lib/floridayLoading.functions.d.ts.map +1 -1
  28. package/dist-lib/lib/floridayLoading.functions.js +17 -5
  29. package/dist-lib/lib/serverRoutes/autoSend.handler.d.ts +2 -0
  30. package/dist-lib/lib/serverRoutes/autoSend.handler.d.ts.map +1 -0
  31. package/dist-lib/lib/serverRoutes/autoSend.handler.js +102 -0
  32. package/dist-lib/moduleDefinition.d.ts.map +1 -1
  33. package/dist-lib/moduleDefinition.js +5 -2
  34. package/package.json +3 -3
@@ -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":"AAyBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAmGjE,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"}
@@ -3,15 +3,16 @@ import { createContext, useContext, useState } from "react";
3
3
  import { useQuery, useQueryClient } from "@tanstack/react-query";
4
4
  import { useServerFn } from "@tanstack/react-start";
5
5
  import { Badge, Checkbox, Button, Dialog, DialogTitle, DialogDescription, Input, Tabs, TabsContent, TabsList, TabsTrigger, toast, } from "@flowselections/core";
6
- import { CalendarClock, CheckCircle2, ChevronDown, ChevronRight, Printer, Users } from "lucide-react";
6
+ import { AlertTriangle, CalendarClock, CheckCircle2, ChevronDown, ChevronRight, Printer, Users } from "lucide-react";
7
+ import { useStickerMeldingen, useOrderArchived } from "../../hooks/useOrderMeldingen";
7
8
  import { ResponsiveDialogBody, ResponsiveDialogContent, ResponsiveDialogFooter, ResponsiveDialogHeader, } from "../ui/responsive-dialog.js";
8
9
  import { usePickAgenda } from "../../hooks/usePickAgenda";
9
10
  import { useOrderBuffers, useBufferMembers } from "../../hooks/useOrderBuffers";
10
11
  import { isSameDay } from "../../lib/pickDate";
11
12
  import { getOrderLines, getDayCartContents, getOrderCartIds, getPakbonCartInfo, } from "../../lib/cartAssignments.functions";
12
13
  import { printLayoutDocument, printPdfDocument, printFoutMelding } from "../../lib/printing";
13
- import { AutoProcessStatus } from "./AutoProcessStatus";
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";
@@ -35,14 +36,47 @@ function voortgang(status) {
35
36
  return { label: "Nog te picken", done: false, variant: "outline" };
36
37
  }
37
38
  const ShipStateCtx = createContext(new Map());
38
- /** Voortgang met voorrang voor de echte verzendstatus: eerst Floriday, dan de scan. */
39
- function useVoortgang(ref, status) {
39
+ /** Eén status per order: annulering/archief > Floriday-poging of afmelding > Engine. */
40
+ function useVoortgang(ref, status, opts = { floriday: null }) {
40
41
  const st = useContext(ShipStateCtx).get(refKey(ref));
41
- if (st?.floridaySent)
42
- return { label: "Verzonden naar Floriday", done: true, variant: "default" };
43
- if (st?.approvedAll)
44
- return { label: "Klaar voor verzending", done: false, variant: "secondary" };
45
- return voortgang(status);
42
+ if (opts.cancelled)
43
+ return { label: "Geannuleerd", done: true, variant: "outline", bron: "Verkoop" };
44
+ if (opts.archived === true)
45
+ return { label: "Gearchiveerd", done: true, variant: "outline", bron: "Engine" };
46
+ const archiefNote = opts.archived === null ? " · archiefstatus onbekend" : "";
47
+ if (opts.floriday !== false && st?.floridaySent)
48
+ return { label: "Verzonden naar Floriday", done: true, variant: "default", bron: "Floriday" + archiefNote };
49
+ if (st?.approvedAll) {
50
+ if (opts.floriday === false)
51
+ return { label: "Afgemeld", done: true, variant: "default", bron: "Engine-scan" + archiefNote };
52
+ if (opts.floriday === true)
53
+ return { label: "Klaar voor verzending", done: false, variant: "secondary", bron: "Engine-scan" + archiefNote };
54
+ return { label: "Alle karren gescand", done: false, variant: "secondary", bron: "Engine-scan" + archiefNote };
55
+ }
56
+ const v = voortgang(status);
57
+ if (v.done && opts.floriday === false)
58
+ return { ...v, label: "Afgemeld", bron: "Engine" + archiefNote };
59
+ if (v.done && opts.floriday === true && v.label === "Afgehandeld")
60
+ return { ...v, label: "Afgemeld (nog niet naar Floriday)", done: false, variant: "secondary", bron: "Engine" + archiefNote };
61
+ return { ...v, bron: "Engine" + archiefNote };
62
+ }
63
+ function isCancelled(status) {
64
+ const s = (status ?? "").trim().toLowerCase();
65
+ return s === "geannuleerd" || s === "cancelled" || s === "canceled";
66
+ }
67
+ function StatusBadge({ vg }) {
68
+ return (_jsxs("span", { className: "inline-flex flex-col items-start", children: [_jsx(Badge, { variant: vg.variant, children: vg.label }), _jsxs("span", { className: "text-[10px] text-muted-foreground", children: ["bron: ", vg.bron] })] }));
69
+ }
70
+ function StickerMeldingen({ orderId }) {
71
+ const q = useStickerMeldingen(orderId);
72
+ if (q.isLoading)
73
+ return null;
74
+ if (q.data === null) {
75
+ return _jsx("div", { className: "text-xs text-destructive", children: "Stickercontrole onbekend" });
76
+ }
77
+ if (!q.data?.length)
78
+ return null;
79
+ return (_jsx("ul", { className: "space-y-1", children: q.data.map((m, i) => (_jsxs("li", { className: "flex items-start gap-1 text-xs text-destructive", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-3 w-3 shrink-0" }), _jsx("span", { children: m.melding })] }, (m.stickerId ?? "") + i))) }));
46
80
  }
47
81
  const NL_DATE = new Intl.DateTimeFormat("nl-NL", { dateStyle: "full" });
48
82
  function refKey(value) {
@@ -556,7 +590,7 @@ function AfhandelenButton({ orderRef, done }) {
556
590
  }
557
591
  function EngineOrderRow({ order }) {
558
592
  const vg = useVoortgang(order.ref, order.status);
559
- return (_jsx("div", { className: "rounded-lg border bg-card px-4 py-3", children: _jsxs("div", { className: "grid grid-cols-2 gap-3 text-sm md:grid-cols-4", children: [_jsxs("div", { children: [_jsx("div", { className: "font-medium", children: order.ref }), _jsx("div", { className: "text-xs text-muted-foreground", children: "Alleen bekend bij de Engine" })] }), _jsxs("div", { children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Karren vandaag" }), _jsx("div", { children: order.carts })] }), _jsxs("div", { children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Planten" }), _jsx("div", { children: order.plants })] }), _jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsx(Badge, { variant: vg.variant, children: vg.label }), _jsx(AfhandelenButton, { orderRef: order.ref, done: vg.done })] })] }) }));
593
+ return (_jsx("div", { className: "rounded-lg border bg-card px-4 py-3", children: _jsxs("div", { className: "grid grid-cols-2 gap-3 text-sm md:grid-cols-4", children: [_jsxs("div", { children: [_jsx("div", { className: "font-medium", children: order.ref }), _jsx("div", { className: "text-xs text-muted-foreground", children: "Alleen bekend bij de Engine" })] }), _jsxs("div", { children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Karren vandaag" }), _jsx("div", { children: order.carts })] }), _jsxs("div", { children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Planten" }), _jsx("div", { children: order.plants })] }), _jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsx(StatusBadge, { vg: vg }), _jsx(AfhandelenButton, { orderRef: order.ref, done: vg.done })] })] }) }));
560
594
  }
561
595
  function OrderRow({ planned: p, cartsToday, engineStatus, }) {
562
596
  const [open, setOpen] = useState(false);
@@ -567,8 +601,13 @@ function OrderRow({ planned: p, cartsToday, engineStatus, }) {
567
601
  const fetchCarts = useServerFn(getOrderCartIds);
568
602
  const fetchDeliveryNote = useServerFn(getDeliveryNotePdf);
569
603
  const fetchShippingNoteLayout = useServerFn(getShippingNoteLayout);
604
+ const archivedQ = useOrderArchived(p.order.id);
570
605
  const fetchPakbonInfo = useServerFn(getPakbonCartInfo);
571
- const vg = useVoortgang(p.order.orderNumber, engineStatus ?? p.order.status);
606
+ const vg = useVoortgang(p.order.orderNumber, engineStatus ?? p.order.status, {
607
+ floriday: !!p.order.salesOrderId,
608
+ cancelled: isCancelled(p.order.status),
609
+ archived: archivedQ.isLoading ? false : (archivedQ.data ?? null),
610
+ });
572
611
  const linesQuery = useQuery({
573
612
  queryKey: ["order-lines", source, p.order.id],
574
613
  queryFn: () => fetchLines({ data: { orderId: p.order.id, orderSource: source } }),
@@ -594,15 +633,7 @@ function OrderRow({ planned: p, cartsToday, engineStatus, }) {
594
633
  };
595
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
596
635
  ? new Date(p.order.deliveryDate).toLocaleDateString("nl-NL")
597
- : "—" })] })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs(Badge, { variant: "secondary", children: [cartsToday, " karren vandaag"] }), _jsx(Badge, { variant: vg.variant, children: vg.label }), _jsx(Badge, { variant: "outline", children: p.order.status }), (() => {
598
- const src = p.order.source === "floriday" ? "floriday" : "manual";
599
- const soId = p.order.salesOrderId ?? (src === "floriday" ? p.order.id : null);
600
- return soId ? (_jsx(AutoProcessStatus, { salesOrderId: soId, compact: true, allowManualTrigger: false })) : null;
601
- })()] })] })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2 border-t pt-3", children: [(() => {
602
- const src = p.order.source === "floriday" ? "floriday" : "manual";
603
- const soId = p.order.salesOrderId ?? (src === "floriday" ? p.order.id : null);
604
- return soId ? (_jsx(AutoProcessStatus, { salesOrderId: soId, orderRef: p.order.orderNumber })) : null;
605
- })(), 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) => {
606
637
  e.stopPropagation();
607
638
  setVerzetOpen(true);
608
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: {
@@ -1 +1 @@
1
- {"version":3,"file":"NuVersturenButton.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/NuVersturenButton.tsx"],"names":[],"mappings":"AAoBA,wBAAgB,iBAAiB,CAAC,EAAE,QAAQ,EAAE,KAAsB,EAAE,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,+BAkH3G"}
1
+ {"version":3,"file":"NuVersturenButton.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/NuVersturenButton.tsx"],"names":[],"mappings":"AAqBA,wBAAgB,iBAAiB,CAAC,EAAE,QAAQ,EAAE,KAAsB,EAAE,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,+BAkI3G"}
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useState } from "react";
3
3
  import { useServerFn } from "@tanstack/react-start";
4
+ import { useQueryClient } from "@tanstack/react-query";
4
5
  import { Button, toast, AlertDialog, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@flowselections/core";
5
6
  import { Send, Loader2, CheckCircle2, XCircle } from "lucide-react";
6
7
  import { prepareFloridayLoading, sendFloridayLoading, } from "../../lib/floridayLoading.functions";
@@ -17,6 +18,8 @@ export function NuVersturenButton({ orderRef, label = "Nu versturen" }) {
17
18
  const [loading, setLoading] = useState(false);
18
19
  const [sending, setSending] = useState(false);
19
20
  const [preview, setPreview] = useState(null);
21
+ const [sent, setSent] = useState(false);
22
+ const queryClient = useQueryClient();
20
23
  const load = async () => setPreview(await prepare({ data: { orderRef } }));
21
24
  const meldFout = (e, standaard) => {
22
25
  const t = e instanceof Error ? e.message : standaard;
@@ -42,11 +45,21 @@ export function NuVersturenButton({ orderRef, label = "Nu versturen" }) {
42
45
  setSending(true);
43
46
  try {
44
47
  const r = await send({ data: { orderRef } });
45
- for (const x of r.results) {
48
+ const results = Array.isArray(r?.results) ? r.results : [];
49
+ let allOk = results.length > 0;
50
+ for (const x of results) {
46
51
  if (x.code === "created" || x.code === "already_fulfilled")
47
52
  toast.success(x.message);
48
- else
53
+ else {
54
+ allOk = false;
49
55
  meldFout(new Error(x.message), "Versturen mislukt");
56
+ }
57
+ }
58
+ if (allOk) {
59
+ setSent(true);
60
+ setPreview(null);
61
+ void queryClient.invalidateQueries();
62
+ return;
50
63
  }
51
64
  await load();
52
65
  }
@@ -57,11 +70,11 @@ export function NuVersturenButton({ orderRef, label = "Nu versturen" }) {
57
70
  setSending(false);
58
71
  }
59
72
  };
60
- 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)" : ""] }), _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
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
61
74
  ? _jsx(CheckCircle2, { className: "h-3.5 w-3.5 text-primary" })
62
- : _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) => {
63
76
  // Begrijpelijke zin, technische code klein eronder.
64
77
  const m = /^(.*?)\s*\[([^\]]+)\]$/s.exec(b);
65
78
  return (_jsxs("li", { children: [m ? m[1] : b, m ? _jsx("div", { className: "text-xs text-muted-foreground", children: m[2] }) : null] }, b));
66
- }) }))] })), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { disabled: sending, children: "Annuleren" }), _jsxs(Button, { onClick: doSend, disabled: !preview?.canSend || sending, children: [sending ? _jsx(Loader2, { className: "h-3 w-3 mr-1 animate-spin" }) : null, "Versturen"] })] })] }) })] }));
79
+ }) }))] })), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { disabled: sending, children: "Annuleren" }), _jsxs(Button, { onClick: doSend, disabled: !preview?.canSend || sending || sent, children: [sending ? _jsx(Loader2, { className: "h-3 w-3 mr-1 animate-spin" }) : null, sent ? "Verzonden naar Floriday" : "Versturen"] })] })] }) })] }));
67
80
  }
@@ -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)
@@ -0,0 +1,12 @@
1
+ export interface StickerMelding {
2
+ melding: string;
3
+ soort: string | null;
4
+ stickerId: string | null;
5
+ plek: string | null;
6
+ sinds: string | null;
7
+ }
8
+ /** Actuele stickermeldingen van de Engine; null = leesfout (stickercontrole onbekend). */
9
+ export declare function useStickerMeldingen(orderId: string | null | undefined): import("@tanstack/react-query").UseQueryResult<StickerMelding[] | null, Error>;
10
+ /** Archiefstatus uit de Engine; null = onbekend (leesfout). */
11
+ export declare function useOrderArchived(orderId: string | null | undefined): import("@tanstack/react-query").UseQueryResult<boolean | null, Error>;
12
+ //# sourceMappingURL=useOrderMeldingen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useOrderMeldingen.d.ts","sourceRoot":"","sources":["../../src/hooks/useOrderMeldingen.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAID,0FAA0F;AAC1F,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,kFAmBrE;AAED,+DAA+D;AAC/D,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,yEAUlE"}
@@ -0,0 +1,38 @@
1
+ import { useQuery } from "@tanstack/react-query";
2
+ import { supabase } from "../integrations/supabase/moduleClient";
3
+ const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
4
+ /** Actuele stickermeldingen van de Engine; null = leesfout (stickercontrole onbekend). */
5
+ export function useStickerMeldingen(orderId) {
6
+ return useQuery({
7
+ queryKey: ["engine", "sticker-meldingen", orderId],
8
+ enabled: !!orderId && UUID.test(orderId),
9
+ refetchInterval: 60000,
10
+ queryFn: async () => {
11
+ const { data, error } = await supabase.rpc("logistics_order_sticker_meldingen", { p_order_id: orderId });
12
+ if (error || !Array.isArray(data))
13
+ return null;
14
+ return data
15
+ .filter((m) => typeof m?.melding === "string" && m.melding)
16
+ .map((m) => ({
17
+ melding: String(m.melding),
18
+ soort: m.soort ? String(m.soort) : null,
19
+ stickerId: m.stickerId ? String(m.stickerId) : null,
20
+ plek: m.plek ? String(m.plek) : null,
21
+ sinds: m.sinds ? String(m.sinds) : null,
22
+ }));
23
+ },
24
+ });
25
+ }
26
+ /** Archiefstatus uit de Engine; null = onbekend (leesfout). */
27
+ export function useOrderArchived(orderId) {
28
+ return useQuery({
29
+ queryKey: ["engine", "order-archived", orderId],
30
+ enabled: !!orderId && UUID.test(orderId),
31
+ queryFn: async () => {
32
+ const { data, error } = await supabase.rpc("logistics_order_is_archived", { p_order_id: orderId });
33
+ if (error || !data || typeof data !== "object")
34
+ return null;
35
+ return data.gearchiveerd === true;
36
+ },
37
+ });
38
+ }
@@ -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
  }