@flowselections/floriday-afhandeling-module 1.0.11 → 1.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-lib/components/afhandeling/ControleLijstPrint.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/ControleLijstPrint.js +63 -2
- package/dist-lib/components/afhandeling/ControlePage.d.ts +3 -1
- package/dist-lib/components/afhandeling/ControlePage.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/ControlePage.js +6 -2
- package/dist-lib/components/pickstation/KioskControle.d.ts +2 -0
- package/dist-lib/components/pickstation/KioskControle.d.ts.map +1 -0
- package/dist-lib/components/pickstation/KioskControle.js +7 -0
- package/dist-lib/components/pickstation/PickstationPage.d.ts +2 -0
- package/dist-lib/components/pickstation/PickstationPage.d.ts.map +1 -0
- package/dist-lib/components/pickstation/PickstationPage.js +217 -0
- 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 +885 -60
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.d.ts +885 -60
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +885 -60
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +907 -62
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/afhandelingSettings.functions.d.ts +1767 -117
- package/dist-lib/lib/afhandelingSettings.functions.d.ts.map +1 -1
- package/dist-lib/lib/carriers.functions.d.ts +4125 -0
- package/dist-lib/lib/carriers.functions.d.ts.map +1 -1
- package/dist-lib/lib/cartAssignments.functions.d.ts +4125 -0
- package/dist-lib/lib/cartAssignments.functions.d.ts.map +1 -1
- package/dist-lib/lib/cartControl.functions.d.ts +8253 -0
- package/dist-lib/lib/cartControl.functions.d.ts.map +1 -1
- package/dist-lib/lib/cartControl.functions.js +3 -0
- package/dist-lib/lib/floriday.functions.d.ts +885 -60
- package/dist-lib/lib/floriday.functions.d.ts.map +1 -1
- package/dist-lib/lib/pickstationOrders.server.d.ts.map +1 -1
- package/dist-lib/lib/pickstationOrders.server.js +1 -0
- package/dist-lib/lib/pickstationTypes.d.ts +2 -0
- package/dist-lib/lib/pickstationTypes.d.ts.map +1 -1
- package/package.json +20 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ControleLijstPrint.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/ControleLijstPrint.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"ControleLijstPrint.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/ControleLijstPrint.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAqEnE,iFAAiF;AACjF,wBAAgB,kBAAkB,CAAC,EACjC,KAAK,EACL,OAAO,GACR,EAAE;IACD,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,sCAgJA"}
|
|
@@ -3,7 +3,7 @@ import { useEffect } from "react";
|
|
|
3
3
|
import { createPortal } from "react-dom";
|
|
4
4
|
import { Button } from "@flowselections/core";
|
|
5
5
|
import { X, Printer } from "lucide-react";
|
|
6
|
-
function
|
|
6
|
+
function fmtDate(iso) {
|
|
7
7
|
if (!iso)
|
|
8
8
|
return "—";
|
|
9
9
|
const d = new Date(iso);
|
|
@@ -15,6 +15,51 @@ function fmt(iso) {
|
|
|
15
15
|
year: "numeric",
|
|
16
16
|
});
|
|
17
17
|
}
|
|
18
|
+
/** Trek 1 uur af van "HH:MM" en geef terug als "HH:MM". */
|
|
19
|
+
function minusOneHour(hhmm) {
|
|
20
|
+
const [h, m] = hhmm.split(":").map((n) => parseInt(n, 10));
|
|
21
|
+
if (Number.isNaN(h) || Number.isNaN(m))
|
|
22
|
+
return hhmm;
|
|
23
|
+
const total = h * 60 + m - 60;
|
|
24
|
+
const norm = ((total % (24 * 60)) + 24 * 60) % (24 * 60);
|
|
25
|
+
const hh = String(Math.floor(norm / 60)).padStart(2, "0");
|
|
26
|
+
const mm = String(norm % 60).padStart(2, "0");
|
|
27
|
+
return `${hh}:${mm}`;
|
|
28
|
+
}
|
|
29
|
+
function todayIsoDay() {
|
|
30
|
+
const d = new Date();
|
|
31
|
+
const y = d.getFullYear();
|
|
32
|
+
const m = String(d.getMonth() + 1).padStart(2, "0");
|
|
33
|
+
const day = String(d.getDate()).padStart(2, "0");
|
|
34
|
+
return `${y}-${m}-${day}`;
|
|
35
|
+
}
|
|
36
|
+
function tomorrowIsoDay() {
|
|
37
|
+
const d = new Date();
|
|
38
|
+
d.setDate(d.getDate() + 1);
|
|
39
|
+
const y = d.getFullYear();
|
|
40
|
+
const m = String(d.getMonth() + 1).padStart(2, "0");
|
|
41
|
+
const day = String(d.getDate()).padStart(2, "0");
|
|
42
|
+
return `${y}-${m}-${day}`;
|
|
43
|
+
}
|
|
44
|
+
function computeKlaarzet(cart) {
|
|
45
|
+
const today = todayIsoDay();
|
|
46
|
+
const tomorrow = tomorrowIsoDay();
|
|
47
|
+
if (cart.pickupDate === today) {
|
|
48
|
+
const time = cart.pickupTime ? minusOneHour(cart.pickupTime) : null;
|
|
49
|
+
if (time) {
|
|
50
|
+
const [h, m] = time.split(":").map((n) => parseInt(n, 10));
|
|
51
|
+
return { label: time, sortKey: h * 60 + m };
|
|
52
|
+
}
|
|
53
|
+
// Ophaaldatum vandaag maar geen tijd bekend → einde van vandaag.
|
|
54
|
+
return { label: "vandaag", sortKey: 23 * 60 + 59 };
|
|
55
|
+
}
|
|
56
|
+
if (cart.pickupDate === tomorrow) {
|
|
57
|
+
// Moet uiterlijk vandaag klaar → toon datum vandaag.
|
|
58
|
+
return { label: fmtDate(today), sortKey: 23 * 60 + 58 };
|
|
59
|
+
}
|
|
60
|
+
// Overige gevallen: eind van de dag, met datum als label.
|
|
61
|
+
return { label: fmtDate(cart.pickupDate), sortKey: 24 * 60 + 999 };
|
|
62
|
+
}
|
|
18
63
|
/** Printbare controlelijst — één regel per kar met checkbox en opmerkingveld. */
|
|
19
64
|
export function ControleLijstPrint({ carts, onClose, }) {
|
|
20
65
|
useEffect(() => {
|
|
@@ -32,7 +77,23 @@ export function ControleLijstPrint({ carts, onClose, }) {
|
|
|
32
77
|
month: "long",
|
|
33
78
|
year: "numeric",
|
|
34
79
|
});
|
|
35
|
-
|
|
80
|
+
// Bereken klaarzet-moment per kar en sorteer op prioriteit
|
|
81
|
+
// (vroegste klaarzet bovenaan; daarna land, karnummer).
|
|
82
|
+
const sortedCarts = carts
|
|
83
|
+
.map((cart) => ({ cart, klaarzet: computeKlaarzet(cart) }))
|
|
84
|
+
.sort((a, b) => {
|
|
85
|
+
if (a.klaarzet.sortKey !== b.klaarzet.sortKey) {
|
|
86
|
+
return a.klaarzet.sortKey - b.klaarzet.sortKey;
|
|
87
|
+
}
|
|
88
|
+
const ac = a.cart.country ?? "\uFFFF";
|
|
89
|
+
const bc = b.cart.country ?? "\uFFFF";
|
|
90
|
+
if (ac !== bc)
|
|
91
|
+
return ac.localeCompare(bc);
|
|
92
|
+
const an = a.cart.cartNumber ?? "\uFFFF";
|
|
93
|
+
const bn = b.cart.cartNumber ?? "\uFFFF";
|
|
94
|
+
return an.localeCompare(bn);
|
|
95
|
+
});
|
|
96
|
+
const content = (_jsxs("div", { id: "controle-print-root", className: "fixed inset-0 z-[100] bg-white overflow-auto", children: [_jsxs("div", { className: "print:hidden sticky top-0 flex items-center justify-between p-3 border-b bg-white", children: [_jsx("div", { className: "font-semibold", children: "Controlelijst \u2014 printweergave" }), _jsxs("div", { className: "flex gap-2", children: [_jsxs(Button, { size: "sm", variant: "outline", onClick: () => window.print(), children: [_jsx(Printer, { className: "h-4 w-4 mr-2" }), "Opnieuw printen"] }), _jsxs(Button, { size: "sm", variant: "outline", onClick: onClose, children: [_jsx(X, { className: "h-4 w-4 mr-2" }), "Sluiten"] })] })] }), _jsxs("div", { className: "print-sheet p-8 print:p-0 max-w-[210mm] mx-auto text-black", children: [_jsxs("div", { className: "flex items-baseline justify-between border-b border-black pb-2 mb-4", children: [_jsx("h1", { className: "text-2xl font-bold", children: "Controlelijst karren" }), _jsx("div", { className: "text-sm", children: today })] }), _jsxs("div", { className: "text-sm mb-3", children: ["Totaal karren: ", _jsx("strong", { children: carts.length })] }), _jsxs("table", { className: "w-full text-[11px] border-collapse", children: [_jsx("thead", { children: _jsxs("tr", { className: "border-b border-black", children: [_jsx("th", { className: "text-left py-1 pr-2", children: "KARNR" }), _jsx("th", { className: "text-right py-1 pr-2", children: "FUSTEN" }), _jsx("th", { className: "text-left py-1 pr-2", children: "LAND" }), _jsx("th", { className: "text-left py-1 pr-2", children: "KLAARZETTEN" }), _jsx("th", { className: "text-center py-1 px-2", children: "KLAAR" }), _jsx("th", { className: "text-left py-1 pr-2", children: "OPMERKINGEN" })] }) }), _jsxs("tbody", { children: [sortedCarts.map(({ cart: c, klaarzet }) => (_jsxs("tr", { className: "border-b border-gray-300", children: [_jsx("td", { className: "py-2 pr-2 font-mono font-bold whitespace-nowrap", children: c.cartNumber ?? "—" }), _jsx("td", { className: "py-2 pr-2 text-right whitespace-nowrap", children: c.cratesOnCart ?? "—" }), _jsx("td", { className: "py-2 pr-2 whitespace-nowrap", children: c.country ?? "—" }), _jsx("td", { className: "py-2 pr-2 whitespace-nowrap font-semibold", children: klaarzet.label }), _jsx("td", { className: "py-2 px-2 text-center", children: _jsx("span", { className: "inline-block w-4 h-4 border border-black" }) }), _jsx("td", { className: "py-2 pr-2 w-[35%]", children: _jsx("span", { className: "inline-block w-full border-b border-gray-400 h-4" }) })] }, `${c.orderSource}#${c.orderId}#${c.cartIndex}`))), sortedCarts.length === 0 && (_jsx("tr", { children: _jsx("td", { colSpan: 6, className: "py-6 text-center text-gray-500", children: "Geen karren om vandaag te controleren." }) }))] })] }), _jsxs("div", { className: "mt-6 text-[11px]", children: [_jsxs("div", { children: ["Gecontroleerd door: ", _jsx("span", { className: "inline-block border-b border-black w-64" })] }), _jsxs("div", { className: "mt-2", children: ["Datum / tijd: ", _jsx("span", { className: "inline-block border-b border-black w-64" })] }), _jsxs("div", { className: "mt-2", children: ["Handtekening: ", _jsx("span", { className: "inline-block border-b border-black w-64" })] })] })] }), _jsx("style", { children: `
|
|
36
97
|
@media print {
|
|
37
98
|
@page { size: A4 portrait; margin: 12mm; }
|
|
38
99
|
html, body { background: #fff !important; margin: 0 !important; padding: 0 !important; }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ControlePage.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/ControlePage.tsx"],"names":[],"mappings":"AA4GA,wBAAgB,YAAY,
|
|
1
|
+
{"version":3,"file":"ControlePage.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/ControlePage.tsx"],"names":[],"mappings":"AA4GA,wBAAgB,YAAY,CAAC,EAAE,MAAM,EAAE,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;CAAO,+BA+tBpE"}
|
|
@@ -54,7 +54,7 @@ function buildDraftDto(input, formState, updatedAt) {
|
|
|
54
54
|
updatedAt,
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
|
-
export function ControlePage() {
|
|
57
|
+
export function ControlePage({ onExit } = {}) {
|
|
58
58
|
const qc = useQueryClient();
|
|
59
59
|
const [tab, setTab] = useState("controle");
|
|
60
60
|
const [notes, setNotes] = useState("");
|
|
@@ -411,7 +411,11 @@ export function ControlePage() {
|
|
|
411
411
|
? "Voortgang automatisch opgeslagen"
|
|
412
412
|
: lastSavedRef.current && notes === lastSavedRef.current
|
|
413
413
|
? "Concept bewaard"
|
|
414
|
-
: "" })] }), _jsx(Textarea, { value: notes, onChange: (e) => setNotes(e.target.value), placeholder: "Bijzonderheden, afwijkingen\u2026", rows: 3 }), _jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: "Wordt automatisch bewaard tot de controle definitief is goedgekeurd." })] })] })), tab === "historie" && _jsx(HistorieView, { loading: histQ.isLoading, sessions: histQ.data?.sessions ?? [] }), showPrint && (_jsx(ControleLijstPrint, { carts: carts, onClose: () => setShowPrint(false) })), _jsx(Dialog, { open: !!result, onOpenChange: (v) => !v && setResult(null), children: _jsxs(ResponsiveDialogContent, { size: "md", children: [_jsxs(ResponsiveDialogHeader, { children: [_jsx(DialogTitle, { children: "Resultaat controle" }), _jsxs(DialogDescription, { children: [result?.checkedCount, " klaar \u00B7 ", result?.rescheduledCount, " doorgeschoven \u00B7", " ", result?.blockedCount, " geblokkeerd"] })] }), _jsxs(ResponsiveDialogBody, { className: "space-y-4", children: [result?.blocked && result.blocked.length > 0 && (_jsxs("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 p-3 space-y-2", children: [_jsxs("div", { className: "flex items-start gap-2 text-destructive font-medium", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0" }), _jsx("span", { children: "De volgende karren kunnen niet automatisch worden doorgeschoven omdat hierdoor de planning of levering in gevaar komt." })] }), _jsx(BlockedTable, { blocked: result.blocked })] })), result?.rescheduled && result.rescheduled.length > 0 && (_jsxs("div", { className: "border rounded-md p-3", children: [_jsx("div", { className: "font-medium mb-2", children: "Doorgeschoven karren" }), _jsx("ul", { className: "text-sm space-y-1", children: result.rescheduled.map((r, i) => (_jsxs("li", { children: [_jsx("span", { className: "font-mono", children: r.cartNumber ?? "—" }), " \u2014", " ", r.customerName, " \u2192 nieuwe pickdatum", " ", _jsx("strong", { children: fmtDate(r.newPickDate) })] }, i))) })] }))] }), _jsx(ResponsiveDialogFooter, { children: _jsx(Button, { onClick: () =>
|
|
414
|
+
: "" })] }), _jsx(Textarea, { value: notes, onChange: (e) => setNotes(e.target.value), placeholder: "Bijzonderheden, afwijkingen\u2026", rows: 3 }), _jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: "Wordt automatisch bewaard tot de controle definitief is goedgekeurd." })] })] })), tab === "historie" && _jsx(HistorieView, { loading: histQ.isLoading, sessions: histQ.data?.sessions ?? [] }), showPrint && (_jsx(ControleLijstPrint, { carts: carts, onClose: () => setShowPrint(false) })), _jsx(Dialog, { open: !!result, onOpenChange: (v) => !v && setResult(null), children: _jsxs(ResponsiveDialogContent, { size: "md", children: [_jsxs(ResponsiveDialogHeader, { children: [_jsx(DialogTitle, { children: "Resultaat controle" }), _jsxs(DialogDescription, { children: [result?.checkedCount, " klaar \u00B7 ", result?.rescheduledCount, " doorgeschoven \u00B7", " ", result?.blockedCount, " geblokkeerd"] })] }), _jsxs(ResponsiveDialogBody, { className: "space-y-4", children: [result?.blocked && result.blocked.length > 0 && (_jsxs("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 p-3 space-y-2", children: [_jsxs("div", { className: "flex items-start gap-2 text-destructive font-medium", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0" }), _jsx("span", { children: "De volgende karren kunnen niet automatisch worden doorgeschoven omdat hierdoor de planning of levering in gevaar komt." })] }), _jsx(BlockedTable, { blocked: result.blocked })] })), result?.rescheduled && result.rescheduled.length > 0 && (_jsxs("div", { className: "border rounded-md p-3", children: [_jsx("div", { className: "font-medium mb-2", children: "Doorgeschoven karren" }), _jsx("ul", { className: "text-sm space-y-1", children: result.rescheduled.map((r, i) => (_jsxs("li", { children: [_jsx("span", { className: "font-mono", children: r.cartNumber ?? "—" }), " \u2014", " ", r.customerName, " \u2192 nieuwe pickdatum", " ", _jsx("strong", { children: fmtDate(r.newPickDate) })] }, i))) })] }))] }), _jsx(ResponsiveDialogFooter, { children: _jsx(Button, { onClick: () => {
|
|
415
|
+
setResult(null);
|
|
416
|
+
if (onExit)
|
|
417
|
+
onExit();
|
|
418
|
+
}, children: "Sluiten" }) })] }) }), _jsx(Dialog, { open: !!pendingProposal, onOpenChange: (v) => !v && setPendingProposal(null), children: _jsxs(ResponsiveDialogContent, { size: "lg", rows: "with-alert", children: [_jsxs(ResponsiveDialogHeader, { children: [_jsx(DialogTitle, { children: "Controle beslisscherm" }), _jsx(DialogDescription, { children: "Bepaal wat er met de niet-gereed gemelde karren moet gebeuren voordat je de controle definitief afrondt." })] }), _jsx("div", { className: criticalTomorrowCount > 0 ? "border-b bg-destructive/10 px-6 py-4" : "border-b bg-muted/30 px-6 py-4", children: _jsxs("div", { className: criticalTomorrowCount > 0 ? "flex min-w-0 items-start gap-3 text-destructive" : "flex min-w-0 items-start gap-3 text-muted-foreground", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-5 w-5 shrink-0" }), _jsxs("div", { className: "min-w-0 space-y-1", children: [_jsx("div", { className: "font-semibold", children: criticalTomorrowCount > 0
|
|
415
419
|
? `LET OP: Er ${criticalTomorrowCount === 1 ? "staat 1 kar" : `staan ${criticalTomorrowCount} karren`} gepland om morgen opgehaald te worden die nog niet gereed ${criticalTomorrowCount === 1 ? "is" : "zijn"}.`
|
|
416
420
|
: "Geen kritieke karren met ophaaldatum morgen." }), _jsx("div", { className: criticalTomorrowCount > 0 ? "text-sm text-destructive/90" : "text-sm text-muted-foreground", children: criticalTomorrowCount > 0
|
|
417
421
|
? "Controleer deze karren voordat je verder gaat met de vervolgprocessen."
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KioskControle.d.ts","sourceRoot":"","sources":["../../../src/components/pickstation/KioskControle.tsx"],"names":[],"mappings":"AAGA,wBAAgB,aAAa,gCAoB5B"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useNavigate } from "@tanstack/react-router";
|
|
3
|
+
import { ControlePage } from "../afhandeling/ControlePage";
|
|
4
|
+
export function KioskControle() {
|
|
5
|
+
const navigate = useNavigate();
|
|
6
|
+
return (_jsxs("div", { className: "min-h-screen bg-slate-50 text-slate-900 flex flex-col", children: [_jsx("header", { className: "bg-white border-b sticky top-0 z-20", children: _jsxs("div", { className: "max-w-6xl mx-auto px-4 py-3 flex items-center gap-3", children: [_jsx("button", { onClick: () => navigate({ to: "/pickstation" }), className: "h-12 px-4 rounded-xl bg-white border-2 border-slate-300 hover:bg-slate-100 font-semibold", children: "\u2190 Terug naar Picking" }), _jsx("h1", { className: "text-xl font-semibold", children: "Picking-modus" })] }) }), _jsx("main", { className: "flex-1", children: _jsx(ControlePage, { onExit: () => navigate({ to: "/pickstation" }) }) })] }));
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PickstationPage.d.ts","sourceRoot":"","sources":["../../../src/components/pickstation/PickstationPage.tsx"],"names":[],"mappings":"AA6CA,wBAAgB,eAAe,gCA8W9B"}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
3
|
+
import { useNavigate } from "@tanstack/react-router";
|
|
4
|
+
import { useQuery } from "@tanstack/react-query";
|
|
5
|
+
import { LanguagePicker } from "./LanguagePicker";
|
|
6
|
+
import { renderPakbonHtml } from "./PakbonTemplate";
|
|
7
|
+
import { printHtml, printPdfBase64 } from "./printDocuments";
|
|
8
|
+
import { ControleLijstPrint } from "../afhandeling/ControleLijstPrint";
|
|
9
|
+
import { PICKSTATION_DICT, PICKSTATION_LANGUAGES, } from "../../lib/pickstationI18n";
|
|
10
|
+
const LANG_KEY = "pickstation.lang";
|
|
11
|
+
const LAST_GOOD_ORDERS_KEY = "pickstation.lastGoodOrders";
|
|
12
|
+
function statusBadge(status, t, printedCount) {
|
|
13
|
+
switch (status) {
|
|
14
|
+
case "not_printed":
|
|
15
|
+
return { label: t.notPrinted, className: "bg-slate-200 text-slate-700" };
|
|
16
|
+
case "printed":
|
|
17
|
+
return {
|
|
18
|
+
label: `✓ ${t.printed(printedCount || 1)}`,
|
|
19
|
+
className: "bg-emerald-100 text-emerald-800",
|
|
20
|
+
};
|
|
21
|
+
case "checked":
|
|
22
|
+
return { label: `✓ ${t.statusChecked}`, className: "bg-sky-100 text-sky-800" };
|
|
23
|
+
case "ready":
|
|
24
|
+
return { label: t.statusReady, className: "bg-indigo-100 text-indigo-800" };
|
|
25
|
+
case "shipped":
|
|
26
|
+
return { label: t.statusShipped, className: "bg-amber-100 text-amber-900" };
|
|
27
|
+
case "delivered":
|
|
28
|
+
return { label: `✓ ${t.statusDelivered}`, className: "bg-emerald-600 text-white" };
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export function PickstationPage() {
|
|
32
|
+
const navigate = useNavigate();
|
|
33
|
+
const [lang, setLang] = useState(null);
|
|
34
|
+
const [printingId, setPrintingId] = useState(null);
|
|
35
|
+
const [pickerOpen, setPickerOpen] = useState(false);
|
|
36
|
+
const [lastGoodOrders, setLastGoodOrders] = useState([]);
|
|
37
|
+
const [pendingConfirm, setPendingConfirm] = useState(null);
|
|
38
|
+
const [confirming, setConfirming] = useState(false);
|
|
39
|
+
const [printWarning, setPrintWarning] = useState(null);
|
|
40
|
+
const [showChecklist, setShowChecklist] = useState(false);
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
const stored = (typeof localStorage !== "undefined"
|
|
43
|
+
? localStorage.getItem(LANG_KEY)
|
|
44
|
+
: null);
|
|
45
|
+
const storedOrders = typeof localStorage !== "undefined"
|
|
46
|
+
? localStorage.getItem(LAST_GOOD_ORDERS_KEY)
|
|
47
|
+
: null;
|
|
48
|
+
if (storedOrders) {
|
|
49
|
+
try {
|
|
50
|
+
const parsed = JSON.parse(storedOrders);
|
|
51
|
+
if (Array.isArray(parsed))
|
|
52
|
+
setLastGoodOrders(parsed);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
localStorage.removeItem(LAST_GOOD_ORDERS_KEY);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (stored && PICKSTATION_LANGUAGES.some((l) => l.code === stored)) {
|
|
59
|
+
setLang(stored);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
setPickerOpen(true);
|
|
63
|
+
}
|
|
64
|
+
}, []);
|
|
65
|
+
const pickLang = useCallback((l) => {
|
|
66
|
+
setLang(l);
|
|
67
|
+
setPickerOpen(false);
|
|
68
|
+
try {
|
|
69
|
+
localStorage.setItem(LANG_KEY, l);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
/* ignore */
|
|
73
|
+
}
|
|
74
|
+
}, []);
|
|
75
|
+
const ordersQuery = useQuery({
|
|
76
|
+
queryKey: ["pickstation", "orders"],
|
|
77
|
+
queryFn: async () => {
|
|
78
|
+
const res = await fetch("/api/public/pickstation/orders", { cache: "no-store" });
|
|
79
|
+
if (!res.ok)
|
|
80
|
+
throw new Error(`HTTP ${res.status}`);
|
|
81
|
+
return res.json();
|
|
82
|
+
},
|
|
83
|
+
refetchInterval: 30000,
|
|
84
|
+
refetchOnWindowFocus: true,
|
|
85
|
+
enabled: lang !== null,
|
|
86
|
+
});
|
|
87
|
+
const t = useMemo(() => PICKSTATION_DICT[lang ?? "nl"], [lang]);
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
if (ordersQuery.data && !ordersQuery.data.error) {
|
|
90
|
+
const next = ordersQuery.data.orders ?? [];
|
|
91
|
+
setLastGoodOrders(next);
|
|
92
|
+
try {
|
|
93
|
+
localStorage.setItem(LAST_GOOD_ORDERS_KEY, JSON.stringify(next));
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
/* ignore */
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}, [ordersQuery.data]);
|
|
100
|
+
const queryError = ordersQuery.data?.error ??
|
|
101
|
+
(ordersQuery.error instanceof Error ? ordersQuery.error.message : undefined);
|
|
102
|
+
const freshOrders = ordersQuery.data?.orders ?? [];
|
|
103
|
+
const orders = queryError ? lastGoodOrders : freshOrders;
|
|
104
|
+
const printedCount = orders.filter((o) => o.printedCount > 0).length;
|
|
105
|
+
const handlePrint = useCallback(async (order) => {
|
|
106
|
+
if (printingId || pendingConfirm)
|
|
107
|
+
return;
|
|
108
|
+
setPrintingId(order.id);
|
|
109
|
+
setPrintWarning(null);
|
|
110
|
+
try {
|
|
111
|
+
let auctionLetter = null;
|
|
112
|
+
try {
|
|
113
|
+
const res = await fetch("/api/public/pickstation/print", {
|
|
114
|
+
method: "POST",
|
|
115
|
+
headers: { "Content-Type": "application/json" },
|
|
116
|
+
body: JSON.stringify({
|
|
117
|
+
orderId: order.id,
|
|
118
|
+
sourceOrderId: order.orderId,
|
|
119
|
+
orderNumber: `${order.orderNumber} (kar ${order.cartIndex}/${order.cartTotal})`,
|
|
120
|
+
customerName: order.customerName,
|
|
121
|
+
documents: ["packing-slip", "auction-letter"],
|
|
122
|
+
confirmed: false,
|
|
123
|
+
}),
|
|
124
|
+
});
|
|
125
|
+
if (res.ok) {
|
|
126
|
+
const body = (await res.json());
|
|
127
|
+
auctionLetter = body.auctionLetter ?? null;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
/* network error – continue with pakbon only */
|
|
132
|
+
}
|
|
133
|
+
const html = renderPakbonHtml(order);
|
|
134
|
+
await printHtml(html);
|
|
135
|
+
if (auctionLetter) {
|
|
136
|
+
await printPdfBase64(auctionLetter.base64, auctionLetter.mime);
|
|
137
|
+
}
|
|
138
|
+
setPendingConfirm(order);
|
|
139
|
+
}
|
|
140
|
+
finally {
|
|
141
|
+
setPrintingId(null);
|
|
142
|
+
}
|
|
143
|
+
}, [printingId, pendingConfirm]);
|
|
144
|
+
const confirmPrinted = useCallback(async (yes) => {
|
|
145
|
+
const order = pendingConfirm;
|
|
146
|
+
if (!order)
|
|
147
|
+
return;
|
|
148
|
+
if (!yes) {
|
|
149
|
+
setPendingConfirm(null);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
setConfirming(true);
|
|
153
|
+
setPrintWarning(null);
|
|
154
|
+
try {
|
|
155
|
+
const res = await fetch("/api/public/pickstation/print", {
|
|
156
|
+
method: "POST",
|
|
157
|
+
headers: { "Content-Type": "application/json" },
|
|
158
|
+
body: JSON.stringify({
|
|
159
|
+
orderId: order.id,
|
|
160
|
+
sourceOrderId: order.orderId,
|
|
161
|
+
orderNumber: `${order.orderNumber} (kar ${order.cartIndex}/${order.cartTotal})`,
|
|
162
|
+
customerName: order.customerName,
|
|
163
|
+
documents: ["packing-slip", "auction-letter"],
|
|
164
|
+
confirmed: true,
|
|
165
|
+
}),
|
|
166
|
+
});
|
|
167
|
+
if (res.ok) {
|
|
168
|
+
const body = (await res.json());
|
|
169
|
+
if (body.warning)
|
|
170
|
+
setPrintWarning(body.warning);
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
setPrintWarning(`Bevestigen mislukt (HTTP ${res.status}).`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
catch (e) {
|
|
177
|
+
setPrintWarning(e instanceof Error ? e.message : "Bevestigen mislukt.");
|
|
178
|
+
}
|
|
179
|
+
finally {
|
|
180
|
+
setConfirming(false);
|
|
181
|
+
setPendingConfirm(null);
|
|
182
|
+
void ordersQuery.refetch();
|
|
183
|
+
}
|
|
184
|
+
}, [pendingConfirm, ordersQuery]);
|
|
185
|
+
const printNext = useCallback(() => {
|
|
186
|
+
const next = orders[0];
|
|
187
|
+
if (next)
|
|
188
|
+
void handlePrint(next);
|
|
189
|
+
}, [orders, handlePrint]);
|
|
190
|
+
if (!lang || pickerOpen) {
|
|
191
|
+
return _jsx(LanguagePicker, { onPick: pickLang });
|
|
192
|
+
}
|
|
193
|
+
return (_jsxs("div", { className: "min-h-screen bg-slate-50 text-slate-900", children: [_jsx("header", { className: "bg-white border-b sticky top-0 z-10", children: _jsxs("div", { className: "max-w-6xl mx-auto px-4 py-4 flex items-center gap-3 flex-wrap", children: [_jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("h1", { className: "text-2xl font-semibold leading-tight", children: t.title }), _jsxs("p", { className: "text-sm text-slate-500", children: [t.subtitle, " \u00B7 ", t.ofTotal(printedCount, orders.length)] })] }), _jsxs("button", { onClick: () => setPickerOpen(true), className: "h-14 px-5 rounded-xl bg-white border-2 border-slate-300 hover:bg-slate-100 font-semibold text-lg", children: ["\uD83C\uDF10 ", t.changeLang] }), _jsxs("button", { onClick: () => setShowChecklist(true), className: "h-14 px-5 rounded-xl bg-white border-2 border-slate-300 hover:bg-slate-100 font-semibold text-lg", children: ["\uD83D\uDCCB ", t.printChecklist] }), _jsxs("button", { onClick: () => navigate({ to: "/controle" }), className: "h-14 px-5 rounded-xl bg-sky-600 hover:bg-sky-700 text-white font-semibold text-lg shadow", children: ["\u2713 ", t.runControl] }), _jsxs("button", { onClick: printNext, disabled: printingId !== null || orders.length === 0, className: "h-14 px-6 rounded-xl bg-emerald-600 hover:bg-emerald-700 disabled:bg-slate-300 text-white font-bold text-lg shadow", children: ["\uD83D\uDDA8 ", t.printNext] })] }) }), _jsxs("main", { className: "max-w-6xl mx-auto px-4 py-6", children: [ordersQuery.isLoading && _jsx("p", { className: "text-xl text-center py-12", children: t.loading }), queryError && orders.length > 0 && (_jsxs("div", { className: "bg-amber-50 border-2 border-amber-300 text-amber-900 rounded-xl p-4 mb-4", children: [_jsx("strong", { children: "Let op:" }), " verversen is niet gelukt, daarom blijven de laatst bekende pakbonnen zichtbaar. ", queryError] })), printWarning && (_jsxs("div", { className: "bg-amber-50 border-2 border-amber-300 text-amber-900 rounded-xl p-4 mb-4", children: [_jsx("strong", { children: "Let op:" }), " ", printWarning] })), queryError && orders.length === 0 && (_jsxs("div", { className: "bg-rose-50 border-2 border-rose-300 text-rose-900 rounded-xl p-4 mb-4", children: [_jsx("strong", { children: "Picklijst niet bijgewerkt:" }), " ", queryError, " De orders zijn hierdoor niet verwijderd; controleer de Pickagenda of probeer opnieuw."] })), !ordersQuery.isLoading && orders.length === 0 && !queryError && (_jsx("p", { className: "text-2xl text-center py-16 text-slate-500", children: t.empty })), orders.length > 0 && (_jsx("div", { className: "bg-white rounded-2xl border border-slate-200 overflow-hidden shadow-sm", children: _jsxs("table", { className: "w-full text-lg", children: [_jsx("thead", { className: "bg-slate-100 text-slate-600 text-sm uppercase tracking-wide", children: _jsxs("tr", { children: [_jsx("th", { className: "text-left px-5 py-3", children: "Karnr" }), _jsx("th", { className: "text-left px-5 py-3", children: t.orderNumber }), _jsx("th", { className: "text-left px-5 py-3", children: "Kar" }), _jsx("th", { className: "text-left px-5 py-3", children: "Fusten" }), _jsx("th", { className: "text-left px-5 py-3", children: t.customer }), _jsx("th", { className: "text-left px-5 py-3", children: t.country }), _jsx("th", { className: "text-left px-5 py-3", children: t.deliveryDate }), _jsx("th", { className: "text-left px-5 py-3", children: t.status })] }) }), _jsx("tbody", { children: orders.map((o) => {
|
|
194
|
+
const isPrinting = printingId === o.id;
|
|
195
|
+
const badge = statusBadge(o.status, t, o.printedCount);
|
|
196
|
+
return (_jsxs("tr", { className: `border-t border-slate-100 transition select-none ${isPrinting ? "bg-emerald-50" : "hover:bg-slate-50"}`, style: { minHeight: 64 }, children: [_jsx("td", { className: "px-5 py-4 whitespace-nowrap cursor-pointer", onClick: () => void handlePrint(o), children: _jsx("span", { className: "inline-block px-3 py-1.5 rounded-md bg-black text-white font-black text-xl tracking-widest font-mono whitespace-nowrap", children: o.cartNumber ?? "—" }) }), _jsx("td", { className: "px-5 py-4 font-bold text-xl cursor-pointer", onClick: () => void handlePrint(o), children: o.orderNumber }), _jsxs("td", { className: "px-5 py-4 font-semibold", children: [o.cartIndex, "/", o.cartTotal] }), _jsx("td", { className: "px-5 py-4 font-semibold", children: o.cratesOnCart != null ? o.cratesOnCart : "—" }), _jsx("td", { className: "px-5 py-4", children: o.customerName }), _jsx("td", { className: "px-5 py-4", children: o.country ?? "—" }), _jsx("td", { className: "px-5 py-4", children: o.deliveryDate
|
|
197
|
+
? new Date(o.deliveryDate).toLocaleDateString(lang === "en" ? "en-GB" : lang)
|
|
198
|
+
: "—" }), _jsx("td", { className: "px-5 py-4", children: isPrinting ? (_jsx("span", { className: "inline-flex items-center gap-2 px-3 py-1 rounded-full bg-emerald-600 text-white font-semibold", children: t.printingNow })) : (_jsx("span", { className: `inline-flex items-center gap-2 px-3 py-1 rounded-full font-semibold ${badge.className}`, children: badge.label })) })] }, o.id));
|
|
199
|
+
}) })] }) })), _jsx("p", { className: "text-xs text-slate-400 mt-6 text-center", children: t.printDialogHint })] }), pendingConfirm && (_jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4", children: _jsxs("div", { className: "bg-white rounded-2xl shadow-2xl max-w-md w-full p-6", children: [_jsx("h2", { className: "text-2xl font-bold mb-2", children: t.confirmPrintedTitle }), _jsx("p", { className: "text-slate-600 mb-2", children: t.confirmPrintedBody }), _jsx("div", { className: "mb-4 inline-block px-4 py-2 rounded-md bg-black text-white font-black text-3xl tracking-widest font-mono", children: pendingConfirm.cartNumber ?? "—" }), _jsxs("p", { className: "text-slate-800 font-semibold mb-6", children: [pendingConfirm.orderNumber, " \u00B7 kar ", pendingConfirm.cartIndex, "/", pendingConfirm.cartTotal] }), _jsxs("div", { className: "flex flex-col gap-3", children: [_jsxs("button", { onClick: () => void confirmPrinted(true), disabled: confirming, className: "h-14 rounded-xl bg-emerald-600 hover:bg-emerald-700 disabled:bg-slate-300 text-white font-bold text-lg shadow", children: ["\u2713 ", t.confirmYes] }), _jsxs("button", { onClick: () => void confirmPrinted(false), disabled: confirming, className: "h-14 rounded-xl bg-white border-2 border-slate-300 hover:bg-slate-100 disabled:opacity-60 font-semibold text-lg", children: ["\u2715 ", t.confirmNo] })] })] }) })), showChecklist && (_jsx(ControleLijstPrint, { carts: orders.map((o) => ({
|
|
200
|
+
cartNumberId: null,
|
|
201
|
+
cartNumber: o.cartNumber,
|
|
202
|
+
orderId: o.orderId,
|
|
203
|
+
orderSource: o.orderSource,
|
|
204
|
+
cartIndex: o.cartIndex - 1,
|
|
205
|
+
orderNumber: o.orderNumber,
|
|
206
|
+
customerName: o.customerName,
|
|
207
|
+
carrierName: o.carrierName,
|
|
208
|
+
deliveryDate: o.deliveryDate,
|
|
209
|
+
pickupDate: o.pickupDate,
|
|
210
|
+
pickupTime: o.pickupTime,
|
|
211
|
+
country: o.country,
|
|
212
|
+
cratesOnCart: o.cratesOnCart,
|
|
213
|
+
pickDate: o.deliveryDate ?? "",
|
|
214
|
+
printedCount: o.printedCount,
|
|
215
|
+
status: o.status,
|
|
216
|
+
})), onClose: () => setShowChecklist(false) }))] }));
|
|
217
|
+
}
|
package/dist-lib/index.d.ts
CHANGED
|
@@ -9,5 +9,7 @@ export { PlanningAgendaPage } from "./components/afhandeling/PlanningAgendaPage"
|
|
|
9
9
|
export { ControlePage } from "./components/afhandeling/ControlePage";
|
|
10
10
|
export { AfhandelingSettingsCard } from "./components/settings/AfhandelingSettingsCard";
|
|
11
11
|
export { VervoerdersSettingsCard } from "./components/settings/VervoerdersSettingsCard";
|
|
12
|
+
export { PickstationPage } from "./components/pickstation/PickstationPage";
|
|
13
|
+
export { KioskControle } from "./components/pickstation/KioskControle";
|
|
12
14
|
export declare const floridayAfhandelingModule: FlowModule;
|
|
13
15
|
//# sourceMappingURL=index.d.ts.map
|
package/dist-lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAAE,eAAe,EAAE,MAAM,0CAA0C,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,6CAA6C,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,6CAA6C,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,+CAA+C,CAAC;AACxF,OAAO,EAAE,uBAAuB,EAAE,MAAM,+CAA+C,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAAE,eAAe,EAAE,MAAM,0CAA0C,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,6CAA6C,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,6CAA6C,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,+CAA+C,CAAC;AACxF,OAAO,EAAE,uBAAuB,EAAE,MAAM,+CAA+C,CAAC;AACxF,OAAO,EAAE,eAAe,EAAE,MAAM,0CAA0C,CAAC;AAC3E,OAAO,EAAE,aAAa,EAAE,MAAM,wCAAwC,CAAC;AAKvE,eAAO,MAAM,yBAAyB,EAAE,UAkBvC,CAAC"}
|
package/dist-lib/index.js
CHANGED
|
@@ -9,6 +9,8 @@ export { PlanningAgendaPage } from "./components/afhandeling/PlanningAgendaPage"
|
|
|
9
9
|
export { ControlePage } from "./components/afhandeling/ControlePage";
|
|
10
10
|
export { AfhandelingSettingsCard } from "./components/settings/AfhandelingSettingsCard";
|
|
11
11
|
export { VervoerdersSettingsCard } from "./components/settings/VervoerdersSettingsCard";
|
|
12
|
+
export { PickstationPage } from "./components/pickstation/PickstationPage";
|
|
13
|
+
export { KioskControle } from "./components/pickstation/KioskControle";
|
|
12
14
|
import { AfhandelingSettingsCard } from "./components/settings/AfhandelingSettingsCard";
|
|
13
15
|
import { VervoerdersSettingsCard } from "./components/settings/VervoerdersSettingsCard";
|
|
14
16
|
export const floridayAfhandelingModule = {
|