@flowselections/floriday-afhandeling-module 1.0.32 → 1.0.33

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 (63) hide show
  1. package/dist-lib/components/afhandeling/AutoProcessStatus.js +1 -1
  2. package/dist-lib/components/afhandeling/ControleLijstPrint.d.ts +6 -6
  3. package/dist-lib/components/afhandeling/ControleLijstPrint.d.ts.map +1 -1
  4. package/dist-lib/components/afhandeling/ControleLijstPrint.js +73 -48
  5. package/dist-lib/components/afhandeling/ControlePage.d.ts.map +1 -1
  6. package/dist-lib/components/afhandeling/ControlePage.js +25 -5
  7. package/dist-lib/components/afhandeling/DagDetailDialog.d.ts.map +1 -1
  8. package/dist-lib/components/afhandeling/DagDetailDialog.js +48 -24
  9. package/dist-lib/components/afhandeling/WeekAgendaView.d.ts.map +1 -1
  10. package/dist-lib/components/afhandeling/WeekAgendaView.js +16 -5
  11. package/dist-lib/components/afhandeling/WerkplekWaarschuwing.d.ts +10 -0
  12. package/dist-lib/components/afhandeling/WerkplekWaarschuwing.d.ts.map +1 -0
  13. package/dist-lib/components/afhandeling/WerkplekWaarschuwing.js +28 -0
  14. package/dist-lib/components/pickstation/PakbonTemplate.js +70 -70
  15. package/dist-lib/components/pickstation/printDocuments.d.ts +9 -2
  16. package/dist-lib/components/pickstation/printDocuments.d.ts.map +1 -1
  17. package/dist-lib/components/pickstation/printDocuments.js +8 -52
  18. package/dist-lib/components/settings/AfhandelingSettingsCard.d.ts.map +1 -1
  19. package/dist-lib/components/settings/AfhandelingSettingsCard.js +12 -1
  20. package/dist-lib/hooks/useEngineTasks.js +1 -1
  21. package/dist-lib/hooks/useOrderPlanning.d.ts.map +1 -1
  22. package/dist-lib/hooks/useOrderPlanning.js +32 -14
  23. package/dist-lib/integrations/supabase/auth-middleware.d.ts +18 -13
  24. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  25. package/dist-lib/integrations/supabase/client.server.d.ts +18 -13
  26. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  27. package/dist-lib/integrations/supabase/{client.d.ts → moduleClient.d.ts} +20 -14
  28. package/dist-lib/integrations/supabase/moduleClient.d.ts.map +1 -0
  29. package/dist-lib/integrations/supabase/moduleClient.js +38 -0
  30. package/dist-lib/integrations/supabase/types.d.ts +19 -14
  31. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  32. package/dist-lib/integrations/supabase/types.js +3 -0
  33. package/dist-lib/lib/afhandelingSettings.functions.d.ts +36 -26
  34. package/dist-lib/lib/afhandelingSettings.functions.d.ts.map +1 -1
  35. package/dist-lib/lib/carrierSignupMail.server.js +26 -26
  36. package/dist-lib/lib/carriers.functions.d.ts +90 -65
  37. package/dist-lib/lib/carriers.functions.d.ts.map +1 -1
  38. package/dist-lib/lib/cartAssignments.functions.d.ts +90 -65
  39. package/dist-lib/lib/cartAssignments.functions.d.ts.map +1 -1
  40. package/dist-lib/lib/cartControl.functions.d.ts +180 -130
  41. package/dist-lib/lib/cartControl.functions.d.ts.map +1 -1
  42. package/dist-lib/lib/deliveryNotePrint.d.ts +14 -6
  43. package/dist-lib/lib/deliveryNotePrint.d.ts.map +1 -1
  44. package/dist-lib/lib/deliveryNotePrint.js +61 -14
  45. package/dist-lib/lib/floriday.functions.d.ts +18 -13
  46. package/dist-lib/lib/floriday.functions.d.ts.map +1 -1
  47. package/dist-lib/lib/floridayAutoProcess.js +1 -1
  48. package/dist-lib/lib/logisticsEngine/capacity.js +1 -1
  49. package/dist-lib/lib/logisticsEngine/emitQueue.d.ts +20 -0
  50. package/dist-lib/lib/logisticsEngine/emitQueue.d.ts.map +1 -0
  51. package/dist-lib/lib/logisticsEngine/emitQueue.js +88 -0
  52. package/dist-lib/lib/logisticsEngine/engine.js +1 -1
  53. package/dist-lib/lib/logisticsEngine/reschedule.js +1 -1
  54. package/dist-lib/lib/logisticsEngine/staffPlanning.js +1 -1
  55. package/dist-lib/lib/printing.d.ts +56 -0
  56. package/dist-lib/lib/printing.d.ts.map +1 -0
  57. package/dist-lib/lib/printing.js +209 -0
  58. package/package.json +3 -1
  59. package/dist-lib/integrations/supabase/auth-attacher.d.ts +0 -2
  60. package/dist-lib/integrations/supabase/auth-attacher.d.ts.map +0 -1
  61. package/dist-lib/integrations/supabase/auth-attacher.js +0 -12
  62. package/dist-lib/integrations/supabase/client.d.ts.map +0 -1
  63. package/dist-lib/integrations/supabase/client.js +0 -13
@@ -3,7 +3,7 @@ import { useEffect, useState } from "react";
3
3
  import { Badge, Button, toast } from "@flowselections/core";
4
4
  import { CheckCircle2, Clock, Loader2, PlayCircle, RefreshCw, AlertTriangle } from "lucide-react";
5
5
  import { autoProcessSalesOrder, fetchAutoProcessStatuses, } from "../../lib/floridayAutoProcess";
6
- import { supabase } from "../../integrations/supabase/client";
6
+ import { supabase } from "../../integrations/supabase/moduleClient";
7
7
  const NL_DATE = new Intl.DateTimeFormat("nl-NL", { day: "2-digit", month: "short" });
8
8
  function formatWaiting(date) {
9
9
  const today = new Date();
@@ -1,9 +1,9 @@
1
1
  import type { ControlCart } from "../../lib/cartControl.functions";
2
2
  import { type PickstationLang } from "../../lib/pickstationI18n";
3
- /** Printbare controlelijst — één regel per kar met checkbox en opmerkingveld. */
4
- export declare function ControleLijstPrint({ carts, onClose, lang, }: {
5
- carts: ControlCart[];
6
- onClose: () => void;
7
- lang?: PickstationLang;
8
- }): import("react").ReactPortal | null;
3
+ /**
4
+ * Rendert de controlelijst als losstaande HTML-pagina (A4 portrait).
5
+ * De HTML gaat naar de printwachtrij van de Layout maker; er wordt
6
+ * geen browser-printdialoog meer gebruikt.
7
+ */
8
+ export declare function renderControleLijstHtml(carts: ControlCart[], lang?: PickstationLang): string;
9
9
  //# sourceMappingURL=ControleLijstPrint.d.ts.map
@@ -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;AAEnE,OAAO,EAGL,KAAK,eAAe,EACrB,MAAM,2BAA2B,CAAC;AAgCnC,iFAAiF;AACjF,wBAAgB,kBAAkB,CAAC,EACjC,KAAK,EACL,OAAO,EACP,IAAW,GACZ,EAAE;IACD,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,IAAI,CAAC,EAAE,eAAe,CAAC;CACxB,sCAoJA"}
1
+ {"version":3,"file":"ControleLijstPrint.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/ControleLijstPrint.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAEnE,OAAO,EAGL,KAAK,eAAe,EACrB,MAAM,2BAA2B,CAAC;AAuCnC;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,WAAW,EAAE,EACpB,IAAI,GAAE,eAAsB,GAC3B,MAAM,CAoGR"}
@@ -1,8 +1,3 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useEffect } from "react";
3
- import { createPortal } from "react-dom";
4
- import { Button } from "@flowselections/core";
5
- import { X, Printer } from "lucide-react";
6
1
  import { countryCode } from "../../lib/countries";
7
2
  import { getPrintDict, PICKSTATION_LOCALE, } from "../../lib/pickstationI18n";
8
3
  const DAY_ABBR_BY_LANG = {
@@ -28,19 +23,20 @@ function computeKlaarzet(cart, lang) {
28
23
  const time = `${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`;
29
24
  return { label: `${day} - ${time}`, sortKey: d.getTime() };
30
25
  }
31
- /** Printbare controlelijst — één regel per kar met checkbox en opmerkingveld. */
32
- export function ControleLijstPrint({ carts, onClose, lang = "nl", }) {
26
+ function esc(value) {
27
+ return String(value ?? "")
28
+ .replace(/&/g, "&")
29
+ .replace(/</g, "&lt;")
30
+ .replace(/>/g, "&gt;");
31
+ }
32
+ /**
33
+ * Rendert de controlelijst als losstaande HTML-pagina (A4 portrait).
34
+ * De HTML gaat naar de printwachtrij van de Layout maker; er wordt
35
+ * geen browser-printdialoog meer gebruikt.
36
+ */
37
+ export function renderControleLijstHtml(carts, lang = "nl") {
33
38
  const t = getPrintDict(lang);
34
39
  const locale = PICKSTATION_LOCALE[lang] ?? "nl-NL";
35
- useEffect(() => {
36
- const timer = setTimeout(() => window.print(), 300);
37
- const handleAfterPrint = () => onClose();
38
- window.addEventListener("afterprint", handleAfterPrint);
39
- return () => {
40
- clearTimeout(timer);
41
- window.removeEventListener("afterprint", handleAfterPrint);
42
- };
43
- }, [onClose]);
44
40
  const statusLabel = (status, printedCount) => {
45
41
  switch (status) {
46
42
  case "not_printed": return t.statusNotPrinted;
@@ -58,7 +54,7 @@ export function ControleLijstPrint({ carts, onClose, lang = "nl", }) {
58
54
  month: "long",
59
55
  year: "numeric",
60
56
  });
61
- const sortedCarts = carts
57
+ const sorted = carts
62
58
  .map((cart) => ({ cart, klaarzet: computeKlaarzet(cart, lang) }))
63
59
  .sort((a, b) => {
64
60
  if (a.klaarzet.sortKey !== b.klaarzet.sortKey) {
@@ -72,35 +68,64 @@ export function ControleLijstPrint({ carts, onClose, lang = "nl", }) {
72
68
  const bn = b.cart.cartNumber ?? "\uFFFF";
73
69
  return an.localeCompare(bn);
74
70
  });
75
- const content = (_jsxs("div", { id: "controle-print-root", className: "fixed inset-0 z-[100] bg-white overflow-auto", lang: lang, 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: t.checklistPrintView }), _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" }), t.reprint] }), _jsxs(Button, { size: "sm", variant: "outline", onClick: onClose, children: [_jsx(X, { className: "h-4 w-4 mr-2" }), t.close] })] })] }), _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: t.checklistTitle }), _jsx("div", { className: "text-sm", children: today })] }), _jsxs("div", { className: "text-sm mb-3", children: [t.totalCarts, ": ", _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: t.colKarnr }), _jsx("th", { className: "text-right py-1 pr-2", children: t.colFusten }), _jsx("th", { className: "text-left py-1 pr-2", children: t.colLand }), _jsx("th", { className: "text-left py-1 pr-2", children: t.colKlaarzetten }), _jsx("th", { className: "text-left py-1 pr-2", children: t.colStatus })] }) }), _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 font-semibold", children: countryCode(c.country) ?? c.country ?? "—" }), _jsx("td", { className: "py-2 pr-2 whitespace-nowrap font-semibold", children: klaarzet.label }), _jsx("td", { className: "py-2 pr-2 whitespace-nowrap", children: statusLabel(c.status, c.printedCount) })] }, `${c.orderSource}#${c.orderId}#${c.cartIndex}`))), sortedCarts.length === 0 && (_jsx("tr", { children: _jsx("td", { colSpan: 5, className: "py-6 text-center text-gray-500", children: t.noCartsToday }) }))] })] }), _jsxs("div", { className: "mt-6 text-[11px]", children: [_jsxs("div", { children: [t.checkedBy, ": ", _jsx("span", { className: "inline-block border-b border-black w-64" })] }), _jsxs("div", { className: "mt-2", children: [t.dateTime, ": ", _jsx("span", { className: "inline-block border-b border-black w-64" })] }), _jsxs("div", { className: "mt-2", children: [t.signature, ": ", _jsx("span", { className: "inline-block border-b border-black w-64" })] })] })] }), _jsx("style", { children: `
76
- @media print {
77
- @page { size: A4 portrait; margin: 12mm; }
78
- html, body { background: #fff !important; margin: 0 !important; padding: 0 !important; }
79
- body > *:not(#controle-print-portal) { display: none !important; }
80
- #controle-print-portal, #controle-print-root {
81
- position: static !important;
82
- overflow: visible !important;
83
- height: auto !important;
84
- width: auto !important;
85
- background: #fff !important;
86
- }
87
- .print-sheet {
88
- max-width: none !important;
89
- padding: 0 !important;
90
- margin: 0 !important;
91
- }
92
- table { page-break-inside: auto; }
93
- tr { page-break-inside: avoid; page-break-after: auto; }
94
- thead { display: table-header-group; }
95
- }
96
- ` })] }));
97
- if (typeof document === "undefined")
98
- return null;
99
- let portalNode = document.getElementById("controle-print-portal");
100
- if (!portalNode) {
101
- portalNode = document.createElement("div");
102
- portalNode.id = "controle-print-portal";
103
- document.body.appendChild(portalNode);
104
- }
105
- return createPortal(content, portalNode);
71
+ const rows = sorted.length
72
+ ? sorted
73
+ .map(({ cart: c, klaarzet }) => `
74
+ <tr>
75
+ <td class="mono bold">${esc(c.cartNumber ?? "—")}</td>
76
+ <td class="right">${esc(c.cratesOnCart ?? "—")}</td>
77
+ <td class="bold">${esc(countryCode(c.country) ?? c.country ?? "—")}</td>
78
+ <td class="bold">${esc(klaarzet.label)}</td>
79
+ <td>${esc(statusLabel(c.status, c.printedCount))}</td>
80
+ </tr>`)
81
+ .join("")
82
+ : `<tr><td colspan="5" class="empty">${esc(t.noCartsToday)}</td></tr>`;
83
+ return `<!DOCTYPE html>
84
+ <html lang="${esc(lang)}">
85
+ <head>
86
+ <meta charset="utf-8" />
87
+ <title>${esc(t.checklistTitle)}</title>
88
+ <style>
89
+ @page { size: A4 portrait; margin: 12mm; }
90
+ body { font-family: Arial, Helvetica, sans-serif; color: #000; margin: 0; }
91
+ h1 { font-size: 20px; margin: 0; }
92
+ .head { display: flex; align-items: baseline; justify-content: space-between;
93
+ border-bottom: 1px solid #000; padding-bottom: 6px; margin-bottom: 12px; }
94
+ .total { font-size: 12px; margin-bottom: 10px; }
95
+ table { width: 100%; border-collapse: collapse; font-size: 11px; }
96
+ th { text-align: left; border-bottom: 1px solid #000; padding: 3px 6px 3px 0; }
97
+ td { border-bottom: 1px solid #ccc; padding: 6px 6px 6px 0; white-space: nowrap; }
98
+ td.right, th.right { text-align: right; }
99
+ .mono { font-family: "Courier New", monospace; }
100
+ .bold { font-weight: 700; }
101
+ .empty { text-align: center; color: #666; padding: 24px 0; border: none; }
102
+ .sign { margin-top: 24px; font-size: 11px; }
103
+ .sign div { margin-top: 8px; }
104
+ .line { display: inline-block; border-bottom: 1px solid #000; width: 220px; }
105
+ tr { page-break-inside: avoid; }
106
+ thead { display: table-header-group; }
107
+ </style>
108
+ </head>
109
+ <body>
110
+ <div class="head"><h1>${esc(t.checklistTitle)}</h1><div>${esc(today)}</div></div>
111
+ <div class="total">${esc(t.totalCarts)}: <strong>${carts.length}</strong></div>
112
+ <table>
113
+ <thead>
114
+ <tr>
115
+ <th>${esc(t.colKarnr)}</th>
116
+ <th class="right">${esc(t.colFusten)}</th>
117
+ <th>${esc(t.colLand)}</th>
118
+ <th>${esc(t.colKlaarzetten)}</th>
119
+ <th>${esc(t.colStatus)}</th>
120
+ </tr>
121
+ </thead>
122
+ <tbody>${rows}</tbody>
123
+ </table>
124
+ <div class="sign">
125
+ <div>${esc(t.checkedBy)}: <span class="line"></span></div>
126
+ <div>${esc(t.dateTime)}: <span class="line"></span></div>
127
+ <div>${esc(t.signature)}: <span class="line"></span></div>
128
+ </div>
129
+ </body>
130
+ </html>`;
106
131
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ControlePage.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/ControlePage.tsx"],"names":[],"mappings":"AAkHA,wBAAgB,YAAY,CAAC,EAAE,MAAM,EAAE,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;CAAO,+BAgzBpE"}
1
+ {"version":3,"file":"ControlePage.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/ControlePage.tsx"],"names":[],"mappings":"AAoHA,wBAAgB,YAAY,CAAC,EAAE,MAAM,EAAE,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;CAAO,+BA2zBpE"}
@@ -1,13 +1,15 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useEffect, useMemo, useRef, useState } from "react";
3
- import { supabase } from "../../integrations/supabase/client";
3
+ import { supabase } from "../../integrations/supabase/moduleClient";
4
4
  import { Button, Card, Checkbox, Dialog, DialogDescription, DialogTitle, Input, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Textarea, Badge, toast, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@flowselections/core";
5
5
  import { Printer, CheckCircle2, AlertTriangle, History, ChevronLeft, ChevronRight } from "lucide-react";
6
6
  import { ResponsiveDialogBody, ResponsiveDialogContent, ResponsiveDialogFooter, ResponsiveDialogHeader, } from "../../components/ui/responsive-dialog";
7
7
  import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
8
8
  import { useServerFn } from "@tanstack/react-start";
9
9
  import { getTodaysControlCarts, getControlRescheduleProposals, submitControlSession, listControlHistory, getControlSessionDetail, getControlDraft, saveControlDraft, } from "../../lib/cartControl.functions";
10
- import { ControleLijstPrint } from "./ControleLijstPrint";
10
+ import { renderControleLijstHtml } from "./ControleLijstPrint";
11
+ import { printHtmlDocument, printFoutMelding } from "../../lib/printing";
12
+ import { WerkplekWaarschuwing, useWerkplek } from "./WerkplekWaarschuwing";
11
13
  function fmtDate(iso) {
12
14
  if (!iso)
13
15
  return "—";
@@ -60,7 +62,8 @@ export function ControlePage({ onExit } = {}) {
60
62
  const [notes, setNotes] = useState("");
61
63
  const [checked, setChecked] = useState(new Set());
62
64
  const [cartStates, setCartStates] = useState({});
63
- const [showPrint, setShowPrint] = useState(false);
65
+ const [printBezig, setPrintBezig] = useState(false);
66
+ const werkplek = useWerkplek();
64
67
  const [result, setResult] = useState(null);
65
68
  const [pendingProposal, setPendingProposal] = useState(null);
66
69
  const [confirmApproval, setConfirmApproval] = useState(null);
@@ -413,7 +416,24 @@ export function ControlePage({ onExit } = {}) {
413
416
  supabase.removeChannel(channel);
414
417
  };
415
418
  }, [qc]);
416
- return (_jsxs("main", { className: "p-6 space-y-6", children: [_jsxs("div", { className: "flex items-start justify-between gap-4", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-2xl font-semibold", children: "Controle karren" }), _jsxs("p", { className: "text-sm text-muted-foreground", children: ["Dagelijkse eindcontrole \u2014 vandaag ", new Date().toLocaleDateString("nl-NL")] })] }), _jsxs("div", { className: "flex gap-2", children: [_jsxs(Button, { variant: "outline", onClick: () => setShowPrint(true), children: [_jsx(Printer, { className: "h-4 w-4 mr-2" }), "Controlelijst afdrukken"] }), _jsxs(Button, { onClick: runControl, disabled: submitM.isPending || proposalM.isPending || carts.length === 0 || tab !== "controle", children: [_jsx(CheckCircle2, { className: "h-4 w-4 mr-2" }), proposalM.isPending ? "Controleren…" : "Controle uitvoeren"] })] })] }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { variant: tab === "controle" ? "default" : "outline", onClick: () => setTab("controle"), children: "Controle" }), _jsxs(Button, { variant: tab === "historie" ? "default" : "outline", onClick: () => setTab("historie"), children: [_jsx(History, { className: "h-4 w-4 mr-2" }), "Historie"] })] }), tab === "controle" && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex flex-wrap gap-3 items-center", children: [_jsxs(Card, { className: "p-3", children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Totaal karren vandaag" }), _jsx("div", { className: "text-2xl font-semibold", children: stats.total })] }), _jsxs(Card, { className: "p-3", children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Geprint" }), _jsx("div", { className: "text-2xl font-semibold", children: stats.printed })] }), _jsxs(Card, { className: "p-3", children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Aangevinkt" }), _jsx("div", { className: "text-2xl font-semibold", children: stats.checked })] })] }), _jsxs(Card, { className: "p-0 overflow-hidden", children: [_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { className: "w-12", children: _jsx(Checkbox, { checked: carts.length > 0 && carts.every((c) => checked.has(controlCartKey(c)))
419
+ return (_jsxs("main", { className: "p-6 space-y-6", children: [_jsx(WerkplekWaarschuwing, { werkplek: werkplek }), _jsxs("div", { className: "flex items-start justify-between gap-4", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-2xl font-semibold", children: "Controle karren" }), _jsxs("p", { className: "text-sm text-muted-foreground", children: ["Dagelijkse eindcontrole \u2014 vandaag ", new Date().toLocaleDateString("nl-NL")] })] }), _jsxs("div", { className: "flex gap-2", children: [_jsxs(Button, { variant: "outline", disabled: !werkplek || printBezig || carts.length === 0, onClick: async () => {
420
+ setPrintBezig(true);
421
+ try {
422
+ await printHtmlDocument({
423
+ documenttype: "controlelijst",
424
+ documentnaam: `CTRL-${new Date().toISOString().slice(0, 10)}`,
425
+ html: renderControleLijstHtml(carts, "nl"),
426
+ papierformaat: "A4",
427
+ });
428
+ toast.success("Controlelijst naar de printer gestuurd.");
429
+ }
430
+ catch (err) {
431
+ toast.error(printFoutMelding(err));
432
+ }
433
+ finally {
434
+ setPrintBezig(false);
435
+ }
436
+ }, children: [_jsx(Printer, { className: "h-4 w-4 mr-2" }), printBezig ? "Bezig met printen…" : "Controlelijst afdrukken"] }), _jsxs(Button, { onClick: runControl, disabled: submitM.isPending || proposalM.isPending || carts.length === 0 || tab !== "controle", children: [_jsx(CheckCircle2, { className: "h-4 w-4 mr-2" }), proposalM.isPending ? "Controleren…" : "Controle uitvoeren"] })] })] }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { variant: tab === "controle" ? "default" : "outline", onClick: () => setTab("controle"), children: "Controle" }), _jsxs(Button, { variant: tab === "historie" ? "default" : "outline", onClick: () => setTab("historie"), children: [_jsx(History, { className: "h-4 w-4 mr-2" }), "Historie"] })] }), tab === "controle" && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex flex-wrap gap-3 items-center", children: [_jsxs(Card, { className: "p-3", children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Totaal karren vandaag" }), _jsx("div", { className: "text-2xl font-semibold", children: stats.total })] }), _jsxs(Card, { className: "p-3", children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Geprint" }), _jsx("div", { className: "text-2xl font-semibold", children: stats.printed })] }), _jsxs(Card, { className: "p-3", children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Aangevinkt" }), _jsx("div", { className: "text-2xl font-semibold", children: stats.checked })] })] }), _jsxs(Card, { className: "p-0 overflow-hidden", children: [_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { className: "w-12", children: _jsx(Checkbox, { checked: carts.length > 0 && carts.every((c) => checked.has(controlCartKey(c)))
417
437
  ? true
418
438
  : carts.some((c) => checked.has(controlCartKey(c)))
419
439
  ? "indeterminate"
@@ -429,7 +449,7 @@ export function ControlePage({ onExit } = {}) {
429
449
  ? "Voortgang automatisch opgeslagen"
430
450
  : lastSavedRef.current && notes === lastSavedRef.current
431
451
  ? "Concept bewaard"
432
- : "" })] }), _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: () => {
452
+ : "" })] }), _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 ?? [] }), _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: () => {
433
453
  setResult(null);
434
454
  if (onExit)
435
455
  onExit();
@@ -1 +1 @@
1
- {"version":3,"file":"DagDetailDialog.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/DagDetailDialog.tsx"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAwCjE,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CACvC;AAID,wBAAgB,eAAe,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE,oBAAoB,sCA8E1F"}
1
+ {"version":3,"file":"DagDetailDialog.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/DagDetailDialog.tsx"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAwCjE,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CACvC;AAWD,wBAAgB,eAAe,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE,oBAAoB,sCAiF1F"}
@@ -1,4 +1,4 @@
1
- import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useState } from "react";
3
3
  import { useQuery, useQueryClient } from "@tanstack/react-query";
4
4
  import { useServerFn } from "@tanstack/react-start";
@@ -7,8 +7,8 @@ import { CalendarClock, ChevronDown, ChevronRight, Printer, Users } from "lucide
7
7
  import { ResponsiveDialogBody, ResponsiveDialogContent, ResponsiveDialogFooter, ResponsiveDialogHeader, } from "../../components/ui/responsive-dialog";
8
8
  import { isSameDay } from "../../lib/pickDate";
9
9
  import { getOrderLines, } from "../../lib/cartAssignments.functions";
10
- import { renderPakbonHtml } from "../pickstation/PakbonTemplate";
11
- import { printHtml } from "../pickstation/printDocuments";
10
+ import { printLayoutDocument, printFoutMelding } from "../../lib/printing";
11
+ import { WerkplekWaarschuwing, useWerkplek } from "./WerkplekWaarschuwing";
12
12
  import { printDeliveryNotesMerged } from "../../lib/deliveryNotePrint";
13
13
  import { AutoProcessStatus } from "./AutoProcessStatus";
14
14
  import { CapacityWarning } from "./CapacityWarning";
@@ -18,6 +18,11 @@ import { VerzetOrderDialog } from "./VerzetOrderDialog";
18
18
  import { setPickDay } from "../../lib/logisticsEngine/reschedule";
19
19
  import { clearDayStaffPlan, computeCapacity, getDayStaffPlan, getEngineStaffDefaults, setDayStaffPlan, toDateKey, } from "../../lib/logisticsEngine/staffPlanning";
20
20
  const NL_DATE = new Intl.DateTimeFormat("nl-NL", { dateStyle: "full" });
21
+ /** Eén melding per dagdialoog zolang de werkplek niet is ingesteld. */
22
+ function DagWerkplekBanner() {
23
+ const werkplek = useWerkplek();
24
+ return _jsx(WerkplekWaarschuwing, { werkplek: werkplek });
25
+ }
21
26
  export function DagDetailDialog({ open, date, planned, onOpenChange }) {
22
27
  if (!date)
23
28
  return null;
@@ -60,7 +65,7 @@ export function DagDetailDialog({ open, date, planned, onOpenChange }) {
60
65
  runningIdx += a.cartCount;
61
66
  }
62
67
  }
63
- return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(ResponsiveDialogContent, { size: "xl", children: [_jsxs(ResponsiveDialogHeader, { children: [_jsxs(DialogTitle, { children: ["Dagdetails \u2014 ", NL_DATE.format(date)] }), _jsxs(DialogDescription, { children: [ordersOnDay.length, " bestellingen \u00B7 ", cartRows.length, " karren"] })] }), _jsxs(ResponsiveDialogBody, { children: [_jsx(DayCapacityWarning, { date: date }), _jsx(BezettingCard, { date: date, cartsToday: cartRows.length }), _jsxs(Tabs, { defaultValue: "bestellingen", children: [_jsxs(TabsList, { children: [_jsxs(TabsTrigger, { value: "bestellingen", children: ["Bestellingen (", ordersOnDay.length, ")"] }), _jsxs(TabsTrigger, { value: "karren", children: ["Karren (", cartRows.length, ")"] })] }), _jsx(TabsContent, { value: "bestellingen", className: "mt-4", children: _jsx(BestellingenTab, { orders: ordersOnDay, date: date }) }), _jsx(TabsContent, { value: "karren", className: "mt-4", children: _jsx(KarrenTab, { carts: cartRows }) })] })] }), _jsx(ResponsiveDialogFooter, { children: _jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), children: "Sluiten" }) })] }) }));
68
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(ResponsiveDialogContent, { size: "xl", children: [_jsxs(ResponsiveDialogHeader, { children: [_jsxs(DialogTitle, { children: ["Dagdetails \u2014 ", NL_DATE.format(date)] }), _jsxs(DialogDescription, { children: [ordersOnDay.length, " bestellingen \u00B7 ", cartRows.length, " karren"] })] }), _jsxs(ResponsiveDialogBody, { children: [_jsx(DagWerkplekBanner, {}), _jsx(DayCapacityWarning, { date: date }), _jsx(BezettingCard, { date: date, cartsToday: cartRows.length }), _jsxs(Tabs, { defaultValue: "bestellingen", children: [_jsxs(TabsList, { children: [_jsxs(TabsTrigger, { value: "bestellingen", children: ["Bestellingen (", ordersOnDay.length, ")"] }), _jsxs(TabsTrigger, { value: "karren", children: ["Karren (", cartRows.length, ")"] })] }), _jsx(TabsContent, { value: "bestellingen", className: "mt-4", children: _jsx(BestellingenTab, { orders: ordersOnDay, date: date }) }), _jsx(TabsContent, { value: "karren", className: "mt-4", children: _jsx(KarrenTab, { carts: cartRows }) })] })] }), _jsx(ResponsiveDialogFooter, { children: _jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), children: "Sluiten" }) })] }) }));
64
69
  }
65
70
  function BezettingCard({ date, cartsToday }) {
66
71
  const queryClient = useQueryClient();
@@ -147,6 +152,7 @@ function OrderRow({ planned: p, cartsToday }) {
147
152
  const [open, setOpen] = useState(false);
148
153
  const [printing, setPrinting] = useState(false);
149
154
  const [verzetOpen, setVerzetOpen] = useState(false);
155
+ const werkplek = useWerkplek();
150
156
  const source = p.order.source;
151
157
  const fetchLines = useServerFn(getOrderLines);
152
158
  const linesQuery = useQuery({
@@ -180,22 +186,36 @@ function OrderRow({ planned: p, cartsToday }) {
180
186
  unit: null,
181
187
  }));
182
188
  let printed = 0;
189
+ let pakbonFailed = 0;
183
190
  for (let idx = 0; idx < cartTotal; idx++) {
184
- const html = renderPakbonHtml({
185
- id: `${p.order.id}#${idx}`,
186
- orderNumber: p.order.orderNumber,
187
- customerName: p.order.customerName,
188
- country: p.order.country ?? null,
189
- deliveryDate: p.order.deliveryDate,
190
- pickDate: pickDateIso,
191
- cartIndex: idx + 1,
192
- cartTotal,
193
- cratesOnCart: null,
194
- cartNumber: `${p.order.orderNumber}-${String(idx + 1).padStart(2, "0")}`,
195
- lines: pakbonLines,
196
- }, "nl");
197
- await printHtml(html);
198
- printed++;
191
+ const cartNumber = `${p.order.orderNumber}-${String(idx + 1).padStart(2, "0")}`;
192
+ try {
193
+ // Centrale pakbonlayout uit de Layout maker; de wachtrij kiest
194
+ // zelf de actieve layout en de bijbehorende printer.
195
+ await printLayoutDocument({
196
+ documenttype: "pakbon",
197
+ documentnaam: `PAK-${p.order.orderNumber}-${String(idx + 1).padStart(2, "0")}`,
198
+ payload: {
199
+ order: {
200
+ id: p.order.id,
201
+ orderNumber: p.order.orderNumber,
202
+ customerName: p.order.customerName,
203
+ country: p.order.country ?? null,
204
+ deliveryDate: p.order.deliveryDate,
205
+ pickDate: pickDateIso,
206
+ cartIndex: idx + 1,
207
+ cartTotal,
208
+ cartNumber,
209
+ lines: pakbonLines,
210
+ },
211
+ },
212
+ });
213
+ printed++;
214
+ }
215
+ catch (err) {
216
+ pakbonFailed++;
217
+ console.warn("[print-all] pakbon mislukt", cartNumber, err);
218
+ }
199
219
  }
200
220
  let deliveryOk = 0;
201
221
  let deliveryFailed = 0;
@@ -219,14 +239,18 @@ function OrderRow({ planned: p, cartsToday }) {
219
239
  const parts = [`${printed} pakbon${printed === 1 ? "" : "nen"}`];
220
240
  if (deliveryOk > 0)
221
241
  parts.push(`${deliveryOk} afleverbrief`);
222
- toast.success(`Documenten verstuurd naar printer — ${parts.join(", ")}.`);
242
+ if (printed > 0 || deliveryOk > 0) {
243
+ toast.success(`Documenten geprint — ${parts.join(", ")}.`);
244
+ }
245
+ if (pakbonFailed > 0) {
246
+ toast.error(`${pakbonFailed} pakbon kon niet worden geprint.`);
247
+ }
223
248
  if (deliveryFailed > 0) {
224
- toast.error(`${deliveryFailed} afleverbrief kon niet worden opgehaald.`);
249
+ toast.error(`${deliveryFailed} afleverbrief kon niet worden geprint.`);
225
250
  }
226
251
  }
227
252
  catch (err) {
228
- const msg = err instanceof Error ? err.message : "Printen mislukt";
229
- toast.error(msg);
253
+ toast.error(printFoutMelding(err));
230
254
  console.error("[print-all]", err);
231
255
  }
232
256
  finally {
@@ -243,7 +267,7 @@ function OrderRow({ planned: p, cartsToday }) {
243
267
  const src = p.order.source === "floriday" ? "floriday" : "manual";
244
268
  const soId = p.order.salesOrderId ?? (src === "floriday" ? p.order.id : null);
245
269
  return soId ? (_jsx(AutoProcessStatus, { salesOrderId: soId })) : null;
246
- })(), _jsxs(Button, { size: "sm", variant: "outline", onClick: handlePrintAll, disabled: printing, title: "Alle pakbonnen en afleverbrieven van deze bestelling afdrukken", children: [_jsx(Printer, { className: "mr-1 h-3.5 w-3.5" }), printing ? "Bezig…" : "Alle documenten afdrukken"] }), _jsxs(Button, { size: "sm", variant: "outline", onClick: (e) => {
270
+ })(), _jsxs(Button, { size: "sm", variant: "outline", onClick: handlePrintAll, disabled: printing || !werkplek, title: "Alle pakbonnen en afleverbrieven van deze bestelling afdrukken", children: [_jsx(Printer, { className: "mr-1 h-3.5 w-3.5" }), printing ? "Bezig…" : "Alle documenten afdrukken"] }), _jsxs(Button, { size: "sm", variant: "outline", onClick: (e) => {
247
271
  e.stopPropagation();
248
272
  setVerzetOpen(true);
249
273
  }, title: "Ophaaldag of ophaaltijd van deze order wijzigen", children: [_jsx(CalendarClock, { className: "mr-1 h-3.5 w-3.5" }), "Verzetten"] })] })] }), _jsx(VerzetOrderDialog, { open: verzetOpen, orderRef: p.order.orderNumber, carrierTarget: {
@@ -1 +1 @@
1
- {"version":3,"file":"WeekAgendaView.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/WeekAgendaView.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAElE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAUjE,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,QAAQ,GAAG,oBAAoB,GAAG,MAAM,CAAC;AAEnF,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,IAAI,EAAE,cAAc,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC9C,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+DAA+D;IAC/D,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,yEAAyE;IACzE,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,KAAK,IAAI,CAAC;IACxD,4EAA4E;IAC5E,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AA+DD,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,IAAI,EACJ,SAAS,EACT,YAAY,EACZ,UAAU,EACV,KAAK,EACL,gBAAgB,EAChB,YAAY,EACZ,UAAU,GACX,EAAE,mBAAmB,+BAyJrB"}
1
+ {"version":3,"file":"WeekAgendaView.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/WeekAgendaView.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAIlE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAUjE,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,QAAQ,GAAG,oBAAoB,GAAG,MAAM,CAAC;AAEnF,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,IAAI,EAAE,cAAc,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC9C,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+DAA+D;IAC/D,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,yEAAyE;IACzE,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,KAAK,IAAI,CAAC;IACxD,4EAA4E;IAC5E,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AA+DD,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,IAAI,EACJ,SAAS,EACT,YAAY,EACZ,UAAU,EACV,KAAK,EACL,gBAAgB,EAChB,YAAY,EACZ,UAAU,GACX,EAAE,mBAAmB,+BA0JrB"}
@@ -2,6 +2,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useMemo, useRef, useState } from "react";
3
3
  import { Button, Badge, toast } from "@flowselections/core";
4
4
  import { ChevronLeft, ChevronRight, Package, Truck, Sprout, Clock, CheckCircle2, Scale, Printer } from "lucide-react";
5
+ import { printFoutMelding } from "../../lib/printing";
6
+ import { WerkplekWaarschuwing, useWerkplek } from "./WerkplekWaarschuwing";
5
7
  import { isSameDay, startOfWeek, addDays } from "../../lib/pickDate";
6
8
  import { printDeliveryNote, printDeliveryNotesMerged, } from "../../lib/deliveryNotePrint";
7
9
  import { useCapacityCheck } from "../../hooks/useCapacityCheck";
@@ -144,12 +146,17 @@ export function WeekAgendaView({ planned, mode, isLoading, onOrderClick, onDayCl
144
146
  d.setHours(0, 0, 0, 0);
145
147
  setCursor(startOfWeek(d));
146
148
  };
147
- return (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsx("h2", { className: "text-xl font-semibold tracking-tight", children: title ?? headerLabel }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: goPrev, children: [_jsx(ChevronLeft, { className: "mr-1 h-4 w-4" }), " Vorige week"] }), _jsx(Button, { variant: "outline", size: "sm", onClick: goToday, children: "Deze week" }), _jsxs(Button, { variant: "outline", size: "sm", onClick: goNext, children: ["Volgende week ", _jsx(ChevronRight, { className: "ml-1 h-4 w-4" })] })] })] }), _jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4", children: [_jsx(SummaryCard, { label: MODE_LABELS[mode].summary, value: totals.leveringen, tone: "default" }), _jsx(SummaryCard, { label: "Verzendklaar", value: totals.verzendklaar, tone: "success" }), _jsx(SummaryCard, { label: "In voorbereiding", value: totals.voorbereiding, tone: "muted" }), _jsx(SummaryCard, { label: "Totaal stuks", value: totals.totaalStuks, tone: "default" })] }), _jsx("div", { className: "max-h-[520px] space-y-2 overflow-y-auto rounded-lg border bg-muted/20 p-2", children: isLoading ? (_jsx("div", { className: "rounded-lg border bg-card p-10 text-center text-sm text-muted-foreground", children: "Laden\u2026" })) : (days.map((day) => (_jsx(DayRow, { day: day, onOrderClick: onOrderClick, onDayClick: onDayClick, showPrintButtons: showPrintButtons, capacity: mode === "delivery" ? undefined : capacity.byDate.get(toDayKey(day.date)) }, day.date.toISOString())))) })] }));
149
+ return (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsx("h2", { className: "text-xl font-semibold tracking-tight", children: title ?? headerLabel }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: goPrev, children: [_jsx(ChevronLeft, { className: "mr-1 h-4 w-4" }), " Vorige week"] }), _jsx(Button, { variant: "outline", size: "sm", onClick: goToday, children: "Deze week" }), _jsxs(Button, { variant: "outline", size: "sm", onClick: goNext, children: ["Volgende week ", _jsx(ChevronRight, { className: "ml-1 h-4 w-4" })] })] })] }), _jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4", children: [_jsx(SummaryCard, { label: MODE_LABELS[mode].summary, value: totals.leveringen, tone: "default" }), _jsx(SummaryCard, { label: "Verzendklaar", value: totals.verzendklaar, tone: "success" }), _jsx(SummaryCard, { label: "In voorbereiding", value: totals.voorbereiding, tone: "muted" }), _jsx(SummaryCard, { label: "Totaal stuks", value: totals.totaalStuks, tone: "default" })] }), _jsxs("div", { className: "max-h-[520px] space-y-2 overflow-y-auto rounded-lg border bg-muted/20 p-2", children: [showPrintButtons && _jsx(AgendaWerkplekBanner, {}), isLoading ? (_jsx("div", { className: "rounded-lg border bg-card p-10 text-center text-sm text-muted-foreground", children: "Laden\u2026" })) : (days.map((day) => (_jsx(DayRow, { day: day, onOrderClick: onOrderClick, onDayClick: onDayClick, showPrintButtons: showPrintButtons, capacity: mode === "delivery" ? undefined : capacity.byDate.get(toDayKey(day.date)) }, day.date.toISOString()))))] })] }));
148
150
  }
149
151
  function SummaryCard({ label, value, tone, }) {
150
152
  const valueClass = tone === "success" ? "text-green-600" : "text-foreground";
151
153
  return (_jsxs("div", { className: "rounded-lg border bg-card p-4", children: [_jsx("div", { className: `text-3xl font-bold ${valueClass}`, children: value }), _jsx("div", { className: "mt-1 text-sm text-muted-foreground", children: label })] }));
152
154
  }
155
+ /** Eén melding bovenaan de agenda zolang de werkplek ontbreekt. */
156
+ function AgendaWerkplekBanner() {
157
+ const werkplek = useWerkplek();
158
+ return _jsx(WerkplekWaarschuwing, { werkplek: werkplek });
159
+ }
153
160
  function DayRow({ day, onOrderClick, onDayClick, showPrintButtons, capacity, }) {
154
161
  const weekdayIdx = (day.date.getDay() + 6) % 7;
155
162
  const today = new Date();
@@ -160,6 +167,7 @@ function DayRow({ day, onOrderClick, onDayClick, showPrintButtons, capacity, })
160
167
  const printableItems = day.entries
161
168
  .filter((e) => e.order.source === "floriday" && e.order.salesOrderId)
162
169
  .map((e) => ({ id: e.order.id, salesOrderId: e.order.salesOrderId ?? undefined }));
170
+ const werkplek = useWerkplek();
163
171
  const handlePrintDay = async () => {
164
172
  if (printingDay || printableItems.length === 0)
165
173
  return;
@@ -174,7 +182,7 @@ function DayRow({ day, onOrderClick, onDayClick, showPrintButtons, capacity, })
174
182
  }
175
183
  }
176
184
  catch (err) {
177
- toast.error(err instanceof Error ? err.message : "Printen mislukt");
185
+ toast.error(printFoutMelding(err));
178
186
  }
179
187
  finally {
180
188
  setPrintingDay(false);
@@ -185,16 +193,19 @@ function DayRow({ day, onOrderClick, onDayClick, showPrintButtons, capacity, })
185
193
  return;
186
194
  setPrintingOrderId(order.id);
187
195
  try {
188
- await printDeliveryNote(order.salesOrderId);
196
+ await printDeliveryNote(order.salesOrderId, {
197
+ documentnaam: `AFL-${order.orderNumber}`,
198
+ });
199
+ toast.success(`Afleverbrief ${order.orderNumber} naar de printer gestuurd.`);
189
200
  }
190
201
  catch (err) {
191
- toast.error(err instanceof Error ? err.message : "Afleverbrief printen mislukt");
202
+ toast.error(printFoutMelding(err));
192
203
  }
193
204
  finally {
194
205
  setPrintingOrderId(null);
195
206
  }
196
207
  };
197
- return (_jsxs("div", { className: `rounded-lg border bg-card ${isToday ? "ring-1 ring-primary" : ""}`, children: [_jsxs("div", { className: "flex flex-wrap items-center gap-6 px-4 py-4 sm:px-6", children: [_jsxs("div", { className: "w-20 shrink-0", children: [_jsx("div", { className: "text-xs font-medium tracking-wider text-muted-foreground", children: WEEKDAY_LABELS[weekdayIdx] }), _jsx("div", { className: "mt-1 text-3xl font-bold leading-none", children: day.date.getDate() }), _jsxs("div", { className: "text-sm text-muted-foreground", children: [MONTHS_NL_SHORT[day.date.getMonth()], "."] })] }), _jsxs("div", { className: "flex flex-1 flex-wrap items-center gap-6", children: [_jsx(Stat, { icon: _jsx(Package, { className: "h-4 w-4 text-slate-500" }), bg: "bg-slate-100", value: day.bestellingen, label: "bestellingen" }), _jsx(Stat, { icon: _jsx(Truck, { className: "h-4 w-4 text-amber-600" }), bg: "bg-amber-100", value: day.karren, label: "karren" }), _jsx(Stat, { icon: _jsx(Sprout, { className: "h-4 w-4 text-green-600" }), bg: "bg-green-100", value: day.planten, label: "planten" })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [day.movedEarlierOrderIds.size > 0 && (_jsxs(Badge, { className: "bg-purple-500/10 text-purple-700 hover:bg-purple-500/10", variant: "secondary", title: "Aantal orders dat vervroegd is ingepland door Pickverdeling.", children: [_jsx(Scale, { className: "mr-1 h-3.5 w-3.5" }), day.movedEarlierOrderIds.size, " vervroegd"] })), day.verzendklaar > 0 && (_jsxs(Badge, { className: "bg-green-500/15 text-green-700 hover:bg-green-500/15", variant: "secondary", children: [_jsx(CheckCircle2, { className: "mr-1 h-3.5 w-3.5" }), day.verzendklaar, " verzendklaar"] })), day.voorbereiding > 0 && (_jsxs(Badge, { className: "bg-blue-500/10 text-blue-700 hover:bg-blue-500/10", variant: "secondary", children: [_jsx(Clock, { className: "mr-1 h-3.5 w-3.5" }), day.voorbereiding, " in voorbereiding"] })), showPrintButtons && printableItems.length > 0 && (_jsxs(Button, { size: "sm", variant: "outline", onClick: handlePrintDay, disabled: printingDay, children: [_jsx(Printer, { className: "mr-1 h-4 w-4" }), printingDay ? "Printen…" : `Print afleverbrieven (${printableItems.length})`] })), onDayClick && day.bestellingen > 0 && (_jsx(Button, { size: "sm", variant: "outline", onClick: () => onDayClick(day.date), children: "Dagdetails" }))] })] }), _jsx(CapacityWarning, { day: capacity, className: "mx-4 mb-4 sm:mx-6" })] }));
208
+ return (_jsxs("div", { className: `rounded-lg border bg-card ${isToday ? "ring-1 ring-primary" : ""}`, children: [_jsxs("div", { className: "flex flex-wrap items-center gap-6 px-4 py-4 sm:px-6", children: [_jsxs("div", { className: "w-20 shrink-0", children: [_jsx("div", { className: "text-xs font-medium tracking-wider text-muted-foreground", children: WEEKDAY_LABELS[weekdayIdx] }), _jsx("div", { className: "mt-1 text-3xl font-bold leading-none", children: day.date.getDate() }), _jsxs("div", { className: "text-sm text-muted-foreground", children: [MONTHS_NL_SHORT[day.date.getMonth()], "."] })] }), _jsxs("div", { className: "flex flex-1 flex-wrap items-center gap-6", children: [_jsx(Stat, { icon: _jsx(Package, { className: "h-4 w-4 text-slate-500" }), bg: "bg-slate-100", value: day.bestellingen, label: "bestellingen" }), _jsx(Stat, { icon: _jsx(Truck, { className: "h-4 w-4 text-amber-600" }), bg: "bg-amber-100", value: day.karren, label: "karren" }), _jsx(Stat, { icon: _jsx(Sprout, { className: "h-4 w-4 text-green-600" }), bg: "bg-green-100", value: day.planten, label: "planten" })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [day.movedEarlierOrderIds.size > 0 && (_jsxs(Badge, { className: "bg-purple-500/10 text-purple-700 hover:bg-purple-500/10", variant: "secondary", title: "Aantal orders dat vervroegd is ingepland door Pickverdeling.", children: [_jsx(Scale, { className: "mr-1 h-3.5 w-3.5" }), day.movedEarlierOrderIds.size, " vervroegd"] })), day.verzendklaar > 0 && (_jsxs(Badge, { className: "bg-green-500/15 text-green-700 hover:bg-green-500/15", variant: "secondary", children: [_jsx(CheckCircle2, { className: "mr-1 h-3.5 w-3.5" }), day.verzendklaar, " verzendklaar"] })), day.voorbereiding > 0 && (_jsxs(Badge, { className: "bg-blue-500/10 text-blue-700 hover:bg-blue-500/10", variant: "secondary", children: [_jsx(Clock, { className: "mr-1 h-3.5 w-3.5" }), day.voorbereiding, " in voorbereiding"] })), showPrintButtons && printableItems.length > 0 && (_jsxs(Button, { size: "sm", variant: "outline", onClick: handlePrintDay, disabled: printingDay || !werkplek, children: [_jsx(Printer, { className: "mr-1 h-4 w-4" }), printingDay ? "Printen…" : `Print afleverbrieven (${printableItems.length})`] })), onDayClick && day.bestellingen > 0 && (_jsx(Button, { size: "sm", variant: "outline", onClick: () => onDayClick(day.date), children: "Dagdetails" }))] })] }), _jsx(CapacityWarning, { day: capacity, className: "mx-4 mb-4 sm:mx-6" })] }));
198
209
  }
199
210
  function Stat({ icon, bg, value, label, }) {
200
211
  return (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: `grid h-8 w-8 place-items-center rounded-full ${bg}`, children: icon }), _jsxs("div", { children: [_jsx("div", { className: "text-lg font-bold leading-none", children: value }), _jsx("div", { className: "text-xs text-muted-foreground", children: label })] })] }));
@@ -0,0 +1,10 @@
1
+ /** Leest de ingestelde werkplek client-side (SSR-veilig). */
2
+ export declare function useWerkplek(): string | null;
3
+ /**
4
+ * Eén melding bovenaan een printscherm zolang er geen werkplek is ingesteld.
5
+ * Printknoppen zijn dan uitgeschakeld, zodat er geen fout per printactie komt.
6
+ */
7
+ export declare function WerkplekWaarschuwing({ werkplek }: {
8
+ werkplek: string | null;
9
+ }): import("react").JSX.Element | null;
10
+ //# sourceMappingURL=WerkplekWaarschuwing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WerkplekWaarschuwing.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/WerkplekWaarschuwing.tsx"],"names":[],"mappings":"AAIA,6DAA6D;AAC7D,wBAAgB,WAAW,IAAI,MAAM,GAAG,IAAI,CAa3C;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,sCAW7E"}
@@ -0,0 +1,28 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
3
+ import { AlertTriangle } from "lucide-react";
4
+ import { getWerkplek } from "../../lib/printing";
5
+ /** Leest de ingestelde werkplek client-side (SSR-veilig). */
6
+ export function useWerkplek() {
7
+ const [werkplek, setWerkplekState] = useState(null);
8
+ useEffect(() => {
9
+ setWerkplekState(getWerkplek());
10
+ const onStorage = () => setWerkplekState(getWerkplek());
11
+ window.addEventListener("storage", onStorage);
12
+ window.addEventListener("focus", onStorage);
13
+ return () => {
14
+ window.removeEventListener("storage", onStorage);
15
+ window.removeEventListener("focus", onStorage);
16
+ };
17
+ }, []);
18
+ return werkplek;
19
+ }
20
+ /**
21
+ * Eén melding bovenaan een printscherm zolang er geen werkplek is ingesteld.
22
+ * Printknoppen zijn dan uitgeschakeld, zodat er geen fout per printactie komt.
23
+ */
24
+ export function WerkplekWaarschuwing({ werkplek }) {
25
+ if (werkplek)
26
+ return null;
27
+ return (_jsxs("div", { className: "flex items-start gap-2 rounded-lg border border-amber-500/40 bg-amber-500/10 px-3 py-2 text-sm", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0 text-amber-600" }), _jsxs("div", { children: [_jsx("span", { className: "font-medium", children: "Geen werkplek ingesteld." }), " Printen is uitgeschakeld. Stel de werkplek (pickstation- of computernaam) in bij ", _jsx("strong", { children: "Instellingen" }), "."] })] }));
28
+ }