@flowselections/pick-schermen-kwekers 1.0.18 → 1.0.20

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 (32) hide show
  1. package/dist-lib/components/pick/PickScherm.d.ts.map +1 -1
  2. package/dist-lib/components/pick/PickScherm.js +340 -59
  3. package/dist-lib/components/pick/QueueTable.d.ts +6 -1
  4. package/dist-lib/components/pick/QueueTable.d.ts.map +1 -1
  5. package/dist-lib/components/pick/QueueTable.js +30 -22
  6. package/dist-lib/hooks/usePickQueue.d.ts +2 -0
  7. package/dist-lib/hooks/usePickQueue.d.ts.map +1 -1
  8. package/dist-lib/hooks/usePickQueue.js +30 -0
  9. package/dist-lib/hooks/useShippingNoteStatus.d.ts +27 -0
  10. package/dist-lib/hooks/useShippingNoteStatus.d.ts.map +1 -0
  11. package/dist-lib/hooks/useShippingNoteStatus.js +132 -0
  12. package/dist-lib/index.d.ts +1 -0
  13. package/dist-lib/index.d.ts.map +1 -1
  14. package/dist-lib/index.js +1 -0
  15. package/dist-lib/integrations/supabase/auth-middleware.d.ts +952 -19
  16. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  17. package/dist-lib/integrations/supabase/client.d.ts +952 -19
  18. package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
  19. package/dist-lib/integrations/supabase/client.server.d.ts +952 -19
  20. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  21. package/dist-lib/integrations/supabase/types.d.ts +952 -17
  22. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  23. package/dist-lib/lib/pickApi.d.ts +29 -0
  24. package/dist-lib/lib/pickApi.d.ts.map +1 -1
  25. package/dist-lib/lib/pickApi.js +49 -0
  26. package/dist-lib/lib/pickTypes.d.ts +59 -2
  27. package/dist-lib/lib/pickTypes.d.ts.map +1 -1
  28. package/dist-lib/lib/pickTypes.js +149 -24
  29. package/dist-lib/lib/shippingNoteStatus.d.ts +35 -0
  30. package/dist-lib/lib/shippingNoteStatus.d.ts.map +1 -0
  31. package/dist-lib/lib/shippingNoteStatus.js +118 -0
  32. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { Badge, Button, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@flowselections/core";
3
- import { AlertTriangle, Check, Eye, Lock, Printer, RotateCcw } from "lucide-react";
4
- import { bufferOrdersOf, dedupeByUnit, displayCartColumn, displayCartCount, displayPageCount, sinceLabel, unitTitleOf } from "../../lib/pickTypes";
3
+ import { AlertTriangle, Check, Eye, Loader2, Lock, Printer, RefreshCw, RotateCcw } from "lucide-react";
4
+ import { bufferOrdersOf, cartColumnInfo, comparePickQueue, dedupeByUnit, displayCartCount, displayPageCount, sinceLabel, unitTitleOf } from "../../lib/pickTypes";
5
5
  import { getPickTranslator } from "../../lib/pickI18n";
6
6
  /**
7
7
  * Uit hoeveel pakbonnen een order bestaat, is pas bekend nadat er geprint is.
@@ -25,23 +25,22 @@ function priorityTone(priority, urgent) {
25
25
  return "bg-secondary text-secondary-foreground";
26
26
  return "bg-muted text-muted-foreground";
27
27
  }
28
- export function QueueTable({ items, lockedIds, statuses, documentTypes, busyId, onPick, onReprint, onPrintAll, onSelectPrinted, onMarkPrinted, onPreview, readOnly, t: translator, }) {
28
+ export function QueueTable({ items, lockedIds, statuses, documentTypes, busyId, readinessById, onPick, onReprint, onPrintAll, onSelectPrinted, onMarkPrinted, onPreview, readOnly, t: translator, }) {
29
29
  const t = translator ?? getPickTranslator("nl");
30
- // Nog te printen documenten altijd bovenaan; onderlinge volgorde blijft gelijk.
31
- // Eén regel per eenheid: een bufferorder verschijnt nooit als losse orders.
30
+ // Volgorde identiek aan de keuze van "volgende uitprinten" (zie
31
+ // comparePickQueue): niet-geprinte regels bovenaan, en bij gelijke stand op
32
+ // kar-nummer, zodat K1 boven K2 boven K3 staat en het printen van boven naar
33
+ // beneden loopt. Eén regel per eenheid: een bufferorder verschijnt nooit als
34
+ // losse orders.
32
35
  const safeItems = dedupeByUnit(Array.isArray(items) ? items : [])
33
- .map((item, index) => ({ item, index }))
34
- .sort((a, b) => {
35
- const aPrinted = a.item.printed_at ? 1 : 0;
36
- const bPrinted = b.item.printed_at ? 1 : 0;
37
- if (aPrinted !== bPrinted)
38
- return aPrinted - bPrinted;
39
- return a.index - b.index;
40
- })
41
- .map((entry) => entry.item);
36
+ .slice()
37
+ .sort(comparePickQueue);
42
38
  const docById = new Map((Array.isArray(documentTypes) ? documentTypes : []).map((d) => [d.id, d]));
43
39
  return (_jsxs(_Fragment, { children: [_jsx("div", { className: "hidden overflow-x-auto rounded-lg border md:block", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: t("document") }), _jsx(TableHead, { children: t("order") }), _jsx(TableHead, { children: t("customer") }), _jsx(TableHead, { className: "text-right", children: t("lines") }), _jsx(TableHead, { className: "text-right", children: "Karren" }), _jsx(TableHead, { className: "text-right", children: "Pakbonnen" }), _jsx(TableHead, { className: "text-right", children: t("page") }), _jsx(TableHead, { children: t("status") }), _jsx(TableHead, { children: t("priority") }), _jsx(TableHead, { children: t("announced") }), _jsx(TableHead, { children: t("waiting") }), _jsx(TableHead, { className: "text-right", children: t("action") })] }) }), _jsxs(TableBody, { children: [safeItems.map((item) => {
44
40
  const printed = !!item.printed_at;
41
+ const readiness = readinessById[item.id];
42
+ const checking = !readiness || readiness.checking;
43
+ const ready = readiness?.ready === true;
45
44
  const locked = !printed && lockedIds.has(item.id);
46
45
  const doc = item.document_type_id ? docById.get(item.document_type_id) : undefined;
47
46
  const warnings = Array.isArray(item.warnings) ? item.warnings : [];
@@ -54,7 +53,10 @@ export function QueueTable({ items, lockedIds, statuses, documentTypes, busyId,
54
53
  event.preventDefault();
55
54
  onSelectPrinted?.(item);
56
55
  }
57
- }, children: [_jsxs(TableCell, { className: "font-medium", children: [_jsxs("div", { className: "flex items-center gap-2", children: [unitTitleOf(item), warnings.length > 0 && (_jsx(AlertTriangle, { className: "h-4 w-4 text-destructive", "aria-label": t("warning") }))] }), _jsx("div", { className: "text-xs text-muted-foreground", children: doc?.name ?? "-" })] }), _jsx(TableCell, { children: bufferOrdersOf(item).length > 0 ? "" : item.order_number }), _jsx(TableCell, { children: item.customer_name }), _jsx(TableCell, { className: "text-right", children: item.line_count }), _jsx(TableCell, { className: "text-right", children: displayCartColumn(item) }), _jsx(TableCell, { className: "text-right", children: printedSlipCount(item) ?? "—" }), _jsx(TableCell, { className: "text-right", children: displayPageCount(item.line_count) }), _jsxs(TableCell, { children: [_jsx(Badge, { variant: printed ? "secondary" : "default", children: printed ? t("statusPrinted") : t("statusToPrint") }), printed && (_jsx("div", { className: "mt-1 text-xs text-muted-foreground", children: new Date(item.printed_at).toLocaleTimeString([], {
56
+ }, children: [_jsxs(TableCell, { className: "font-medium", children: [_jsxs("div", { className: "flex items-center gap-2", children: [unitTitleOf(item), warnings.length > 0 && (_jsx(AlertTriangle, { className: "h-4 w-4 text-destructive", "aria-label": t("warning") }))] }), _jsx("div", { className: "text-xs text-muted-foreground", children: doc?.name ?? "-" })] }), _jsx(TableCell, { children: bufferOrdersOf(item).length > 0 ? "" : item.order_number }), _jsx(TableCell, { children: item.customer_name }), _jsx(TableCell, { className: "text-right", children: item.line_count }), _jsx(TableCell, { className: "text-right", children: (() => {
57
+ const info = cartColumnInfo(item);
58
+ return (_jsxs("div", { className: "flex flex-col items-end", children: [_jsx("span", { children: info.carts }), info.cartLabel && (_jsx("span", { className: "text-xs text-muted-foreground", children: info.cartLabel })), info.reason && (_jsx("span", { className: "text-xs text-muted-foreground", children: info.reason }))] }));
59
+ })() }), _jsx(TableCell, { className: "text-right", children: printedSlipCount(item) ?? "—" }), _jsx(TableCell, { className: "text-right", children: displayPageCount(item.line_count) }), _jsxs(TableCell, { children: [_jsx(Badge, { variant: printed ? "secondary" : "default", children: printed ? t("statusPrinted") : t("statusToPrint") }), printed && (_jsx("div", { className: "mt-1 text-xs text-muted-foreground", children: new Date(item.printed_at).toLocaleTimeString([], {
58
60
  hour: "2-digit",
59
61
  minute: "2-digit",
60
62
  }) }))] }), _jsx(TableCell, { children: _jsx("span", { className: "rounded px-2 py-1 text-xs font-semibold " +
@@ -67,15 +69,18 @@ export function QueueTable({ items, lockedIds, statuses, documentTypes, busyId,
67
69
  }, children: _jsx(Eye, { className: "h-5 w-5" }) })), !printed && onMarkPrinted && (_jsx("button", { type: "button", "aria-label": "Markeer " + item.document_number + " als geprint", title: "Markeer als geprint (zonder opnieuw printen)", disabled: readOnly || busyId === item.id, className: "inline-flex h-11 w-11 shrink-0 items-center justify-center rounded-md text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-50", onClick: (e) => {
68
70
  e.stopPropagation();
69
71
  onMarkPrinted(item);
70
- }, children: _jsx(Check, { className: "h-5 w-5" }) })), printed ? (_jsxs(Button, { size: "sm", variant: "ghost", disabled: readOnly || busyId === item.id, onClick: (e) => {
72
+ }, children: _jsx(Check, { className: "h-5 w-5" }) })), printed ? (_jsxs(Button, { size: "sm", variant: "ghost", disabled: readOnly || busyId === item.id || checking, onClick: (e) => {
71
73
  e.stopPropagation();
72
74
  (onPrintAll ?? onReprint ?? onPick)(item);
73
- }, children: [_jsx(RotateCcw, { className: "mr-1 h-4 w-4" }), t("print")] })) : locked ? (_jsxs("span", { className: "inline-flex items-center gap-1 text-xs text-muted-foreground", children: [_jsx(Lock, { className: "h-3.5 w-3.5" }), " ", t("inProgress")] })) : (_jsxs(Button, { size: "sm", variant: "outline", disabled: readOnly || busyId === item.id, onClick: (e) => {
75
+ }, children: [checking ? (_jsx(Loader2, { className: "mr-1 h-4 w-4 animate-spin" })) : ready ? (_jsx(RotateCcw, { className: "mr-1 h-4 w-4" })) : (_jsx(RefreshCw, { className: "mr-1 h-4 w-4" })), checking ? "Pakbon controleren" : ready ? t("print") : "Pakbon ophalen"] })) : locked ? (_jsxs("span", { className: "inline-flex items-center gap-1 text-xs text-muted-foreground", children: [_jsx(Lock, { className: "h-3.5 w-3.5" }), " ", t("inProgress")] })) : (_jsxs(Button, { size: "sm", variant: "outline", disabled: readOnly || busyId === item.id || checking, onClick: (e) => {
74
76
  e.stopPropagation();
75
77
  (onPrintAll ?? onPick)(item);
76
- }, children: [_jsx(Printer, { className: "mr-1 h-4 w-4" }), t("print")] }))] }) })] }, item.id));
78
+ }, children: [checking ? (_jsx(Loader2, { className: "mr-1 h-4 w-4 animate-spin" })) : !ready ? (_jsx(RefreshCw, { className: "mr-1 h-4 w-4" })) : (_jsx(Printer, { className: "mr-1 h-4 w-4" })), checking ? "Pakbon controleren" : ready ? t("print") : "Pakbon ophalen"] }))] }) })] }, item.id));
77
79
  }), safeItems.length === 0 && (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: 11, className: "py-10 text-center text-muted-foreground", children: t("emptyQueue") }) }))] })] }) }), _jsxs("div", { className: "space-y-3 md:hidden", children: [safeItems.map((item) => {
78
80
  const printed = !!item.printed_at;
81
+ const readiness = readinessById[item.id];
82
+ const checking = !readiness || readiness.checking;
83
+ const ready = readiness?.ready === true;
79
84
  const locked = !printed && lockedIds.has(item.id);
80
85
  const doc = item.document_type_id ? docById.get(item.document_type_id) : undefined;
81
86
  const warnings = Array.isArray(item.warnings) ? item.warnings : [];
@@ -95,16 +100,19 @@ export function QueueTable({ items, lockedIds, statuses, documentTypes, busyId,
95
100
  hour: "2-digit",
96
101
  minute: "2-digit",
97
102
  }) })), _jsx("span", { className: "rounded px-2 py-0.5 text-xs font-semibold " +
98
- priorityTone(item.priority, item.is_urgent), children: item.is_urgent ? t("urgent") : item.priority })] })] })] }), _jsxs("div", { className: "mt-2 min-w-0 space-y-0.5 text-sm", children: [bufferOrdersOf(item).length > 0 ? null : (_jsx("p", { className: "truncate text-foreground", children: item.order_number })), _jsx("p", { className: "truncate text-muted-foreground", children: item.customer_name })] }), _jsxs("div", { className: "mt-2 flex flex-wrap gap-x-3 gap-y-1 text-xs text-muted-foreground", children: [_jsxs("span", { children: [t("lines"), ": ", item.line_count] }), _jsxs("span", { children: ["Karren: ", displayCartColumn(item)] }), _jsxs("span", { children: ["Pakbonnen: ", printedSlipCount(item) ?? "—"] }), _jsxs("span", { children: [t("page"), ": ", displayPageCount(item.line_count)] }), _jsxs("span", { children: [t("announced"), ":", " ", new Date(item.announced_at).toLocaleTimeString(t.locale, {
103
+ priorityTone(item.priority, item.is_urgent), children: item.is_urgent ? t("urgent") : item.priority })] })] })] }), _jsxs("div", { className: "mt-2 min-w-0 space-y-0.5 text-sm", children: [bufferOrdersOf(item).length > 0 ? null : (_jsx("p", { className: "truncate text-foreground", children: item.order_number })), _jsx("p", { className: "truncate text-muted-foreground", children: item.customer_name })] }), _jsxs("div", { className: "mt-2 flex flex-wrap gap-x-3 gap-y-1 text-xs text-muted-foreground", children: [_jsxs("span", { children: [t("lines"), ": ", item.line_count] }), (() => {
104
+ const info = cartColumnInfo(item);
105
+ return (_jsxs("span", { children: ["Karren: ", info.carts, info.cartLabel ? ` · ${info.cartLabel}` : "", info.reason ? ` · ${info.reason}` : ""] }));
106
+ })(), _jsxs("span", { children: ["Pakbonnen: ", printedSlipCount(item) ?? "—"] }), _jsxs("span", { children: [t("page"), ": ", displayPageCount(item.line_count)] }), _jsxs("span", { children: [t("announced"), ":", " ", new Date(item.announced_at).toLocaleTimeString(t.locale, {
99
107
  hour: "2-digit",
100
108
  minute: "2-digit",
101
- })] }), _jsxs("span", { children: [t("waiting"), ": ", sinceLabel(item.announced_at)] })] }), _jsxs("div", { className: "mt-3", children: [printed ? (_jsxs(Button, { size: "lg", variant: "outline", className: "h-12 w-full", disabled: readOnly || busyId === item.id, onClick: (e) => {
109
+ })] }), _jsxs("span", { children: [t("waiting"), ": ", sinceLabel(item.announced_at)] })] }), _jsxs("div", { className: "mt-3", children: [printed ? (_jsxs(Button, { size: "lg", variant: "outline", className: "h-12 w-full", disabled: readOnly || busyId === item.id || checking, onClick: (e) => {
102
110
  e.stopPropagation();
103
111
  (onPrintAll ?? onReprint ?? onPick)(item);
104
- }, children: [_jsx(RotateCcw, { className: "mr-2 h-4 w-4" }), t("print")] })) : locked ? (_jsxs("span", { className: "inline-flex items-center gap-1 text-xs text-muted-foreground", children: [_jsx(Lock, { className: "h-3.5 w-3.5" }), " ", t("inProgress")] })) : (_jsxs(Button, { size: "lg", className: "h-12 w-full", disabled: readOnly || busyId === item.id, onClick: (e) => {
112
+ }, children: [checking ? (_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" })) : ready ? (_jsx(RotateCcw, { className: "mr-2 h-4 w-4" })) : (_jsx(RefreshCw, { className: "mr-2 h-4 w-4" })), checking ? "Pakbon controleren" : ready ? t("print") : "Pakbon ophalen"] })) : locked ? (_jsxs("span", { className: "inline-flex items-center gap-1 text-xs text-muted-foreground", children: [_jsx(Lock, { className: "h-3.5 w-3.5" }), " ", t("inProgress")] })) : (_jsxs(Button, { size: "lg", className: "h-12 w-full", disabled: readOnly || busyId === item.id || checking, onClick: (e) => {
105
113
  e.stopPropagation();
106
114
  (onPrintAll ?? onPick)(item);
107
- }, children: [_jsx(Printer, { className: "mr-2 h-4 w-4" }), t("print")] })), !printed && onMarkPrinted && (_jsxs(Button, { size: "sm", variant: "ghost", className: "mt-2 w-full", disabled: readOnly || busyId === item.id, onClick: (e) => {
115
+ }, children: [checking ? (_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" })) : !ready ? (_jsx(RefreshCw, { className: "mr-2 h-4 w-4" })) : (_jsx(Printer, { className: "mr-2 h-4 w-4" })), checking ? "Pakbon controleren" : ready ? t("print") : "Pakbon ophalen"] })), !printed && onMarkPrinted && (_jsxs(Button, { size: "sm", variant: "ghost", className: "mt-2 w-full", disabled: readOnly || busyId === item.id, onClick: (e) => {
108
116
  e.stopPropagation();
109
117
  onMarkPrinted(item);
110
118
  }, children: [_jsx(Check, { className: "mr-2 h-4 w-4" }), "Markeer als geprint"] }))] })] }, item.id));
@@ -33,6 +33,8 @@ export declare function usePickQueue(args: {
33
33
  error: string | null;
34
34
  lockedIds: Set<string>;
35
35
  realtimeOk: boolean;
36
+ /** Tijdstip van de laatste heropbouw na een buffer-seintje van de Engine. */
37
+ rebuiltAt: number | null;
36
38
  refresh: () => void;
37
39
  };
38
40
  /** Houdt bij dat dit scherm online is en ruimt verlopen locks op. */
@@ -1 +1 @@
1
- {"version":3,"file":"usePickQueue.d.ts","sourceRoot":"","sources":["../../src/hooks/usePickQueue.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAY,aAAa,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAG9E,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,UAAU,EAAE,OAAO,CAAC;IACpB,mDAAmD;IACnD,WAAW,EAAE,OAAO,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,YAAY,EAAE,CAAC;CAC3B;AAED,eAAO,MAAM,cAAc,EAAE,YAS5B,CAAC;AAEF;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,YAAY,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,2EAA2E;IAC3E,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;;;;;;;;;;;;EAoJA;AAED,qEAAqE;AACrE,wBAAgB,gBAAgB,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,EAAE,SAAS,CAAC,EAAE,MAAM;;EAuBhG;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,0FAOnF;AACD,iEAAiE;AACjE,wBAAgB,WAAW,CAAC,OAAO,UAAO;;;aAQzC"}
1
+ {"version":3,"file":"usePickQueue.d.ts","sourceRoot":"","sources":["../../src/hooks/usePickQueue.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAY,aAAa,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAG9E,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,UAAU,EAAE,OAAO,CAAC;IACpB,mDAAmD;IACnD,WAAW,EAAE,OAAO,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,YAAY,EAAE,CAAC;CAC3B;AAED,eAAO,MAAM,cAAc,EAAE,YAS5B,CAAC;AAEF;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,YAAY,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,2EAA2E;IAC3E,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;;;;;;;;;;;IAgLG,6EAA6E;;;EAIhF;AAED,qEAAqE;AACrE,wBAAgB,gBAAgB,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,EAAE,SAAS,CAAC,EAAE,MAAM;;EAuBhG;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,0FAOnF;AACD,iEAAiE;AACjE,wBAAgB,WAAW,CAAC,OAAO,UAAO;;;aAQzC"}
@@ -39,6 +39,20 @@ export function usePickQueue(args) {
39
39
  refetchInterval: 60000,
40
40
  retry: false,
41
41
  });
42
+ /**
43
+ * Seintjes van de Engine: een gesloten buffer of een losgekoppelde order.
44
+ * Zodra er zo'n seintje is, worden de regels opnieuw opgebouwd vanuit de
45
+ * Engine — de oude groepering per eenheid wordt niet meer getoond.
46
+ */
47
+ const bufferEventsQuery = useQuery({
48
+ queryKey: ["pick", "bufferEvents"],
49
+ enabled,
50
+ queryFn: () => pickApi.applyBufferEvents(),
51
+ refetchInterval: 15000,
52
+ retry: false,
53
+ });
54
+ const rebuildCount = bufferEventsQuery.data?.events ?? 0;
55
+ const [rebuiltAt, setRebuiltAt] = useState(null);
42
56
  const pickDayQuery = useQuery({
43
57
  queryKey: ["pick", "pickDay"],
44
58
  enabled: enabled && !syncQuery.isLoading,
@@ -54,6 +68,20 @@ export function usePickQueue(args) {
54
68
  queryClient.invalidateQueries({ queryKey: ["pick", "queue"] });
55
69
  queryClient.invalidateQueries({ queryKey: ["pick", "dayCounts"] });
56
70
  }, [syncQuery.data, queryClient]);
71
+ /**
72
+ * Na een buffer-seintje: de oude regels uit het geheugen gooien en alles
73
+ * opnieuw ophalen bij de Engine. Niets uit cache tonen.
74
+ */
75
+ useEffect(() => {
76
+ if (rebuildCount <= 0)
77
+ return;
78
+ queryClient.removeQueries({ queryKey: ["pick", "queue"] });
79
+ queryClient.invalidateQueries({ queryKey: ["pick", "pickDay"] });
80
+ queryClient.invalidateQueries({ queryKey: ["pick", "pickDays"] });
81
+ queryClient.invalidateQueries({ queryKey: ["pick", "dayCounts"] });
82
+ queryClient.invalidateQueries({ queryKey: ["pick", "countryCounts"] });
83
+ setRebuiltAt(Date.now());
84
+ }, [rebuildCount, bufferEventsQuery.dataUpdatedAt, queryClient]);
57
85
  const key = useMemo(() => ["pick", "queue", stationId ?? "-", screenId ?? "-", pickDay ?? "-", filters], [stationId, screenId, pickDay, filters]);
58
86
  const queueQuery = useQuery({
59
87
  queryKey: key,
@@ -142,6 +170,8 @@ export function usePickQueue(args) {
142
170
  error: queueQuery.error instanceof Error ? queueQuery.error.message : null,
143
171
  lockedIds,
144
172
  realtimeOk,
173
+ /** Tijdstip van de laatste heropbouw na een buffer-seintje van de Engine. */
174
+ rebuiltAt,
145
175
  refresh: invalidateNow,
146
176
  };
147
177
  }
@@ -0,0 +1,27 @@
1
+ import { type ShippingNoteStatus } from "../lib/shippingNoteStatus";
2
+ export interface WatchedShippingNote {
3
+ /** Het print_job-id van de pakbon; leidend bij het opvragen van de status. */
4
+ jobId: string;
5
+ cartId: string | null;
6
+ cartLabel: string | null;
7
+ state: ShippingNoteStatus;
8
+ }
9
+ /**
10
+ * Volgt per geprinte kar of de verzendbrief wordt klaargezet, op Floriday
11
+ * wacht, geprint is of is mislukt. Uitsluitend lezen: er wordt nooit een nieuwe
12
+ * aanvraag of printopdracht gemaakt.
13
+ */
14
+ export declare function useShippingNoteStatus(): {
15
+ shippingNotes: WatchedShippingNote[];
16
+ watchShippingNotes: (entries: {
17
+ jobId: string;
18
+ cartId?: string | null;
19
+ cartLabel?: string | null;
20
+ }[]) => void;
21
+ clearShippingNotes: () => void;
22
+ refreshShippingNotesNow: () => Promise<{
23
+ ok: boolean;
24
+ message: string | null;
25
+ }>;
26
+ };
27
+ //# sourceMappingURL=useShippingNoteStatus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useShippingNoteStatus.d.ts","sourceRoot":"","sources":["../../src/hooks/useShippingNoteStatus.ts"],"names":[],"mappings":"AACA,OAAO,EAIL,KAAK,kBAAkB,EACxB,MAAM,2BAA2B,CAAC;AAEnC,MAAM,WAAW,mBAAmB;IAClC,8EAA8E;IAC9E,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,EAAE,kBAAkB,CAAC;CAC3B;AAMD;;;;GAIG;AACH,wBAAgB,qBAAqB;;kCAYvB;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,EAAE;;mCA4C5B,OAAO,CAAC;QAC5D,EAAE,EAAE,OAAO,CAAC;QACZ,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;KACxB,CAAC;EAuFH"}
@@ -0,0 +1,132 @@
1
+ import { useCallback, useEffect, useRef, useState } from "react";
2
+ import { isWaitingShippingNote, readShippingNoteStatus, requestShippingNoteNow, } from "../lib/shippingNoteStatus";
3
+ /** Ververs elke 3 seconden, maximaal 15 minuten. */
4
+ const INTERVAL_MS = 3000;
5
+ const MAX_MS = 15 * 60 * 1000;
6
+ /**
7
+ * Volgt per geprinte kar of de verzendbrief wordt klaargezet, op Floriday
8
+ * wacht, geprint is of is mislukt. Uitsluitend lezen: er wordt nooit een nieuwe
9
+ * aanvraag of printopdracht gemaakt.
10
+ */
11
+ export function useShippingNoteStatus() {
12
+ const [notes, setNotes] = useState([]);
13
+ const notesRef = useRef([]);
14
+ const startedAt = useRef({});
15
+ const apply = useCallback((next) => {
16
+ notesRef.current = next;
17
+ setNotes(next);
18
+ }, []);
19
+ /** Start (of hergebruik) de statuscontrole voor de geprinte karren. */
20
+ const watchShippingNotes = useCallback((entries) => {
21
+ const now = Date.now();
22
+ const existing = new Map(notesRef.current.map((note) => [note.jobId, note]));
23
+ const next = [];
24
+ let hasNew = false;
25
+ for (const entry of Array.isArray(entries) ? entries : []) {
26
+ if (!entry?.jobId)
27
+ continue;
28
+ const prev = existing.get(entry.jobId);
29
+ if (!startedAt.current[entry.jobId]) {
30
+ startedAt.current[entry.jobId] = now;
31
+ hasNew = true;
32
+ }
33
+ next.push(prev ?? {
34
+ jobId: entry.jobId,
35
+ cartId: entry.cartId ?? null,
36
+ cartLabel: entry.cartLabel ?? null,
37
+ state: {
38
+ status: null,
39
+ message: null,
40
+ attempts: null,
41
+ nextAttemptAt: null,
42
+ source: null,
43
+ },
44
+ });
45
+ }
46
+ apply(next);
47
+ // Eén directe aanvraag per printactie, niet per kar en niet bij verversen.
48
+ if (hasNew)
49
+ void requestShippingNoteNow();
50
+ }, [apply]);
51
+ const clearShippingNotes = useCallback(() => {
52
+ startedAt.current = {};
53
+ apply([]);
54
+ }, [apply]);
55
+ /**
56
+ * Handmatig: vraagt de shell één keer om nu alle openstaande verzendbrieven op
57
+ * te halen en leest daarna de status van de karren die in beeld staan. Blijft
58
+ * alleen-lezen richting Floriday en maakt nooit een tweede aanvraag.
59
+ */
60
+ const refreshShippingNotesNow = useCallback(async () => {
61
+ const result = await requestShippingNoteNow();
62
+ const current = notesRef.current;
63
+ if (current.length > 0) {
64
+ const results = await Promise.all(current.map(async (note) => {
65
+ try {
66
+ return { jobId: note.jobId, state: await readShippingNoteStatus(note.jobId, note.cartId) };
67
+ }
68
+ catch {
69
+ return null;
70
+ }
71
+ }));
72
+ const byJob = new Map(results
73
+ .filter((r) => r !== null)
74
+ .map((r) => [r.jobId, r.state]));
75
+ if (byJob.size > 0) {
76
+ apply(notesRef.current.map((note) => byJob.has(note.jobId) ? { ...note, state: byJob.get(note.jobId) } : note));
77
+ }
78
+ }
79
+ return result;
80
+ }, [apply]);
81
+ const key = notes.map((note) => note.jobId).join(",");
82
+ useEffect(() => {
83
+ if (!key)
84
+ return;
85
+ let stopped = false;
86
+ let timer = null;
87
+ const stop = () => {
88
+ if (timer !== null) {
89
+ window.clearInterval(timer);
90
+ timer = null;
91
+ }
92
+ };
93
+ const read = async () => {
94
+ const current = notesRef.current;
95
+ const pending = current.filter((note) => {
96
+ if (note.state.status && !isWaitingShippingNote(note.state.status))
97
+ return false;
98
+ const since = startedAt.current[note.jobId] ?? Date.now();
99
+ return Date.now() - since < MAX_MS;
100
+ });
101
+ if (pending.length === 0) {
102
+ stop();
103
+ return;
104
+ }
105
+ const results = await Promise.all(pending.map(async (note) => {
106
+ try {
107
+ return { jobId: note.jobId, state: await readShippingNoteStatus(note.jobId, note.cartId) };
108
+ }
109
+ catch {
110
+ // Status tijdelijk niet leesbaar; volgende ronde volgt.
111
+ return null;
112
+ }
113
+ }));
114
+ if (stopped)
115
+ return;
116
+ const byJob = new Map(results.filter((r) => r !== null)
117
+ .map((r) => [r.jobId, r.state]));
118
+ if (byJob.size === 0)
119
+ return;
120
+ const updated = notesRef.current.map((note) => byJob.has(note.jobId) ? { ...note, state: byJob.get(note.jobId) } : note);
121
+ notesRef.current = updated;
122
+ setNotes(updated);
123
+ };
124
+ void read();
125
+ timer = window.setInterval(() => void read(), INTERVAL_MS);
126
+ return () => {
127
+ stopped = true;
128
+ stop();
129
+ };
130
+ }, [key]);
131
+ return { shippingNotes: notes, watchShippingNotes, clearShippingNotes, refreshShippingNotesNow };
132
+ }
@@ -14,6 +14,7 @@ export * from './lib/pickI18n';
14
14
  export { QueueTable } from './components/pick/QueueTable';
15
15
  export { ShippingNoteScan } from './components/pick/ShippingNoteScan';
16
16
  export * from './lib/shippingNote';
17
+ export * from './lib/shippingNoteStatus';
17
18
  export { PickScherm } from './components/pick/PickScherm';
18
19
  export { PickstationsPage } from './components/PickstationsPage';
19
20
  export { PickPublicPage } from './components/PickPublicPage';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAGvD,cAAc,kBAAkB,CAAC;AACjC,cAAc,2BAA2B,CAAC;AAC1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAErE,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,sBAAsB,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAChE,cAAc,gBAAgB,CAAC;AAC/B,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AACtE,cAAc,oBAAoB,CAAC;AACnC,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACpE,OAAO,EACL,gBAAgB,EAChB,mBAAmB,EACnB,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AAC9F,OAAO,EAAE,gCAAgC,EAAE,MAAM,wDAAwD,CAAC;AAC1G,OAAO,EAAE,oBAAoB,EAAE,MAAM,wCAAwC,CAAC;AAE9E,eAAO,MAAM,kBAAkB,EAAE,UAYhC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAGvD,cAAc,kBAAkB,CAAC;AACjC,cAAc,2BAA2B,CAAC;AAC1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAErE,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,sBAAsB,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAChE,cAAc,gBAAgB,CAAC;AAC/B,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AACtE,cAAc,oBAAoB,CAAC;AACnC,cAAc,0BAA0B,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACpE,OAAO,EACL,gBAAgB,EAChB,mBAAmB,EACnB,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AAC9F,OAAO,EAAE,gCAAgC,EAAE,MAAM,wDAAwD,CAAC;AAC1G,OAAO,EAAE,oBAAoB,EAAE,MAAM,wCAAwC,CAAC;AAE9E,eAAO,MAAM,kBAAkB,EAAE,UAYhC,CAAC"}
package/dist-lib/index.js CHANGED
@@ -30,6 +30,7 @@ export * from './lib/pickI18n';
30
30
  export { QueueTable } from './components/pick/QueueTable';
31
31
  export { ShippingNoteScan } from './components/pick/ShippingNoteScan';
32
32
  export * from './lib/shippingNote';
33
+ export * from './lib/shippingNoteStatus';
33
34
  export { PickScherm } from './components/pick/PickScherm';
34
35
  export { PickstationsPage } from './components/PickstationsPage';
35
36
  export { PickPublicPage } from './components/PickPublicPage';