@flowselections/pick-schermen-kwekers 1.0.16 → 1.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-lib/components/pick/PickScherm.d.ts +1 -1
- package/dist-lib/components/pick/PickScherm.d.ts.map +1 -1
- package/dist-lib/components/pick/PickScherm.js +513 -60
- package/dist-lib/components/pick/PreviewDialog.d.ts.map +1 -1
- package/dist-lib/components/pick/PreviewDialog.js +11 -2
- package/dist-lib/components/pick/QueueTable.d.ts +4 -1
- package/dist-lib/components/pick/QueueTable.d.ts.map +1 -1
- package/dist-lib/components/pick/QueueTable.js +77 -11
- package/dist-lib/components/pick/ShippingNoteScan.d.ts +15 -0
- package/dist-lib/components/pick/ShippingNoteScan.d.ts.map +1 -0
- package/dist-lib/components/pick/ShippingNoteScan.js +89 -0
- package/dist-lib/hooks/usePickQueue.d.ts +7 -0
- package/dist-lib/hooks/usePickQueue.d.ts.map +1 -1
- package/dist-lib/hooks/usePickQueue.js +20 -2
- package/dist-lib/hooks/usePrintJob.d.ts +23 -0
- package/dist-lib/hooks/usePrintJob.d.ts.map +1 -1
- package/dist-lib/hooks/usePrintJob.js +83 -1
- package/dist-lib/index.d.ts +2 -0
- package/dist-lib/index.d.ts.map +1 -1
- package/dist-lib/index.js +2 -0
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +3762 -1229
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.d.ts +3762 -1229
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +3762 -1229
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +3893 -1334
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.js +1 -0
- package/dist-lib/lib/pickApi.d.ts +61 -3
- package/dist-lib/lib/pickApi.d.ts.map +1 -1
- package/dist-lib/lib/pickApi.js +169 -2
- package/dist-lib/lib/pickPreview.d.ts +9 -4
- package/dist-lib/lib/pickPreview.d.ts.map +1 -1
- package/dist-lib/lib/pickPreview.js +43 -61
- package/dist-lib/lib/pickTypes.d.ts +87 -0
- package/dist-lib/lib/pickTypes.d.ts.map +1 -1
- package/dist-lib/lib/pickTypes.js +226 -0
- package/dist-lib/lib/pickUiText.d.ts +7 -0
- package/dist-lib/lib/pickUiText.d.ts.map +1 -1
- package/dist-lib/lib/pickUiText.js +14 -0
- package/dist-lib/lib/shippingNote.d.ts +41 -0
- package/dist-lib/lib/shippingNote.d.ts.map +1 -0
- package/dist-lib/lib/shippingNote.js +77 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PreviewDialog.d.ts","sourceRoot":"","sources":["../../../src/components/pick/PreviewDialog.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,UAAU,KAAK;IACb,IAAI,EAAE,aAAa,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,EAAE,KAAK,+
|
|
1
|
+
{"version":3,"file":"PreviewDialog.d.ts","sourceRoot":"","sources":["../../../src/components/pick/PreviewDialog.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,UAAU,KAAK;IACb,IAAI,EAAE,aAAa,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,EAAE,KAAK,+BAqGlE"}
|
|
@@ -9,6 +9,7 @@ import { fetchPickPreview, PickPreviewError } from "../../lib/pickPreview";
|
|
|
9
9
|
*/
|
|
10
10
|
export function PreviewDialog({ item, stationSlug, onClose }) {
|
|
11
11
|
const [url, setUrl] = useState(null);
|
|
12
|
+
const [html, setHtml] = useState(null);
|
|
12
13
|
const [bron, setBron] = useState(null);
|
|
13
14
|
const [error, setError] = useState(null);
|
|
14
15
|
const [loading, setLoading] = useState(false);
|
|
@@ -24,11 +25,19 @@ export function PreviewDialog({ item, stationSlug, onClose }) {
|
|
|
24
25
|
setLoading(true);
|
|
25
26
|
setError(null);
|
|
26
27
|
setUrl(null);
|
|
28
|
+
setHtml(null);
|
|
27
29
|
setBron(null);
|
|
28
30
|
fetchPickPreview({ stationSlug, queueItemId: item.id, signal: controller.signal })
|
|
29
31
|
.then((result) => {
|
|
30
|
-
|
|
32
|
+
if (controller.signal.aborted) {
|
|
33
|
+
if (result.isObjectUrl && result.url)
|
|
34
|
+
URL.revokeObjectURL(result.url);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (result.isObjectUrl)
|
|
38
|
+
objectUrl = result.url;
|
|
31
39
|
setUrl(result.url);
|
|
40
|
+
setHtml(result.html);
|
|
32
41
|
setBron(result.bron);
|
|
33
42
|
})
|
|
34
43
|
.catch((err) => {
|
|
@@ -48,5 +57,5 @@ export function PreviewDialog({ item, stationSlug, onClose }) {
|
|
|
48
57
|
URL.revokeObjectURL(objectUrl);
|
|
49
58
|
};
|
|
50
59
|
}, [item, stationSlug]);
|
|
51
|
-
return (_jsx(Dialog, { open: !!item, onOpenChange: (open) => !open && onClose(), children: _jsxs(DialogContent, { className: "max-w-3xl", children: [_jsxs(DialogHeader, { children: [_jsxs(DialogTitle, { children: ["Voorbeeld ", item?.document_number ?? ""] }), _jsx(DialogDescription, { children: bron ? `Bron: ${bron}` : "Zo komt dit document uit de printer." })] }), loading && (_jsx("div", { className: "flex h-96 items-center justify-center", children: _jsx(Loader2, { className: "h-8 w-8 animate-spin text-primary" }) })), !loading && error && (_jsxs("div", { className: "flex items-start gap-3 rounded-lg border border-destructive/40 bg-destructive/10 p-4", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-5 w-5 shrink-0 text-destructive" }), _jsx("p", { className: "text-sm text-foreground", children: error })] })), !loading && !error && url && (_jsx("object", { data: url, type: "application/pdf", className: "h-[70vh] max-h-[70vh] w-full rounded-lg border", "aria-label": `Voorbeeld van ${item?.document_number ?? "document"}`, children: _jsxs("p", { className: "p-4 text-sm text-muted-foreground", children: ["Deze browser kan de PDF niet tonen.", " ", _jsx("a", { className: "underline", href: url, target: "_blank", rel: "noreferrer", children: "Openen in een nieuw tabblad" })] }) }))] }) }));
|
|
60
|
+
return (_jsx(Dialog, { open: !!item, onOpenChange: (open) => !open && onClose(), children: _jsxs(DialogContent, { className: "max-w-3xl", children: [_jsxs(DialogHeader, { children: [_jsxs(DialogTitle, { children: ["Voorbeeld ", item?.document_number ?? ""] }), _jsx(DialogDescription, { children: bron ? `Bron: ${bron}` : "Zo komt dit document uit de printer." })] }), loading && (_jsx("div", { className: "flex h-96 items-center justify-center", children: _jsx(Loader2, { className: "h-8 w-8 animate-spin text-primary" }) })), !loading && error && (_jsxs("div", { className: "flex items-start gap-3 rounded-lg border border-destructive/40 bg-destructive/10 p-4", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-5 w-5 shrink-0 text-destructive" }), _jsx("p", { className: "text-sm text-foreground", children: error })] })), !loading && !error && url && (_jsx("object", { data: url, type: "application/pdf", className: "h-[70vh] max-h-[70vh] w-full rounded-lg border", "aria-label": `Voorbeeld van ${item?.document_number ?? "document"}`, children: _jsxs("p", { className: "p-4 text-sm text-muted-foreground", children: ["Deze browser kan de PDF niet tonen.", " ", _jsx("a", { className: "underline", href: url, target: "_blank", rel: "noreferrer", children: "Openen in een nieuw tabblad" })] }) })), !loading && !error && !url && html && (_jsx("iframe", { srcDoc: html, className: "h-[70vh] max-h-[70vh] w-full rounded-lg border bg-white", title: `Voorbeeld van ${item?.document_number ?? "document"}` }))] }) }));
|
|
52
61
|
}
|
|
@@ -8,10 +8,13 @@ interface Props {
|
|
|
8
8
|
busyId: string | null;
|
|
9
9
|
onPick: (item: PickQueueItem) => void;
|
|
10
10
|
onReprint?: (item: PickQueueItem) => void;
|
|
11
|
+
onPrintAll?: (item: PickQueueItem) => void;
|
|
12
|
+
onSelectPrinted?: (item: PickQueueItem) => void;
|
|
13
|
+
onMarkPrinted?: (item: PickQueueItem) => void;
|
|
11
14
|
onPreview?: (item: PickQueueItem) => void;
|
|
12
15
|
readOnly?: boolean;
|
|
13
16
|
t?: PickTranslator;
|
|
14
17
|
}
|
|
15
|
-
export declare function QueueTable({ items, lockedIds, statuses, documentTypes, busyId, onPick, onReprint, onPreview, readOnly, t: translator, }: Props): import("react").JSX.Element;
|
|
18
|
+
export declare function QueueTable({ items, lockedIds, statuses, documentTypes, busyId, onPick, onReprint, onPrintAll, onSelectPrinted, onMarkPrinted, onPreview, readOnly, t: translator, }: Props): import("react").JSX.Element;
|
|
16
19
|
export {};
|
|
17
20
|
//# sourceMappingURL=QueueTable.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QueueTable.d.ts","sourceRoot":"","sources":["../../../src/components/pick/QueueTable.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEvF,OAAO,EAAqB,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAE5E,UAAU,KAAK;IACb,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACvB,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,aAAa,EAAE,gBAAgB,EAAE,CAAC;IAClC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,CAAC;IACtC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,CAAC;IAC1C,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,CAAC;IAC1C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,CAAC,CAAC,EAAE,cAAc,CAAC;CACpB;
|
|
1
|
+
{"version":3,"file":"QueueTable.d.ts","sourceRoot":"","sources":["../../../src/components/pick/QueueTable.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEvF,OAAO,EAAqB,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAE5E,UAAU,KAAK;IACb,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACvB,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,aAAa,EAAE,gBAAgB,EAAE,CAAC;IAClC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,CAAC;IACtC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,CAAC;IAC1C,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,CAAC;IAC3C,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,CAAC;IAChD,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,CAAC;IAC9C,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,CAAC;IAC1C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,CAAC,CAAC,EAAE,cAAc,CAAC;CACpB;AAqBD,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,SAAS,EACT,QAAQ,EACR,aAAa,EACb,MAAM,EACN,MAAM,EACN,SAAS,EACT,UAAU,EACV,eAAe,EACf,aAAa,EACb,SAAS,EAGT,QAAQ,EACR,CAAC,EAAE,UAAU,GACd,EAAE,KAAK,+BAuXP"}
|
|
@@ -1,8 +1,21 @@
|
|
|
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, Eye, Lock, Printer, RotateCcw } from "lucide-react";
|
|
4
|
-
import { sinceLabel } from "../../lib/pickTypes";
|
|
3
|
+
import { AlertTriangle, Check, Eye, Lock, Printer, RotateCcw } from "lucide-react";
|
|
4
|
+
import { bufferOrdersOf, dedupeByUnit, displayCartColumn, displayCartCount, displayPageCount, sinceLabel, unitTitleOf } from "../../lib/pickTypes";
|
|
5
5
|
import { getPickTranslator } from "../../lib/pickI18n";
|
|
6
|
+
/**
|
|
7
|
+
* Uit hoeveel pakbonnen een order bestaat, is pas bekend nadat er geprint is.
|
|
8
|
+
* Vóór het printen blijft de kolom leeg in plaats van een aantal te beloven
|
|
9
|
+
* dat de Logistiek Engine nog kan wijzigen.
|
|
10
|
+
*/
|
|
11
|
+
function printedSlipCount(item) {
|
|
12
|
+
if (!item.printed_at)
|
|
13
|
+
return null;
|
|
14
|
+
const printed = Array.isArray(item.printed_cart_ids) ? item.printed_cart_ids.length : 0;
|
|
15
|
+
if (printed > 0)
|
|
16
|
+
return printed;
|
|
17
|
+
return displayCartCount(item.cart_count) ?? 1;
|
|
18
|
+
}
|
|
6
19
|
function priorityTone(priority, urgent) {
|
|
7
20
|
if (urgent)
|
|
8
21
|
return "bg-destructive text-destructive-foreground";
|
|
@@ -12,35 +25,88 @@ function priorityTone(priority, urgent) {
|
|
|
12
25
|
return "bg-secondary text-secondary-foreground";
|
|
13
26
|
return "bg-muted text-muted-foreground";
|
|
14
27
|
}
|
|
15
|
-
export function QueueTable({ items, lockedIds, statuses, documentTypes, busyId, onPick, onReprint, onPreview, readOnly, t: translator, }) {
|
|
28
|
+
export function QueueTable({ items, lockedIds, statuses, documentTypes, busyId, onPick, onReprint, onPrintAll, onSelectPrinted, onMarkPrinted, onPreview, readOnly, t: translator, }) {
|
|
16
29
|
const t = translator ?? getPickTranslator("nl");
|
|
17
|
-
|
|
30
|
+
// Nog te printen documenten altijd bovenaan; onderlinge volgorde blijft gelijk.
|
|
31
|
+
// Eén regel per eenheid: een bufferorder verschijnt nooit als losse orders.
|
|
32
|
+
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);
|
|
18
42
|
const docById = new Map((Array.isArray(documentTypes) ? documentTypes : []).map((d) => [d.id, d]));
|
|
19
|
-
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: "
|
|
43
|
+
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) => {
|
|
20
44
|
const printed = !!item.printed_at;
|
|
21
45
|
const locked = !printed && lockedIds.has(item.id);
|
|
22
46
|
const doc = item.document_type_id ? docById.get(item.document_type_id) : undefined;
|
|
23
47
|
const warnings = Array.isArray(item.warnings) ? item.warnings : [];
|
|
24
|
-
|
|
48
|
+
const selectable = !!onSelectPrinted && !readOnly && !locked;
|
|
49
|
+
return (_jsxs(TableRow, { tabIndex: selectable ? 0 : undefined, role: selectable ? "button" : undefined, "aria-label": selectable ? `Selecteer ${item.document_number}` : undefined, className: (locked || printed ? "opacity-60 " : "") + (selectable ? "cursor-pointer hover:bg-muted/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring" : ""), onClick: () => {
|
|
50
|
+
if (selectable)
|
|
51
|
+
onSelectPrinted?.(item);
|
|
52
|
+
}, onKeyDown: (event) => {
|
|
53
|
+
if (selectable && (event.key === "Enter" || event.key === " ")) {
|
|
54
|
+
event.preventDefault();
|
|
55
|
+
onSelectPrinted?.(item);
|
|
56
|
+
}
|
|
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([], {
|
|
58
|
+
hour: "2-digit",
|
|
59
|
+
minute: "2-digit",
|
|
60
|
+
}) }))] }), _jsx(TableCell, { children: _jsx("span", { className: "rounded px-2 py-1 text-xs font-semibold " +
|
|
25
61
|
priorityTone(item.priority, item.is_urgent), children: item.is_urgent ? t("urgent") : item.priority }) }), _jsx(TableCell, { className: "whitespace-nowrap text-sm", children: new Date(item.announced_at).toLocaleTimeString(t.locale, {
|
|
26
62
|
hour: "2-digit",
|
|
27
63
|
minute: "2-digit",
|
|
28
64
|
}) }), _jsx(TableCell, { className: "whitespace-nowrap text-sm", children: sinceLabel(item.announced_at) }), _jsx(TableCell, { className: "text-right", children: _jsxs("div", { className: "flex items-center justify-end gap-1", children: [onPreview && (_jsx("button", { type: "button", "aria-label": "Voorbeeld " + item.document_number, title: "Voorbeeld " + item.document_number, className: "inline-flex h-11 w-11 shrink-0 items-center justify-center rounded-md text-muted-foreground hover:bg-muted hover:text-foreground", onClick: (e) => {
|
|
29
65
|
e.stopPropagation();
|
|
30
66
|
onPreview(item);
|
|
31
|
-
}, children: _jsx(Eye, { className: "h-5 w-5" }) })), printed
|
|
67
|
+
}, 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
|
+
e.stopPropagation();
|
|
69
|
+
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) => {
|
|
71
|
+
e.stopPropagation();
|
|
72
|
+
(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) => {
|
|
74
|
+
e.stopPropagation();
|
|
75
|
+
(onPrintAll ?? onPick)(item);
|
|
76
|
+
}, children: [_jsx(Printer, { className: "mr-1 h-4 w-4" }), t("print")] }))] }) })] }, item.id));
|
|
32
77
|
}), 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) => {
|
|
33
78
|
const printed = !!item.printed_at;
|
|
34
79
|
const locked = !printed && lockedIds.has(item.id);
|
|
35
80
|
const doc = item.document_type_id ? docById.get(item.document_type_id) : undefined;
|
|
36
81
|
const warnings = Array.isArray(item.warnings) ? item.warnings : [];
|
|
37
|
-
|
|
82
|
+
const selectable = !!onSelectPrinted && !readOnly && !locked;
|
|
83
|
+
return (_jsxs("div", { tabIndex: selectable ? 0 : undefined, role: selectable ? "button" : undefined, "aria-label": selectable ? `Selecteer ${item.document_number}` : undefined, className: "rounded-lg border bg-card p-4 " + (locked || printed ? "opacity-60 " : "") + (selectable ? "cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring" : ""), onClick: () => {
|
|
84
|
+
if (selectable)
|
|
85
|
+
onSelectPrinted?.(item);
|
|
86
|
+
}, onKeyDown: (event) => {
|
|
87
|
+
if (selectable && (event.key === "Enter" || event.key === " ")) {
|
|
88
|
+
event.preventDefault();
|
|
89
|
+
onSelectPrinted?.(item);
|
|
90
|
+
}
|
|
91
|
+
}, children: [_jsxs("div", { className: "grid grid-cols-[minmax(0,1fr)_auto] items-start gap-3", children: [_jsxs("div", { className: "min-w-0", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [_jsx("span", { className: "truncate text-lg font-bold text-foreground", children: unitTitleOf(item) }), warnings.length > 0 && (_jsx(AlertTriangle, { className: "h-4 w-4 shrink-0 text-destructive", "aria-label": t("warning") }))] }), _jsx("p", { className: "truncate text-xs text-muted-foreground", children: doc?.name ?? "-" })] }), _jsxs("div", { className: "flex shrink-0 items-start gap-1", children: [onPreview && (_jsx("button", { type: "button", "aria-label": "Voorbeeld " + item.document_number, title: "Voorbeeld " + item.document_number, className: "inline-flex h-11 w-11 shrink-0 items-center justify-center rounded-md text-muted-foreground hover:bg-muted hover:text-foreground", onClick: (e) => {
|
|
38
92
|
e.stopPropagation();
|
|
39
93
|
onPreview(item);
|
|
40
|
-
}, children: _jsx(Eye, { className: "h-5 w-5" }) })), _jsxs("div", { className: "flex shrink-0 flex-col items-end gap-1", children: [_jsx(Badge, { variant: printed ? "secondary" : "default", children: printed ? t("statusPrinted") : t("statusToPrint") }), _jsx("span", { className: "
|
|
41
|
-
|
|
94
|
+
}, children: _jsx(Eye, { className: "h-5 w-5" }) })), _jsxs("div", { className: "flex shrink-0 flex-col items-end gap-1", children: [_jsx(Badge, { variant: printed ? "secondary" : "default", children: printed ? t("statusPrinted") : t("statusToPrint") }), printed && (_jsx("span", { className: "text-xs text-muted-foreground", children: new Date(item.printed_at).toLocaleTimeString([], {
|
|
95
|
+
hour: "2-digit",
|
|
96
|
+
minute: "2-digit",
|
|
97
|
+
}) })), _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, {
|
|
42
99
|
hour: "2-digit",
|
|
43
100
|
minute: "2-digit",
|
|
44
|
-
})] }), _jsxs("span", { children: [t("waiting"), ": ", sinceLabel(item.announced_at)] })] }),
|
|
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) => {
|
|
102
|
+
e.stopPropagation();
|
|
103
|
+
(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) => {
|
|
105
|
+
e.stopPropagation();
|
|
106
|
+
(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) => {
|
|
108
|
+
e.stopPropagation();
|
|
109
|
+
onMarkPrinted(item);
|
|
110
|
+
}, children: [_jsx(Check, { className: "mr-2 h-4 w-4" }), "Markeer als geprint"] }))] })] }, item.id));
|
|
45
111
|
}), safeItems.length === 0 && (_jsx("div", { className: "rounded-lg border py-10 text-center text-muted-foreground", children: t("emptyQueue") }))] })] }));
|
|
46
112
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
stationId: string | null;
|
|
3
|
+
/** Wordt aangeroepen met het nummer van de aangemaakte printopdracht. */
|
|
4
|
+
onJobCreated?: (args: {
|
|
5
|
+
jobId: string;
|
|
6
|
+
label: string | null;
|
|
7
|
+
}) => void;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Pakbon scannen -> verzendbrief. De module zoekt eerst op (leest alleen) en
|
|
11
|
+
* maakt pas na een expliciete bevestiging een verzendbrief aan via de Engine.
|
|
12
|
+
*/
|
|
13
|
+
export declare function ShippingNoteScan({ stationId, onJobCreated }: Props): import("react").JSX.Element;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=ShippingNoteScan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ShippingNoteScan.d.ts","sourceRoot":"","sources":["../../../src/components/pick/ShippingNoteScan.tsx"],"names":[],"mappings":"AASA,UAAU,KAAK;IACb,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,yEAAyE;IACzE,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,KAAK,IAAI,CAAC;CACxE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,SAAS,EAAE,YAAY,EAAE,EAAE,KAAK,+BA0LlE"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useState } from "react";
|
|
3
|
+
import { Badge, Button, Card, CardContent, Input, toast } from "@flowselections/core";
|
|
4
|
+
import { AlertTriangle, CheckCircle2, Loader2, ScanLine } from "lucide-react";
|
|
5
|
+
import { createShippingNote, lookupBarcode, } from "../../lib/shippingNote";
|
|
6
|
+
/**
|
|
7
|
+
* Pakbon scannen -> verzendbrief. De module zoekt eerst op (leest alleen) en
|
|
8
|
+
* maakt pas na een expliciete bevestiging een verzendbrief aan via de Engine.
|
|
9
|
+
*/
|
|
10
|
+
export function ShippingNoteScan({ stationId, onJobCreated }) {
|
|
11
|
+
const [code, setCode] = useState("");
|
|
12
|
+
const [busy, setBusy] = useState(false);
|
|
13
|
+
const [found, setFound] = useState(null);
|
|
14
|
+
const [notice, setNotice] = useState(null);
|
|
15
|
+
const [reprintNotice, setReprintNotice] = useState(null);
|
|
16
|
+
const [warning, setWarning] = useState(null);
|
|
17
|
+
const reset = useCallback(() => {
|
|
18
|
+
setFound(null);
|
|
19
|
+
setReprintNotice(null);
|
|
20
|
+
setWarning(null);
|
|
21
|
+
}, []);
|
|
22
|
+
const handleLookup = useCallback(async (raw) => {
|
|
23
|
+
const barcode = raw.trim();
|
|
24
|
+
if (!barcode)
|
|
25
|
+
return;
|
|
26
|
+
setBusy(true);
|
|
27
|
+
reset();
|
|
28
|
+
setNotice(null);
|
|
29
|
+
try {
|
|
30
|
+
const lookup = await lookupBarcode(barcode);
|
|
31
|
+
if (!lookup.ok) {
|
|
32
|
+
// De RPC levert een kant-en-klare melding; die tonen we letterlijk.
|
|
33
|
+
setNotice(lookup.error ?? "De barcode kon niet worden opgezocht.");
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
setFound({ barcode, lookup });
|
|
37
|
+
setCode("");
|
|
38
|
+
}
|
|
39
|
+
catch (e) {
|
|
40
|
+
setNotice(e instanceof Error ? e.message : "De barcode kon niet worden opgezocht.");
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
setBusy(false);
|
|
44
|
+
}
|
|
45
|
+
}, [reset]);
|
|
46
|
+
const handleConfirm = useCallback(async (allowReprint) => {
|
|
47
|
+
if (!found)
|
|
48
|
+
return;
|
|
49
|
+
setBusy(true);
|
|
50
|
+
setWarning(null);
|
|
51
|
+
try {
|
|
52
|
+
const result = await createShippingNote({
|
|
53
|
+
barcode: found.barcode,
|
|
54
|
+
allowReprint,
|
|
55
|
+
stationId,
|
|
56
|
+
});
|
|
57
|
+
if (!result.ok) {
|
|
58
|
+
if (result.errorCode === "al_geprint") {
|
|
59
|
+
setReprintNotice(result.error ?? "Er is al een verzendbrief voor deze kar gemaakt.");
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
setNotice(result.error ?? "De verzendbrief kon niet worden klaargezet.");
|
|
63
|
+
reset();
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (result.warning)
|
|
67
|
+
setWarning(result.warning);
|
|
68
|
+
toast.success("Verzendbrief staat klaar bij de printer.");
|
|
69
|
+
if (result.printJobId) {
|
|
70
|
+
onJobCreated?.({ jobId: result.printJobId, label: found.lookup.cart?.cartLabel ?? null });
|
|
71
|
+
}
|
|
72
|
+
setFound(null);
|
|
73
|
+
setReprintNotice(null);
|
|
74
|
+
}
|
|
75
|
+
catch (e) {
|
|
76
|
+
setNotice(e instanceof Error ? e.message : "De verzendbrief kon niet worden klaargezet.");
|
|
77
|
+
}
|
|
78
|
+
finally {
|
|
79
|
+
setBusy(false);
|
|
80
|
+
}
|
|
81
|
+
}, [found, stationId, onJobCreated, reset]);
|
|
82
|
+
const cart = found?.lookup.cart ?? null;
|
|
83
|
+
return (_jsx(Card, { children: _jsxs(CardContent, { className: "space-y-3 p-3 sm:p-4", children: [_jsxs("div", { className: "flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between", children: [_jsx("p", { className: "text-sm font-semibold text-foreground", children: "Verzendbrief \u00B7 pakbon scannen" }), _jsxs("div", { className: "relative w-full min-w-0 sm:w-72", children: [_jsx(ScanLine, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { className: "h-12 pl-9 text-base", placeholder: "Barcode van de pakbon", value: code, disabled: busy, onChange: (e) => setCode(e.target.value), onKeyDown: (e) => {
|
|
84
|
+
if (e.key !== "Enter")
|
|
85
|
+
return;
|
|
86
|
+
e.preventDefault();
|
|
87
|
+
void handleLookup(code);
|
|
88
|
+
} })] })] }), notice && (_jsxs("div", { className: "flex items-start gap-2 rounded-md border-2 border-destructive/50 bg-destructive/5 p-3 text-sm text-destructive", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0" }), _jsx("span", { children: notice })] })), warning && (_jsxs("div", { className: "flex items-start gap-2 rounded-md border p-3 text-sm text-muted-foreground", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0" }), _jsx("span", { children: warning })] })), found && (_jsxs("div", { className: "rounded-md border-2 border-primary/40 bg-muted/20 p-3 sm:p-4", children: [_jsxs("div", { className: "flex flex-wrap items-start justify-between gap-2", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "truncate text-lg font-bold text-foreground", children: found.lookup.orderNumber ?? "Order" }), _jsx("p", { className: "truncate text-sm text-muted-foreground", children: [found.lookup.customerName, cart?.cartLabel].filter(Boolean).join(" · ") })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [cart?.slipVariant && _jsx(Badge, { variant: "outline", children: cart.slipVariant }), cart?.barcode && _jsx(Badge, { variant: "outline", children: cart.barcode }), found.lookup.alreadyPrinted && (_jsxs(Badge, { className: "bg-destructive text-destructive-foreground", children: ["Al geprint (", found.lookup.printCount, ")"] }))] })] }), _jsx("div", { className: "mt-3 divide-y rounded-md border bg-background", children: cart && cart.items.length > 0 ? (cart.items.map((item, index) => (_jsxs("div", { className: "flex items-center justify-between gap-3 p-3", children: [_jsx("span", { className: "min-w-0 truncate text-sm text-foreground", children: item.productName }), _jsx("span", { className: "shrink-0 text-sm font-semibold text-foreground", children: item.quantity ?? 0 })] }, `${item.productName}-${index}`)))) : (_jsx("p", { className: "p-3 text-sm text-muted-foreground", children: "Geen regels bij deze kar." })) }), reprintNotice && (_jsxs("div", { className: "mt-3 flex items-start gap-2 rounded-md border-2 border-destructive/50 bg-destructive/5 p-3 text-sm text-destructive", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0" }), _jsx("span", { children: reprintNotice })] })), _jsxs("div", { className: "mt-3 flex flex-col gap-2 sm:flex-row sm:justify-end", children: [_jsx(Button, { variant: "outline", size: "lg", className: "h-12 w-full sm:w-auto", onClick: reset, disabled: busy, children: "Annuleren" }), _jsxs(Button, { size: "lg", className: "h-12 w-full sm:w-auto", onClick: () => void handleConfirm(reprintNotice !== null), disabled: busy, children: [busy ? (_jsx(Loader2, { className: "mr-2 h-5 w-5 animate-spin" })) : (_jsx(CheckCircle2, { className: "mr-2 h-5 w-5" })), reprintNotice ? "Toch opnieuw printen" : "Verzendbrief klaarzetten"] })] })] }))] }) }));
|
|
89
|
+
}
|
|
@@ -20,6 +20,8 @@ export declare function usePickQueue(args: {
|
|
|
20
20
|
screenId?: string;
|
|
21
21
|
filters: QueueFilters;
|
|
22
22
|
enabled?: boolean;
|
|
23
|
+
/** Handmatig gekozen pickdag (YYYY-MM-DD). Leeg = de dag van de Engine. */
|
|
24
|
+
pickDayOverride?: string | null;
|
|
23
25
|
}): {
|
|
24
26
|
items: PickQueueItem[];
|
|
25
27
|
total: number;
|
|
@@ -41,4 +43,9 @@ export declare function usePickCountryCounts(args: {
|
|
|
41
43
|
stationId?: string;
|
|
42
44
|
screenId?: string;
|
|
43
45
|
}): import("@tanstack/react-query").UseQueryResult<NoInfer<Record<string, number>>, Error>;
|
|
46
|
+
/** Pickdagen waarop nog orders klaarstaan (vandaag en later). */
|
|
47
|
+
export declare function usePickDays(enabled?: boolean): import("@tanstack/react-query").UseQueryResult<NoInfer<{
|
|
48
|
+
day: string;
|
|
49
|
+
orders: number;
|
|
50
|
+
}[]>, Error>;
|
|
44
51
|
//# sourceMappingURL=usePickQueue.d.ts.map
|
|
@@ -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;
|
|
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"}
|
|
@@ -18,13 +18,21 @@ export const defaultFilters = {
|
|
|
18
18
|
*/
|
|
19
19
|
export function usePickQueue(args) {
|
|
20
20
|
const queryClient = useQueryClient();
|
|
21
|
-
const { stationId, screenId, filters } = args;
|
|
21
|
+
const { stationId, screenId, filters, pickDayOverride } = args;
|
|
22
22
|
const enabled = args.enabled !== false;
|
|
23
23
|
const syncQuery = useQuery({
|
|
24
24
|
queryKey: ["pick", "sync"],
|
|
25
25
|
enabled,
|
|
26
26
|
queryFn: async () => {
|
|
27
27
|
await pickApi.syncQueue();
|
|
28
|
+
// Bewaking: orders zonder karplan meteen bij de Engine melden. Mislukt
|
|
29
|
+
// dit, dan mag de wachtrij daar niet op stuklopen.
|
|
30
|
+
try {
|
|
31
|
+
await pickApi.reportMissingCartPlans();
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
/* bewaking is aanvullend, nooit blokkerend */
|
|
35
|
+
}
|
|
28
36
|
return Date.now();
|
|
29
37
|
},
|
|
30
38
|
staleTime: 60000,
|
|
@@ -37,7 +45,7 @@ export function usePickQueue(args) {
|
|
|
37
45
|
queryFn: () => pickApi.currentPickDay(),
|
|
38
46
|
staleTime: 30000,
|
|
39
47
|
});
|
|
40
|
-
const pickDay = pickDayQuery.data ?? null;
|
|
48
|
+
const pickDay = pickDayOverride ?? pickDayQuery.data ?? null;
|
|
41
49
|
// Na een geslaagde synchronisatie opnieuw pickdag, wachtrij en tellers laden.
|
|
42
50
|
useEffect(() => {
|
|
43
51
|
if (syncQuery.data === undefined)
|
|
@@ -171,3 +179,13 @@ export function usePickCountryCounts(args) {
|
|
|
171
179
|
refetchInterval: 20000,
|
|
172
180
|
});
|
|
173
181
|
}
|
|
182
|
+
/** Pickdagen waarop nog orders klaarstaan (vandaag en later). */
|
|
183
|
+
export function usePickDays(enabled = true) {
|
|
184
|
+
return useQuery({
|
|
185
|
+
queryKey: ["pick", "pickDays"],
|
|
186
|
+
enabled,
|
|
187
|
+
queryFn: () => pickApi.availablePickDays(),
|
|
188
|
+
staleTime: 60000,
|
|
189
|
+
refetchInterval: 120000,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import type { PickPrintJob } from "../lib/pickTypes";
|
|
2
|
+
import type { PickBatchPrintJob } from "../lib/pickApi";
|
|
2
3
|
export interface TrackedPrintJob {
|
|
3
4
|
jobId: string;
|
|
4
5
|
queueItemId: string;
|
|
5
6
|
documentNumber: string | null;
|
|
7
|
+
cartId: string | null;
|
|
8
|
+
cartLabel: string | null;
|
|
6
9
|
job: PickPrintJob | null;
|
|
7
10
|
}
|
|
8
11
|
/**
|
|
@@ -16,7 +19,27 @@ export declare function usePrintJob(): {
|
|
|
16
19
|
jobId: string;
|
|
17
20
|
queueItemId: string;
|
|
18
21
|
documentNumber: string | null;
|
|
22
|
+
cartId?: string | null;
|
|
23
|
+
cartLabel?: string | null;
|
|
19
24
|
}) => void;
|
|
20
25
|
clear: () => void;
|
|
21
26
|
};
|
|
27
|
+
export interface TrackedPrintBatchJob extends PickBatchPrintJob {
|
|
28
|
+
job: PickPrintJob | null;
|
|
29
|
+
}
|
|
30
|
+
export interface TrackedPrintBatch {
|
|
31
|
+
queueItemId: string;
|
|
32
|
+
documentNumber: string | null;
|
|
33
|
+
jobs: TrackedPrintBatchJob[];
|
|
34
|
+
}
|
|
35
|
+
/** Volgt alle opdrachten van één order via één realtime-kanaal en één terugvalpoll. */
|
|
36
|
+
export declare function usePrintBatch(): {
|
|
37
|
+
batch: TrackedPrintBatch | null;
|
|
38
|
+
trackBatch: (args: {
|
|
39
|
+
queueItemId: string;
|
|
40
|
+
documentNumber: string | null;
|
|
41
|
+
jobs: PickBatchPrintJob[];
|
|
42
|
+
}) => void;
|
|
43
|
+
clearBatch: () => void;
|
|
44
|
+
};
|
|
22
45
|
//# sourceMappingURL=usePrintJob.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usePrintJob.d.ts","sourceRoot":"","sources":["../../src/hooks/usePrintJob.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"usePrintJob.d.ts","sourceRoot":"","sources":["../../src/hooks/usePrintJob.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAExD,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,GAAG,EAAE,YAAY,GAAG,IAAI,CAAC;CAC1B;AAED;;;;GAIG;AACH,wBAAgB,WAAW;;kBAKhB;QACL,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,EAAE,MAAM,CAAC;QACpB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9B,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACvB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAC3B;;EAgEJ;AAED,MAAM,WAAW,oBAAqB,SAAQ,iBAAiB;IAC7D,GAAG,EAAE,YAAY,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,IAAI,EAAE,oBAAoB,EAAE,CAAC;CAC9B;AAED,uFAAuF;AACvF,wBAAgB,aAAa;;uBAIW;QACpC,WAAW,EAAE,MAAM,CAAC;QACpB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9B,IAAI,EAAE,iBAAiB,EAAE,CAAC;KAC3B;;EA2EF"}
|
|
@@ -10,7 +10,12 @@ export function usePrintJob() {
|
|
|
10
10
|
const jobIdRef = useRef(null);
|
|
11
11
|
const track = useCallback((args) => {
|
|
12
12
|
jobIdRef.current = args.jobId;
|
|
13
|
-
setTracked({
|
|
13
|
+
setTracked({
|
|
14
|
+
...args,
|
|
15
|
+
cartId: args.cartId ?? null,
|
|
16
|
+
cartLabel: args.cartLabel ?? null,
|
|
17
|
+
job: null,
|
|
18
|
+
});
|
|
14
19
|
}, []);
|
|
15
20
|
const clear = useCallback(() => {
|
|
16
21
|
jobIdRef.current = null;
|
|
@@ -57,3 +62,80 @@ export function usePrintJob() {
|
|
|
57
62
|
}, [jobId]);
|
|
58
63
|
return { tracked, track, clear };
|
|
59
64
|
}
|
|
65
|
+
/** Volgt alle opdrachten van één order via één realtime-kanaal en één terugvalpoll. */
|
|
66
|
+
export function usePrintBatch() {
|
|
67
|
+
const [batch, setBatch] = useState(null);
|
|
68
|
+
const batchRef = useRef(null);
|
|
69
|
+
const trackBatch = useCallback((args) => {
|
|
70
|
+
const next = { ...args, jobs: args.jobs.map((job) => ({ ...job, job: null })) };
|
|
71
|
+
batchRef.current = next;
|
|
72
|
+
setBatch(next);
|
|
73
|
+
}, []);
|
|
74
|
+
const clearBatch = useCallback(() => {
|
|
75
|
+
batchRef.current = null;
|
|
76
|
+
setBatch(null);
|
|
77
|
+
}, []);
|
|
78
|
+
const batchKey = batch?.jobs.map((entry) => entry.jobId).join(",") ?? "";
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
if (!batchKey)
|
|
81
|
+
return;
|
|
82
|
+
const channelSeed = batchRef.current?.jobs[0]?.jobId ?? "unknown";
|
|
83
|
+
const jobIds = batchKey.split(",").filter(Boolean);
|
|
84
|
+
const jobIdSet = new Set(jobIds);
|
|
85
|
+
let stopped = false;
|
|
86
|
+
let timer = null;
|
|
87
|
+
const read = async () => {
|
|
88
|
+
const current = batchRef.current;
|
|
89
|
+
if (!current)
|
|
90
|
+
return;
|
|
91
|
+
let statuses;
|
|
92
|
+
try {
|
|
93
|
+
statuses = await pickApi.printJobsStatus(jobIds);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (stopped || batchRef.current !== current)
|
|
99
|
+
return;
|
|
100
|
+
const statusesById = new Map(statuses.map((status) => [status.id, status]));
|
|
101
|
+
const next = {
|
|
102
|
+
...current,
|
|
103
|
+
// Geannuleerde opdrachten (bijv. van een meelopend bufferlid) verdwijnen direct.
|
|
104
|
+
jobs: current.jobs
|
|
105
|
+
.map((entry) => ({
|
|
106
|
+
...entry,
|
|
107
|
+
job: statusesById.get(entry.jobId) ?? entry.job,
|
|
108
|
+
}))
|
|
109
|
+
.filter((entry) => entry.job?.status !== "geannuleerd"),
|
|
110
|
+
};
|
|
111
|
+
batchRef.current = next;
|
|
112
|
+
setBatch(next);
|
|
113
|
+
if (next.jobs.every((entry) => entry.job && ["voltooid", "mislukt", "geannuleerd"].includes(entry.job.status)) && timer !== null) {
|
|
114
|
+
window.clearInterval(timer);
|
|
115
|
+
timer = null;
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
void read();
|
|
119
|
+
timer = window.setInterval(read, 10000);
|
|
120
|
+
const channel = pickDb
|
|
121
|
+
.channel(`print_batch_${channelSeed}`)
|
|
122
|
+
.on("postgres_changes", {
|
|
123
|
+
event: "*",
|
|
124
|
+
schema: "public",
|
|
125
|
+
table: "print_jobs",
|
|
126
|
+
filter: `id=in.(${jobIds.join(",")})`,
|
|
127
|
+
}, (payload) => {
|
|
128
|
+
const changedId = payload.new?.id ?? payload.old?.id;
|
|
129
|
+
if (changedId && jobIdSet.has(changedId))
|
|
130
|
+
void read();
|
|
131
|
+
})
|
|
132
|
+
.subscribe();
|
|
133
|
+
return () => {
|
|
134
|
+
stopped = true;
|
|
135
|
+
if (timer !== null)
|
|
136
|
+
window.clearInterval(timer);
|
|
137
|
+
pickDb.removeChannel(channel);
|
|
138
|
+
};
|
|
139
|
+
}, [batchKey]);
|
|
140
|
+
return { batch, trackBatch, clearBatch };
|
|
141
|
+
}
|
package/dist-lib/index.d.ts
CHANGED
|
@@ -12,6 +12,8 @@ export * from './hooks/usePickQueue';
|
|
|
12
12
|
export { LanguageTiles } from './components/pick/LanguageTiles';
|
|
13
13
|
export * from './lib/pickI18n';
|
|
14
14
|
export { QueueTable } from './components/pick/QueueTable';
|
|
15
|
+
export { ShippingNoteScan } from './components/pick/ShippingNoteScan';
|
|
16
|
+
export * from './lib/shippingNote';
|
|
15
17
|
export { PickScherm } from './components/pick/PickScherm';
|
|
16
18
|
export { PickstationsPage } from './components/PickstationsPage';
|
|
17
19
|
export { PickPublicPage } from './components/PickPublicPage';
|
package/dist-lib/index.d.ts.map
CHANGED
|
@@ -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,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,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
|
@@ -28,6 +28,8 @@ export * from './hooks/usePickQueue';
|
|
|
28
28
|
export { LanguageTiles } from './components/pick/LanguageTiles';
|
|
29
29
|
export * from './lib/pickI18n';
|
|
30
30
|
export { QueueTable } from './components/pick/QueueTable';
|
|
31
|
+
export { ShippingNoteScan } from './components/pick/ShippingNoteScan';
|
|
32
|
+
export * from './lib/shippingNote';
|
|
31
33
|
export { PickScherm } from './components/pick/PickScherm';
|
|
32
34
|
export { PickstationsPage } from './components/PickstationsPage';
|
|
33
35
|
export { PickPublicPage } from './components/PickPublicPage';
|