@flowselections/pick-schermen-kwekers 1.0.41 → 1.0.46

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 (56) hide show
  1. package/dist-lib/components/PickstationsPage.js +1 -1
  2. package/dist-lib/components/pick/PickScherm.d.ts.map +1 -1
  3. package/dist-lib/components/pick/PickScherm.js +194 -37
  4. package/dist-lib/components/pick/QueueTable.d.ts +10 -1
  5. package/dist-lib/components/pick/QueueTable.d.ts.map +1 -1
  6. package/dist-lib/components/pick/QueueTable.js +57 -16
  7. package/dist-lib/components/pick/ReferentiedataBanner.js +1 -1
  8. package/dist-lib/index.d.ts +1 -0
  9. package/dist-lib/index.d.ts.map +1 -1
  10. package/dist-lib/index.js +1 -0
  11. package/dist-lib/integrations/supabase/previewAuthStorage.d.ts.map +1 -1
  12. package/dist-lib/integrations/supabase/previewAuthStorage.js +22 -4
  13. package/dist-lib/integrations/supabase/types.d.ts +725 -0
  14. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  15. package/dist-lib/lib/__tests__/pick142.test.d.ts +2 -0
  16. package/dist-lib/lib/__tests__/pick142.test.d.ts.map +1 -0
  17. package/dist-lib/lib/__tests__/pick142.test.js +80 -0
  18. package/dist-lib/lib/__tests__/pick143.test.d.ts +2 -0
  19. package/dist-lib/lib/__tests__/pick143.test.d.ts.map +1 -0
  20. package/dist-lib/lib/__tests__/pick143.test.js +61 -0
  21. package/dist-lib/lib/__tests__/pick144.test.d.ts +2 -0
  22. package/dist-lib/lib/__tests__/pick144.test.d.ts.map +1 -0
  23. package/dist-lib/lib/__tests__/pick144.test.js +23 -0
  24. package/dist-lib/lib/__tests__/pick145.test.d.ts +2 -0
  25. package/dist-lib/lib/__tests__/pick145.test.d.ts.map +1 -0
  26. package/dist-lib/lib/__tests__/pick145.test.js +35 -0
  27. package/dist-lib/lib/pick142.d.ts +58 -0
  28. package/dist-lib/lib/pick142.d.ts.map +1 -0
  29. package/dist-lib/lib/pick142.js +235 -0
  30. package/dist-lib/lib/pick143.d.ts +31 -0
  31. package/dist-lib/lib/pick143.d.ts.map +1 -0
  32. package/dist-lib/lib/pick143.js +93 -0
  33. package/dist-lib/lib/pick144.d.ts +17 -0
  34. package/dist-lib/lib/pick144.d.ts.map +1 -0
  35. package/dist-lib/lib/pick144.js +53 -0
  36. package/dist-lib/lib/pick145.d.ts +23 -0
  37. package/dist-lib/lib/pick145.d.ts.map +1 -0
  38. package/dist-lib/lib/pick145.js +88 -0
  39. package/dist-lib/lib/pickApi.d.ts +17 -0
  40. package/dist-lib/lib/pickApi.d.ts.map +1 -1
  41. package/dist-lib/lib/pickApi.js +37 -6
  42. package/dist-lib/lib/pickCartResults.d.ts +1 -1
  43. package/dist-lib/lib/pickCartResults.d.ts.map +1 -1
  44. package/dist-lib/lib/pickCartResults.js +2 -2
  45. package/dist-lib/lib/pickI18n.js +6 -6
  46. package/dist-lib/lib/pickPrintJobText.js +2 -2
  47. package/dist-lib/lib/pickRowState.d.ts +1 -1
  48. package/dist-lib/lib/pickRowState.d.ts.map +1 -1
  49. package/dist-lib/lib/pickRowState.js +6 -2
  50. package/dist-lib/lib/pickTypes.d.ts +31 -1
  51. package/dist-lib/lib/pickTypes.d.ts.map +1 -1
  52. package/dist-lib/lib/pickTypes.js +42 -8
  53. package/dist-lib/lib/pickUiText.d.ts.map +1 -1
  54. package/dist-lib/lib/pickUiText.js +10 -0
  55. package/dist-lib/lib/shippingNoteStatus.js +2 -2
  56. package/package.json +1 -1
@@ -57,7 +57,7 @@ export function PickstationsPage() {
57
57
  toast.error(url);
58
58
  }
59
59
  };
60
- return (_jsxs("main", { className: "space-y-6 p-6", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-2xl font-bold text-foreground", children: "Pickstations" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Beheer je pickstations en hun openbare link. Alle logica komt uit de Logistiek Engine." })] }), _jsxs(Button, { onClick: () => setDraft({ ...emptyDraft }), children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), " Nieuw pickstation"] })] }), _jsx(ReferentiedataBanner, {}), isLoading ? (_jsxs("div", { className: "flex items-center gap-2 text-muted-foreground", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), " Laden..."] })) : safeStations.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Nog geen pickstations." })) : (_jsx("div", { className: "grid gap-4 sm:grid-cols-2 xl:grid-cols-3", children: safeStations.map((station) => (_jsx(Card, { className: "border-2", children: _jsxs(CardContent, { className: "space-y-4 p-6", children: [_jsxs("div", { className: "flex items-start justify-between gap-2", children: [_jsxs("div", { children: [_jsx("h2", { className: "text-lg font-semibold text-foreground", children: station.name }), _jsx("p", { className: "text-sm text-muted-foreground", children: station.description })] }), _jsx("span", { className: "rounded-full px-2 py-1 text-xs font-medium " +
60
+ return (_jsxs("main", { className: "space-y-6 p-6", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-2xl font-bold text-foreground", children: "Pickstations" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Beheer je pickstations en de link die je met de medewerkers deelt." })] }), _jsxs(Button, { onClick: () => setDraft({ ...emptyDraft }), children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), " Nieuw pickstation"] })] }), _jsx(ReferentiedataBanner, {}), isLoading ? (_jsxs("div", { className: "flex items-center gap-2 text-muted-foreground", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), " Laden..."] })) : safeStations.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Nog geen pickstations." })) : (_jsx("div", { className: "grid gap-4 sm:grid-cols-2 xl:grid-cols-3", children: safeStations.map((station) => (_jsx(Card, { className: "border-2", children: _jsxs(CardContent, { className: "space-y-4 p-6", children: [_jsxs("div", { className: "flex items-start justify-between gap-2", children: [_jsxs("div", { children: [_jsx("h2", { className: "text-lg font-semibold text-foreground", children: station.name }), _jsx("p", { className: "text-sm text-muted-foreground", children: station.description })] }), _jsx("span", { className: "rounded-full px-2 py-1 text-xs font-medium " +
61
61
  (station.active
62
62
  ? "bg-primary/10 text-primary"
63
63
  : "bg-muted text-muted-foreground"), children: station.active ? "Actief" : "Inactief" })] }), _jsx("code", { className: "block truncate rounded bg-muted px-2 py-1 text-xs text-muted-foreground", children: publicUrl(station.slug) }), _jsx(StationPrintplek, { station: station }), _jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsx(Button, { className: "flex-1", asChild: true, children: _jsxs("a", { href: publicUrl(station.slug), target: "_blank", rel: "noreferrer", children: [_jsx(ExternalLink, { className: "mr-2 h-4 w-4" }), " Open pickstation"] }) }), _jsxs(Button, { variant: "outline", onClick: () => copyLink(station), children: [_jsx(Copy, { className: "mr-2 h-4 w-4" }), " Link kopi\u00EBren"] })] }), _jsxs("div", { className: "flex gap-2", children: [_jsxs(Button, { variant: "ghost", size: "sm", onClick: () => setDraft({
@@ -1 +1 @@
1
- {"version":3,"file":"PickScherm.d.ts","sourceRoot":"","sources":["../../../src/components/pick/PickScherm.tsx"],"names":[],"mappings":"AAqCA,OAAO,EAWL,KAAK,WAAW,EACjB,MAAM,qBAAqB,CAAC;AAuD7B,UAAU,KAAK;IACb,OAAO,EAAE,WAAW,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;CACrB;AAwBD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,KAAK,+BAm9DpD"}
1
+ {"version":3,"file":"PickScherm.d.ts","sourceRoot":"","sources":["../../../src/components/pick/PickScherm.tsx"],"names":[],"mappings":"AA0DA,OAAO,EAaL,KAAK,WAAW,EACjB,MAAM,qBAAqB,CAAC;AAuD7B,UAAU,KAAK;IACb,OAAO,EAAE,WAAW,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;CACrB;AAwBD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,KAAK,+BAypEpD"}
@@ -1,21 +1,25 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
3
3
  import { useQuery } from "@tanstack/react-query";
4
4
  import { Badge, Button, Card, CardContent, Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, toast, } from "@flowselections/core";
5
5
  import { AlertTriangle, ArrowLeft, CheckCircle2, ChevronLeft, ChevronRight, FileDown, Globe, Loader2, Printer, RefreshCw, Search, WifiOff, Zap, } from "lucide-react";
6
6
  import { LanguageTiles } from "./LanguageTiles";
7
7
  import { QueueTable } from "./QueueTable";
8
+ import { isRowPrintable, pickDayOf, pickDayShortLabel, printBlockMessage, rowWaitOf, setKnownSiteZone } from "../../lib/pick143";
9
+ import { isPickWindowNotStarted } from "../../lib/pick144";
10
+ import { isForcedFor, lateSignupOf, releaseInfoOf, releaseMessage } from "../../lib/pick145";
8
11
  import { PreviewDialog } from "./PreviewDialog";
9
12
  import { ShippingNoteScan } from "./ShippingNoteScan";
10
13
  import { getSessionId, pickApi } from "../../lib/pickApi";
11
14
  import { sendLogisticsEvent } from "../../lib/logisticsApi";
15
+ import { CART_PLAN_MISMATCH_MESSAGE, formatSiteTime, WORKDAYS_UNKNOWN, autoSendMessage, fetchAutoSend, isPickWorkday, mismatchReason, nextWorkday, printErrorText, PRINT_FAILED_HINT, printStageLabel, siteTimeZone, siteToday, } from "../../lib/pick142";
12
16
  import { completeLogisticsTaskForItem } from "../../lib/pickPrint";
13
- import { isPdfOnlyFailure, pickReadiness, printedCartIdsOf, pdfSaveWarning, unitRefOf, statusRefOf, unitTitleOf, } from "../../lib/pickTypes";
17
+ import { cartRowOf, isPdfOnlyFailure, pickReadiness, printedCartIdsOf, pdfSaveWarning, unitRefOf, statusRefOf, unitTitleOf, } from "../../lib/pickTypes";
14
18
  import { rowFingerprint, rowsToVerify } from "../../lib/pickVerify";
15
19
  import { alreadyPrintedResults, CART_MISSING_MESSAGE, cartResultLine, cartsForRow, isCartMissingError, reprintCartsIndividually, resettableJobs, runExclusive, resultsFromBatch, resultsFromBatchError, summarizeCartResults, trackableJobs, } from "../../lib/pickCartResults";
16
20
  import { getPickTranslator } from "../../lib/pickI18n";
17
21
  import { friendlyPickError, getPickUiTexts } from "../../lib/pickUiText";
18
- import { getPrintJobTexts, printJobStatusLabel } from "../../lib/pickPrintJobText";
22
+ import { getPrintJobTexts } from "../../lib/pickPrintJobText";
19
23
  import { defaultFilters, usePickDays, usePickHeartbeat, usePickQueue, } from "../../hooks/usePickQueue";
20
24
  import { usePickDocumentTypes, usePickStatuses, useStationPrintPlek, } from "../../hooks/usePickConfig";
21
25
  import { usePrintBatch, usePrintJob } from "../../hooks/usePrintJob";
@@ -38,7 +42,7 @@ async function releaseQuietly(itemId) {
38
42
  }
39
43
  }
40
44
  /** Melding als de regel nog niet compleet is aangeleverd door de bron. */
41
- const NOT_READY_NOTICE = "Deze pakbon is nog niet printklaar. Klik op \"Pakbon ophalen\" om de ontbrekende gegevens bij de bron op te vragen.";
45
+ const NOT_READY_NOTICE = "Deze pakbon is nog niet printklaar. Klik op \"Pakbon ophalen\" om de ontbrekende gegevens op te vragen.";
42
46
  /**
43
47
  * Pickscherm. De Logistiek Engine bepaalt welke documenten zichtbaar zijn en
44
48
  * in welke volgorde; dit scherm toont uitsluitend wat de wachtrij teruggeeft.
@@ -50,6 +54,8 @@ export function PickScherm({ station, onBack }) {
50
54
  const [chosenDay, setChosenDay] = useState(null);
51
55
  const [loadingDay, setLoadingDay] = useState(false);
52
56
  const [searchInput, setSearchInput] = useState("");
57
+ /** Telefoon: filters zitten achter een knop zodat de lijst bovenaan begint. */
58
+ const [filtersOpen, setFiltersOpen] = useState(false);
53
59
  const [busyId, setBusyId] = useState(null);
54
60
  const [pending, setPending] = useState(null);
55
61
  const [cartPlan, setCartPlan] = useState(null);
@@ -57,6 +63,42 @@ export function PickScherm({ station, onBack }) {
57
63
  const [planStatus, setPlanStatus] = useState(null);
58
64
  const [fetchingData, setFetchingData] = useState(false);
59
65
  const [readinessById, setReadinessById] = useState({});
66
+ /** Tijdzone van de vestiging (Engine-werktijden); null = onbekend. */
67
+ const [siteTz, setSiteTz] = useState(null);
68
+ const siteTzRef = useRef(null);
69
+ siteTzRef.current = siteTz;
70
+ const pickDayRef = useRef(null);
71
+ useEffect(() => {
72
+ void siteTimeZone().then((tz) => {
73
+ setKnownSiteZone(tz);
74
+ setSiteTz(tz);
75
+ });
76
+ }, []);
77
+ /** Order waarvan na de scan het automatisch versturen gevolgd wordt. */
78
+ const [autoSendOrderId, setAutoSendOrderId] = useState(null);
79
+ const [autoSend, setAutoSend] = useState(null);
80
+ const cartPlanRef = useRef(null);
81
+ useEffect(() => {
82
+ if (cartPlan)
83
+ cartPlanRef.current = cartPlan;
84
+ }, [cartPlan]);
85
+ /** Na de scan: stand van het automatisch versturen, elke 10 s (alleen lezen). */
86
+ useEffect(() => {
87
+ if (!autoSendOrderId)
88
+ return;
89
+ let stopped = false;
90
+ const read = async () => {
91
+ const state = await fetchAutoSend(autoSendOrderId, station.slug ?? null);
92
+ if (!stopped)
93
+ setAutoSend(state);
94
+ };
95
+ void read();
96
+ const timer = window.setInterval(() => void read(), 10000);
97
+ return () => {
98
+ stopped = true;
99
+ window.clearInterval(timer);
100
+ };
101
+ }, [autoSendOrderId, station.slug]);
60
102
  /** De vastgehouden rij is door de Engine vervangen of bestaat niet meer. */
61
103
  const [rowGone, setRowGone] = useState(false);
62
104
  /** Moment waarop dit scherm een printopdracht verstuurde (voor "wacht al X minuten"). */
@@ -98,7 +140,7 @@ export function PickScherm({ station, onBack }) {
98
140
  toast.success(result.message ?? "Verzendbrieven worden opgehaald.");
99
141
  }
100
142
  else {
101
- toast.info("Ophalen kon nu niet gestart worden — de verwerking gaat zelf verder.");
143
+ toast.info("De verzendbrieven worden zo vanzelf opgehaald. Je hoeft niets te doen.");
102
144
  }
103
145
  }
104
146
  finally {
@@ -112,12 +154,14 @@ export function PickScherm({ station, onBack }) {
112
154
  */
113
155
  const track = useCallback((args) => {
114
156
  trackJobRaw(args);
157
+ setAutoSendOrderId(cartPlanRef.current?.orderId ?? null);
115
158
  watchShippingNotes([
116
159
  { jobId: args.jobId, cartId: args.cartId ?? null, cartLabel: args.cartLabel ?? null },
117
160
  ]);
118
161
  }, [trackJobRaw, watchShippingNotes]);
119
162
  const trackBatch = useCallback((args) => {
120
163
  trackBatchRaw(args);
164
+ setAutoSendOrderId(cartPlanRef.current?.orderId ?? null);
121
165
  watchShippingNotes(args.jobs);
122
166
  }, [trackBatchRaw, watchShippingNotes]);
123
167
  /**
@@ -164,14 +208,25 @@ export function PickScherm({ station, onBack }) {
164
208
  enabled: lang !== null,
165
209
  pickDayOverride: chosenDay,
166
210
  });
211
+ /** Geprint maar nog niet gescand, van een eerdere pickdag (blijft open tot de scan). */
212
+ const viewDay = chosenDay ?? pickDay ?? null;
213
+ const { data: earlierRaw, refetch: refetchEarlier } = useQuery({
214
+ queryKey: ["pick", "unscannedEarlier", station.id, viewDay ?? "-"],
215
+ enabled: lang !== null && !!viewDay,
216
+ refetchInterval: 30000,
217
+ queryFn: () => pickApi.unscannedEarlier({ stationId: station.id, beforeDay: viewDay }),
218
+ });
219
+ const earlierRows = Array.isArray(earlierRaw) ? earlierRaw : [];
167
220
  // Eén toast per nieuwe syncfout; de rode balk blijft staan tot hij is opgelost.
221
+ const SYNC_ERROR_MESSAGE = "De lijst kon niet worden ververst. Controleer de wifi en klik op ‘Wachtrij verversen’.";
168
222
  const lastSyncError = useRef(null);
169
223
  useEffect(() => {
170
224
  if (syncError && syncError !== lastSyncError.current) {
171
- toast.error(`Pickwachtrij kan niet worden bijgewerkt: ${syncError}`);
225
+ toast.error(SYNC_ERROR_MESSAGE, { description: `Details voor beheer: ${syncError}` });
172
226
  }
173
227
  lastSyncError.current = syncError;
174
228
  }, [syncError]);
229
+ pickDayRef.current = chosenDay ?? pickDay ?? null;
175
230
  const { data: pickDays } = usePickDays(lang !== null);
176
231
  const safePickDays = Array.isArray(pickDays) ? pickDays : [];
177
232
  const dayLabel = useCallback((day) => new Date(`${day}T00:00:00`).toLocaleDateString(t.lang === "en" ? "en-GB" : "nl-NL", {
@@ -197,6 +252,7 @@ export function PickScherm({ station, onBack }) {
197
252
  /** Kiest een andere pickdag en laat de Engine die dag alvast klaarzetten. */
198
253
  const chooseDay = useCallback(async (value) => {
199
254
  const day = value === "current" ? null : value;
255
+ const previousDay = chosenDay;
200
256
  const previousPending = pending;
201
257
  daySelectionVersion.current += 1;
202
258
  setPending(null);
@@ -223,24 +279,33 @@ export function PickScherm({ station, onBack }) {
223
279
  toast.success(ui.pickDayLoaded);
224
280
  }
225
281
  catch (error) {
282
+ // Bijv. niet-werkdag: terug naar de vorige dag, tekst van de Engine tonen.
283
+ setChosenDay(previousDay);
226
284
  toast.error(error instanceof Error ? error.message : ui.genericError);
227
285
  }
228
286
  finally {
229
287
  setLoadingDay(false);
230
288
  }
231
- }, [pending, refresh, ui]);
289
+ }, [pending, refresh, ui, chosenDay]);
232
290
  /**
233
291
  * Verschuift de pickdag één dag vooruit of achteruit. Zonder handmatige keuze
234
292
  * is de kalenderdag van vandaag het startpunt, niet de dag van de Engine.
235
293
  */
236
- const shiftDay = useCallback((delta) => {
237
- const todayIso = new Date().toLocaleDateString("sv-SE", { timeZone: "Europe/Amsterdam" });
238
- const base = chosenDay ?? todayIso;
239
- const next = new Date(`${base}T00:00:00Z`);
240
- next.setUTCDate(next.getUTCDate() + delta);
241
- const iso = next.toISOString().slice(0, 10);
294
+ const shiftDay = useCallback(async (delta) => {
295
+ // Vandaag in de tijdzone van de vestiging; onbekend = niet verschuiven.
296
+ const todayIso = siteToday(siteTz);
297
+ const base = chosenDay ?? pickDay ?? todayIso;
298
+ if (!base) {
299
+ toast.error(WORKDAYS_UNKNOWN);
300
+ return;
301
+ }
302
+ const iso = await nextWorkday(base, delta, isPickWorkday);
303
+ if (!iso) {
304
+ toast.error(WORKDAYS_UNKNOWN);
305
+ return;
306
+ }
242
307
  void chooseDay(iso === todayIso || (pickDay && iso === pickDay) ? "current" : iso);
243
- }, [chosenDay, pickDay, chooseDay]);
308
+ }, [chosenDay, pickDay, chooseDay, siteTz]);
244
309
  /**
245
310
  * Haalt het vastgelegde karplan én de toestand van de planning uit de bron.
246
311
  * Er wordt niets verzonnen: is het plan er niet, of is het nog niet
@@ -336,6 +401,8 @@ export function PickScherm({ station, onBack }) {
336
401
  const fresh = todo.filter((item) => !verifyMemo.current.has(item.id)).length;
337
402
  if (fresh > 0)
338
403
  setCheckProgress({ done: 0, total: fresh });
404
+ else
405
+ setCheckProgress({ done: 0, total: 0 });
339
406
  running = true;
340
407
  const next = {};
341
408
  try {
@@ -347,14 +414,24 @@ export function PickScherm({ station, onBack }) {
347
414
  let entry = {
348
415
  checking: false,
349
416
  ready: false,
350
- reason: "De bron kon de pakbon nog niet bevestigen.",
417
+ reason: "De pakbon is nog niet klaar. Klik over een minuut op ‘Pakbon ophalen’.",
418
+ waitUntil: null,
351
419
  };
352
420
  if (r && !r.error) {
353
421
  const state = pickReadiness(r.plan, r.status, item);
354
- entry = { checking: false, ready: state.ready, reason: state.reason };
422
+ const rowWait = rowWaitOf(item.payload);
423
+ const notPrintable = !rowWait.printable || r.plan?.printable === false;
424
+ const waitUntil = notPrintable ? rowWait.until ?? r.plan?.waitUntil ?? null : null;
425
+ const st = r.status;
426
+ const ownCartId = (() => { const p = (item.payload ?? {}); return typeof p["cartId"] === "string" ? p["cartId"] : null; })();
427
+ const notStarted = !state.ready && isPickWindowNotStarted(st) && !isForcedFor(item, pickDayRef.current)
428
+ ? releaseMessage(releaseInfoOf(r.plan?.raw, ownCartId, item, st), siteTzRef.current)
429
+ : null;
430
+ const lateSignup = lateSignupOf(item, r.plan?.raw);
431
+ entry = { checking: false, ready: state.ready && !notPrintable && !notStarted, reason: state.reason, waitUntil, notPrintable, notStarted, lateSignup };
355
432
  }
356
433
  else if (r?.error && isRowGoneError(r.error)) {
357
- entry = { checking: false, ready: false, reason: String(r.error), replaced: true };
434
+ entry = { checking: false, ready: false, reason: String(r.error), replaced: true, waitUntil: null };
358
435
  }
359
436
  next[item.id] = entry;
360
437
  verifyMemo.current.set(item.id, {
@@ -415,9 +492,15 @@ export function PickScherm({ station, onBack }) {
415
492
  /* het seintje is aanvullend op de bewaking */
416
493
  }
417
494
  }
495
+ let lastMismatches = [];
418
496
  for (let attempt = 0; attempt < 20; attempt += 1) {
419
497
  await new Promise((resolve) => setTimeout(resolve, 3000));
420
- const plan = await pickApi.queueCartPlan(item.id);
498
+ const checked = await pickApi.queueCartPlanChecked(item.id);
499
+ const plan = checked.plan;
500
+ const own = cartRowOf(item);
501
+ lastMismatches = own
502
+ ? checked.mismatches.filter((m) => m.cartId === own.cartId)
503
+ : checked.mismatches;
421
504
  let status = null;
422
505
  const statusRef = statusRefOf(item, plan);
423
506
  try {
@@ -444,7 +527,42 @@ export function PickScherm({ station, onBack }) {
444
527
  return true;
445
528
  }
446
529
  }
447
- toast.warning(planReason ?? "De pakbon is nog niet compleet. De bron is op de hoogte gebracht.");
530
+ if (lastMismatches.length > 0) {
531
+ // Nooit stil blijven hangen: duidelijke melding en de reden naar de bewaking.
532
+ const reason = mismatchReason(lastMismatches);
533
+ setReadinessById((prev) => ({
534
+ ...prev,
535
+ [item.id]: { checking: false, ready: false, reason: CART_PLAN_MISMATCH_MESSAGE, mismatch: true },
536
+ }));
537
+ toast.error(CART_PLAN_MISMATCH_MESSAGE, { description: `Details voor beheer: ${reason}` });
538
+ try {
539
+ await pickApi.reportMissingCartPlans();
540
+ }
541
+ catch {
542
+ /* bewaking is aanvullend */
543
+ }
544
+ if (ref) {
545
+ try {
546
+ await sendLogisticsEvent({
547
+ event_type: "order.changed",
548
+ order_ref: ref,
549
+ shadow: false,
550
+ payload: {
551
+ reason: "pick.cart_plan_mismatch",
552
+ detail: reason,
553
+ mismatches: lastMismatches,
554
+ queue_item_id: item.id,
555
+ idempotency_key: `pick.cart_plan_mismatch:${item.id}:${reason}`,
556
+ },
557
+ });
558
+ }
559
+ catch {
560
+ /* het seintje is aanvullend op de bewaking */
561
+ }
562
+ }
563
+ return false;
564
+ }
565
+ toast.warning(planReason ?? "De pakbon is nog niet klaar. Klik over een minuut op ‘Pakbon ophalen’.");
448
566
  return false;
449
567
  }
450
568
  finally {
@@ -481,7 +599,12 @@ export function PickScherm({ station, onBack }) {
481
599
  setPending(restored);
482
600
  setActiveCartId(tracked.cartId);
483
601
  }
484
- toast.error(`${jobTexts.failed}: ${job.foutmelding ?? jobTexts.unknown}`);
602
+ {
603
+ const friendly = printErrorText(job.foutmelding);
604
+ toast.error(friendly?.message ?? `Niet geprint. ${PRINT_FAILED_HINT}`, {
605
+ description: job.foutmelding ? `Details voor beheer: ${job.foutmelding}` : undefined,
606
+ });
607
+ }
485
608
  }
486
609
  catch (e) {
487
610
  toast.error(e instanceof Error ? e.message : jobTexts.failed);
@@ -522,6 +645,11 @@ export function PickScherm({ station, onBack }) {
522
645
  return expiredClaim;
523
646
  }, [t, handleRowGone]);
524
647
  const handlePick = useCallback(async (item) => {
648
+ const block = printBlockMessage(item, siteTz);
649
+ if (block) {
650
+ toast.warning(block);
651
+ return;
652
+ }
525
653
  setBusyId(item.id);
526
654
  try {
527
655
  const claimed = await pickApi.claim({
@@ -548,7 +676,7 @@ export function PickScherm({ station, onBack }) {
548
676
  finally {
549
677
  setBusyId(null);
550
678
  }
551
- }, [station.id, station.lock_timeout_seconds, sessionId, loadCartPlan, refresh, showPrintError]);
679
+ }, [station.id, station.lock_timeout_seconds, sessionId, loadCartPlan, refresh, showPrintError, siteTz]);
552
680
  /**
553
681
  * Eén vergrendeling voor alle printacties. Gaat direct bij de klik in (ref,
554
682
  * niet pas bij de volgende weergave), zodat een dubbelklik nooit een tweede
@@ -598,6 +726,11 @@ export function PickScherm({ station, onBack }) {
598
726
  * zonder de wachtrijstatus te wijzigen.
599
727
  */
600
728
  const handleReprint = useCallback(async (item) => {
729
+ const block = printBlockMessage(item, siteTz);
730
+ if (block) {
731
+ toast.warning(block);
732
+ return;
733
+ }
601
734
  setBusyId(item.id);
602
735
  try {
603
736
  const jobId = await pickApi.reprint(item.id, sessionId);
@@ -618,7 +751,7 @@ export function PickScherm({ station, onBack }) {
618
751
  finally {
619
752
  setBusyId(null);
620
753
  }
621
- }, [t, sessionId, track, showPrintError]);
754
+ }, [t, sessionId, track, showPrintError, siteTz]);
622
755
  /** Open een geprinte order bovenaan zonder de wachtrijstatus te wijzigen. */
623
756
  const handleReprintLocked = useMemo(() => withActionLock(handleReprint), [withActionLock, handleReprint]);
624
757
  const selectPrintedForReprint = useCallback(async (item) => {
@@ -644,6 +777,11 @@ export function PickScherm({ station, onBack }) {
644
777
  * ontbrekende gegevens bij de bron opgevraagd.
645
778
  */
646
779
  const printAllForItemInner = useCallback(async (item) => {
780
+ const block = printBlockMessage(item, siteTz);
781
+ if (block) {
782
+ toast.warning(block);
783
+ return;
784
+ }
647
785
  setBusyId(item.id);
648
786
  setCartMissing(false);
649
787
  try {
@@ -747,6 +885,7 @@ export function PickScherm({ station, onBack }) {
747
885
  showCartResults,
748
886
  refresh,
749
887
  showPrintError,
888
+ siteTz,
750
889
  ]);
751
890
  const printAllForItem = useMemo(() => withActionLock(printAllForItemInner), [withActionLock, printAllForItemInner]);
752
891
  /**
@@ -768,6 +907,10 @@ export function PickScherm({ station, onBack }) {
768
907
  for (const item of visible) {
769
908
  if (targets.length >= cartsPerPrint)
770
909
  break;
910
+ if (!isRowPrintable(item)) {
911
+ skipped.push({ title: unitTitleOf(item), reason: printBlockMessage(item, siteTz) ?? "nog niet printklaar" });
912
+ continue;
913
+ }
771
914
  const state = readinessById[item.id];
772
915
  if (!state || state.checking) {
773
916
  skipped.push({ title: unitTitleOf(item), reason: "de pakbon wordt nog gecontroleerd" });
@@ -805,7 +948,7 @@ export function PickScherm({ station, onBack }) {
805
948
  finally {
806
949
  setBatchPrinting(false);
807
950
  }
808
- }, [cartsPerPrint, batchPrinting, items, readinessById, printAllForItemInner, showPrintError]);
951
+ }, [cartsPerPrint, batchPrinting, items, readinessById, printAllForItemInner, showPrintError, siteTz]);
809
952
  const printNextCarts = useMemo(() => withActionLock(printNextCartsInner), [withActionLock, printNextCartsInner]);
810
953
  const fetchNext = useCallback(async (silent = false) => {
811
954
  const requestedDayVersion = daySelectionVersion.current;
@@ -1015,7 +1158,8 @@ export function PickScherm({ station, onBack }) {
1015
1158
  setPending(restored);
1016
1159
  setActiveCartId(failed[0]?.cartId ?? null);
1017
1160
  const firstError = failed[0]?.job?.foutmelding ?? jobTexts.unknown;
1018
- toast.error(`${failed.length} ${failed.length === 1 ? "pakbon is" : "pakbonnen zijn"} mislukt: ${firstError}`);
1161
+ toast.error(printErrorText(firstError)?.message ??
1162
+ `${failed.length} ${failed.length === 1 ? "pakbon is" : "pakbonnen zijn"} niet geprint. ${PRINT_FAILED_HINT}`, { description: `Details voor beheer: ${firstError}` });
1019
1163
  clearBatch();
1020
1164
  setBusyId(null);
1021
1165
  refresh();
@@ -1030,15 +1174,20 @@ export function PickScherm({ station, onBack }) {
1030
1174
  setActiveCartId(null);
1031
1175
  setBusyId(null);
1032
1176
  refresh();
1033
- await fetchNext();
1177
+ // Geen automatisch "Opdracht bevestigen": alleen na een klik op "Volgende printen".
1034
1178
  })().catch((error) => {
1035
1179
  finalizedBatch.current = null;
1036
1180
  setBusyId(null);
1037
1181
  showPrintError(error);
1038
1182
  });
1039
- }, [batch, sessionId, pending, jobTexts, clearBatch, refresh, fetchNext, showPrintError]);
1183
+ }, [batch, sessionId, pending, jobTexts, clearBatch, refresh, showPrintError]);
1040
1184
  /** Tik op een al geprinte kar: die pakbon nog een keer naar de printer sturen. */
1041
1185
  const reprintCart = useCallback(async (item, cart) => {
1186
+ const block = printBlockMessage(item, siteTz);
1187
+ if (block) {
1188
+ toast.warning(block);
1189
+ return;
1190
+ }
1042
1191
  setBusyId("confirm");
1043
1192
  try {
1044
1193
  const jobId = await pickApi.reprintCart(item.id, sessionId, cart.cartId);
@@ -1060,7 +1209,7 @@ export function PickScherm({ station, onBack }) {
1060
1209
  finally {
1061
1210
  setBusyId(null);
1062
1211
  }
1063
- }, [sessionId, t, track, showPrintError]);
1212
+ }, [sessionId, t, track, showPrintError, siteTz]);
1064
1213
  /** Eén losse pakbon (kar) printen vanaf de knop op de karregel. */
1065
1214
  const printSingleCartInner = useCallback(async (item, cart) => {
1066
1215
  // Alleen een kar uit het karplan van deze rij; bij een eigen kar alleen die.
@@ -1197,8 +1346,10 @@ export function PickScherm({ station, onBack }) {
1197
1346
  ? 100
1198
1347
  : Math.round((checkDone / checkTotal) * 100);
1199
1348
  return (_jsxs("div", { className: "space-y-4 pb-24 md:pb-0", children: [busyLoading && (_jsxs("div", { className: "space-y-1", role: "status", "aria-live": "polite", "aria-label": "Gegevens worden geladen", children: [_jsxs("div", { className: "flex items-center justify-between text-xs text-muted-foreground", children: [_jsx("span", { children: loading || loadingDay
1200
- ? "Wachtrij wordt geladen…"
1201
- : `Pakbonnen controleren bij de bron (${checkDone} van ${checkTotal})` }), _jsxs("span", { className: "tabular-nums", children: [loadPercent, "%"] })] }), _jsx("div", { className: "h-1.5 w-full overflow-hidden rounded-full bg-muted", children: _jsx("div", { className: "h-full rounded-full bg-primary transition-all duration-300", style: { width: `${Math.max(5, loadPercent)}%` } }) })] })), _jsxs("div", { className: "flex flex-col gap-3 md:flex-row md:flex-wrap md:items-center md:justify-between", children: [_jsxs("div", { className: "grid grid-cols-[auto_minmax(0,1fr)] items-center gap-3", children: [_jsxs(Button, { variant: "outline", size: "lg", className: "h-12 shrink-0 px-3 sm:px-4", onClick: () => setLang(null), "aria-label": t("changeLanguage"), children: [_jsx(Globe, { className: "h-5 w-5 sm:mr-2" }), _jsx("span", { className: "hidden sm:inline", children: t("changeLanguage") })] }), _jsxs("div", { className: "min-w-0", children: [_jsx("h2", { className: "truncate text-lg font-bold text-foreground sm:text-xl", children: station.name }), _jsxs("p", { className: "truncate text-sm text-muted-foreground", children: [pendingCount, " ", t("statusToPrint"), " \u00B7 ", printedCount, " ", t("statusPrinted"), openJobIds.length > 0 ? ` · ${openJobIds.length} wacht op printer` : ""] })] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Button, { variant: "outline", size: "lg", className: "h-12 px-3", onClick: () => shiftDay(-1), disabled: loadingDay || !(chosenDay ?? pickDay), "aria-label": ui.pickDayPrev, children: [_jsx(ChevronLeft, { className: "h-5 w-5 sm:mr-1" }), _jsx("span", { className: "hidden sm:inline", children: ui.pickDayPrev })] }), _jsx("div", { className: "flex h-12 items-center rounded-md border bg-muted px-4 text-sm font-semibold text-foreground", children: chosenDay ? dayLabel(chosenDay) : ui.pickDayToday }), _jsxs(Button, { variant: "outline", size: "lg", className: "h-12 px-3", onClick: () => shiftDay(1), disabled: loadingDay || !(chosenDay ?? pickDay), "aria-label": ui.pickDayNext, children: [_jsx("span", { className: "hidden sm:inline", children: ui.pickDayNext }), _jsx(ChevronRight, { className: "h-5 w-5 sm:ml-1" })] }), chosenDay && (_jsx(Button, { variant: "secondary", size: "lg", className: "h-12 px-4", onClick: () => void chooseDay("current"), disabled: loadingDay, children: ui.pickDayBackToToday }))] }), _jsxs("div", { className: "flex items-center gap-2", children: [!online && (_jsxs(Badge, { className: "bg-destructive text-destructive-foreground", children: [_jsx(WifiOff, { className: "mr-1 h-3.5 w-3.5" }), " ", t("offline")] })), _jsx(Button, { variant: "outline", size: "icon", className: "h-12 w-12 shrink-0", onClick: refresh, "aria-label": t("refresh"), children: _jsx(RefreshCw, { className: "h-4 w-4" }) }), _jsxs(Button, { variant: "outline", size: "lg", className: "hidden h-14 px-5 md:inline-flex", disabled: fetchingNotes, onClick: () => void fetchShippingNotes(), children: [fetchingNotes ? (_jsx(Loader2, { className: "mr-2 h-5 w-5 animate-spin" })) : (_jsx(FileDown, { className: "mr-2 h-5 w-5" })), fetchingNotes ? "Ophalen…" : "Verzendbrieven ophalen"] }), cartsPerPrint != null && (_jsxs(Button, { size: "lg", className: "hidden h-14 px-6 text-lg md:inline-flex", disabled: loading || batchPrinting || (busyId != null || actionBusy), onClick: () => void printNextCarts(), children: [batchPrinting ? (_jsx(Loader2, { className: "mr-2 h-5 w-5 animate-spin" })) : (_jsx(Printer, { className: "mr-2 h-5 w-5" })), "Print eerstvolgende ", cartsPerPrint, " karren"] })), _jsxs(Button, { size: "lg", className: "hidden h-14 px-8 text-lg md:inline-flex", onClick: () => (pending ? confirmPending() : fetchNextLocked()), disabled: (busyId !== null || actionBusy) || fetchingData, children: [(busyId !== null || actionBusy) || fetchingData ? (_jsx(Loader2, { className: "mr-2 h-5 w-5 animate-spin" })) : pending && !readiness.ready ? (_jsx(RefreshCw, { className: "mr-2 h-5 w-5" })) : (_jsx(Printer, { className: "mr-2 h-5 w-5" })), pending && !readiness.ready ? "Pakbon ophalen" : t("printNext"), pending?.document_number ? ` · ${pending.document_number}` : ""] })] })] }), syncError && (_jsxs("div", { role: "alert", 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" }), _jsxs("span", { children: ["Pickwachtrij kan niet worden bijgewerkt: ", syncError] })] })), !plekLoading && !printPlek && (_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: "Nog geen printplek gekoppeld \u2014 de opdracht blijft in de wachtrij staan." })] })), (cartMissing || rowGone) && (_jsxs("div", { role: "alert", className: "flex flex-col gap-2 rounded-md border-2 border-destructive/50 bg-destructive/5 p-3 text-sm text-destructive sm:flex-row sm:items-center sm:justify-between", children: [_jsxs("span", { className: "flex items-start gap-2", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0" }), rowGone ? ROW_GONE_MESSAGE : CART_MISSING_MESSAGE] }), _jsx(Button, { variant: "outline", size: "sm", onClick: () => {
1349
+ ? "Wachtrij laden…"
1350
+ : checkDone > 0
1351
+ ? `Pakbonnen controleren (${checkDone} van ${checkTotal})…`
1352
+ : `Pakbonnen controleren (${checkTotal})…` }), _jsxs("span", { className: "tabular-nums", children: [loadPercent, "%"] })] }), _jsx("div", { className: "h-1.5 w-full overflow-hidden rounded-full bg-muted", children: _jsx("div", { className: "h-full rounded-full bg-primary transition-all duration-300", style: { width: `${Math.max(5, loadPercent)}%` } }) })] })), _jsxs("div", { className: "flex flex-col gap-3 md:flex-row md:flex-wrap md:items-center md:justify-between", children: [_jsxs("div", { className: "grid grid-cols-[auto_minmax(0,1fr)] items-center gap-3", children: [_jsxs(Button, { variant: "outline", size: "lg", className: "h-12 shrink-0 px-3 sm:px-4", onClick: () => setLang(null), "aria-label": t("changeLanguage"), children: [_jsx(Globe, { className: "h-5 w-5 sm:mr-2" }), _jsx("span", { className: "hidden sm:inline", children: t("changeLanguage") })] }), _jsxs("div", { className: "min-w-0", children: [_jsx("h2", { className: "truncate text-lg font-bold text-foreground sm:text-xl", children: station.name }), _jsxs("p", { className: "truncate text-sm text-muted-foreground", children: [pendingCount, " ", t("statusToPrint"), " \u00B7 ", printedCount, " ", t("statusPrinted"), openJobIds.length > 0 ? ` · ${openJobIds.length} wacht op printer` : ""] })] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Button, { variant: "outline", size: "lg", className: "h-12 px-3", onClick: () => void shiftDay(-1), disabled: loadingDay || !(chosenDay ?? pickDay), "aria-label": ui.pickDayPrev, children: [_jsx(ChevronLeft, { className: "h-5 w-5 sm:mr-1" }), _jsx("span", { className: "hidden sm:inline", children: ui.pickDayPrev })] }), _jsx("div", { className: "flex h-12 items-center rounded-md border bg-muted px-4 text-sm font-semibold text-foreground", children: chosenDay ? dayLabel(chosenDay) : ui.pickDayToday }), _jsxs(Button, { variant: "outline", size: "lg", className: "h-12 px-3", onClick: () => void shiftDay(1), disabled: loadingDay || !(chosenDay ?? pickDay), "aria-label": ui.pickDayNext, children: [_jsx("span", { className: "hidden sm:inline", children: ui.pickDayNext }), _jsx(ChevronRight, { className: "h-5 w-5 sm:ml-1" })] }), chosenDay && (_jsx(Button, { variant: "secondary", size: "lg", className: "h-12 px-4", onClick: () => void chooseDay("current"), disabled: loadingDay, children: ui.pickDayBackToToday }))] }), _jsxs("div", { className: "flex items-center gap-2", children: [!online && (_jsxs(Badge, { className: "bg-destructive text-destructive-foreground", children: [_jsx(WifiOff, { className: "mr-1 h-3.5 w-3.5" }), " ", t("offline")] })), _jsx(Button, { variant: "outline", size: "icon", className: "h-12 w-12 shrink-0", onClick: refresh, "aria-label": t("refresh"), children: _jsx(RefreshCw, { className: "h-4 w-4" }) }), _jsxs(Button, { variant: "outline", size: "lg", className: "hidden h-14 px-5 md:inline-flex", disabled: fetchingNotes, onClick: () => void fetchShippingNotes(), children: [fetchingNotes ? (_jsx(Loader2, { className: "mr-2 h-5 w-5 animate-spin" })) : (_jsx(FileDown, { className: "mr-2 h-5 w-5" })), fetchingNotes ? "Ophalen…" : "Verzendbrieven ophalen"] }), cartsPerPrint != null && (_jsxs(Button, { size: "lg", className: "hidden h-14 px-6 text-lg md:inline-flex", disabled: loading || batchPrinting || (busyId != null || actionBusy), onClick: () => void printNextCarts(), children: [batchPrinting ? (_jsx(Loader2, { className: "mr-2 h-5 w-5 animate-spin" })) : (_jsx(Printer, { className: "mr-2 h-5 w-5" })), "Volgende printen (", cartsPerPrint, " karren)"] })), _jsxs(Button, { size: "lg", className: "hidden h-14 px-8 text-lg md:inline-flex", onClick: () => (pending ? confirmPending() : fetchNextLocked()), disabled: (busyId !== null || actionBusy) || fetchingData, children: [(busyId !== null || actionBusy) || fetchingData ? (_jsx(Loader2, { className: "mr-2 h-5 w-5 animate-spin" })) : pending && !readiness.ready ? (_jsx(RefreshCw, { className: "mr-2 h-5 w-5" })) : (_jsx(Printer, { className: "mr-2 h-5 w-5" })), pending && !readiness.ready ? "Pakbon ophalen" : t("printNext")] })] })] }), syncError && (_jsxs("div", { role: "alert", 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" }), _jsxs("span", { children: [SYNC_ERROR_MESSAGE, _jsxs("details", { className: "mt-1 text-xs text-muted-foreground", children: [_jsx("summary", { children: "Details voor beheer" }), syncError] })] })] })), !plekLoading && !printPlek && (_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: "Nog geen printplek gekoppeld \u2014 de opdracht blijft in de wachtrij staan." })] })), (cartMissing || rowGone) && (_jsxs("div", { role: "alert", className: "flex flex-col gap-2 rounded-md border-2 border-destructive/50 bg-destructive/5 p-3 text-sm text-destructive sm:flex-row sm:items-center sm:justify-between", children: [_jsxs("span", { className: "flex items-start gap-2", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0" }), rowGone ? ROW_GONE_MESSAGE : CART_MISSING_MESSAGE] }), _jsx(Button, { variant: "outline", size: "sm", onClick: () => {
1202
1353
  setCartMissing(false);
1203
1354
  setRowGone(false);
1204
1355
  setPending(null);
@@ -1211,9 +1362,9 @@ export function PickScherm({ station, onBack }) {
1211
1362
  setLastCartResults((prev) => prev.filter((x) => x.cartId !== r.cartId));
1212
1363
  void reprintCart(lastPrinted, cart);
1213
1364
  }, children: "Opnieuw printen" })] }, r.cartId))) }) })), lastCartResults.length > 1 && (_jsxs("div", { className: "rounded-md border p-3 text-sm", "aria-label": "Resultaat per kar", children: [_jsx("p", { className: "mb-1 font-semibold text-foreground", children: "Resultaat per kar" }), _jsx("ul", { className: "space-y-0.5 text-muted-foreground", children: lastCartResults.map((r) => (_jsx("li", { className: r.outcome === "created" ? "text-foreground" : r.outcome === "error" ? "text-destructive" : "", children: cartResultLine(r) }, r.cartId))) })] })), _jsx(ShippingNoteScan, { stationId: station.id }), !pending && emptyNotice && (_jsx("p", { className: "text-sm text-muted-foreground", children: emptyNotice })), pending && (_jsxs(Card, { className: "border-2 border-primary", children: [_jsxs(CardContent, { className: "flex flex-col gap-4 p-4 sm:flex-row sm:flex-wrap sm:items-center sm:justify-between sm:p-5", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "text-sm font-medium text-muted-foreground", children: t("confirmPrintTitle") }), _jsx("p", { className: "truncate text-xl font-bold text-foreground sm:text-2xl", children: pending.document_number ?? t("document") }), _jsx("p", { className: "truncate text-sm text-muted-foreground", children: [pending.order_number, pending.customer_name].filter(Boolean).join(" · ") })] }), _jsxs("div", { className: "flex flex-col gap-2 sm:flex-row", children: [_jsx(Button, { variant: "outline", size: "lg", className: "h-12 w-full sm:w-auto", onClick: cancelPending, disabled: (busyId !== null || actionBusy), children: t("cancel") }), _jsxs(Button, { size: "lg", className: "h-14 w-full px-8 text-lg sm:w-auto", onClick: confirmPending, disabled: (busyId !== null || actionBusy) || fetchingData, variant: readiness.ready ? "default" : "secondary", children: [busyId === "confirm" || fetchingData ? (_jsx(Loader2, { className: "mr-2 h-5 w-5 animate-spin" })) : readiness.ready ? (_jsx(Printer, { className: "mr-2 h-5 w-5" })) : (_jsx(RefreshCw, { className: "mr-2 h-5 w-5" })), readiness.ready ? t("print") : "Pakbon ophalen"] })] })] }), !readiness.ready && (_jsx(CardContent, { className: "border-t p-4 sm:p-5", children: _jsxs("div", { className: "flex items-start gap-2 rounded-md border bg-muted/30 p-3 text-sm text-muted-foreground", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0" }), _jsx("span", { children: fetchingData
1214
- ? "De pakbon wordt bij de bron opgevraagd. Zodra alles compleet is, verschijnt de printknop weer."
1365
+ ? "De pakbon wordt opgevraagd. Zodra alles compleet is, verschijnt de printknop weer."
1215
1366
  : planReason
1216
- ? `${planReason} Klik op "Pakbon ophalen" om hem bij de bron op te vragen.`
1367
+ ? `${planReason} Klik op "Pakbon ophalen" om hem op te vragen.`
1217
1368
  : NOT_READY_NOTICE })] }) })), cartPlan && (_jsxs(CardContent, { className: "border-t p-4 sm:p-5", children: [_jsx(OrderStickerMeldingen, { orderId: cartPlan.orderId, slug: station.slug ?? null }), _jsx("div", { className: "mb-3 empty:hidden", children: _jsx(PlanWarnings, { warnings: cartPlan.warnings }) }), _jsxs("div", { className: "grid gap-3 lg:grid-cols-[minmax(0,0.8fr)_minmax(0,1.2fr)]", children: [_jsxs("div", { className: "space-y-2", children: [_jsx("p", { className: "text-sm font-semibold text-foreground", children: "Karren van deze order" }), _jsx("div", { className: "max-h-72 space-y-2 overflow-y-auto rounded-md border p-2", children: cartPlan.carts.map((cart) => {
1218
1369
  const done = printedCartIdsOf(pending).includes(cart.cartId);
1219
1370
  const active = cart.cartId === activeCartId;
@@ -1240,7 +1391,7 @@ export function PickScherm({ station, onBack }) {
1240
1391
  const cart = cartPlan?.carts.find((c) => c.cartId === entry.cartId) ?? null;
1241
1392
  const item = lastPrinted ?? pending;
1242
1393
  return (_jsxs("div", { className: "mt-1 flex flex-wrap items-center gap-2 text-sm font-medium text-destructive", children: [_jsxs("span", { children: [entry.cartLabel ? `${entry.cartLabel}: ` : "", stuckMessage(minutes), entry.job?.printer_naam ? ` (${entry.job.printer_naam})` : ""] }), cart && item && (_jsx(Button, { size: "sm", variant: "outline", disabled: busyId !== null || actionBusy, onClick: () => void reprintCart(item, cart), children: "Opnieuw proberen" }))] }, entry.jobId));
1243
- })] })] }) })), tracked && (_jsx(Card, { className: jobFailed ? "border-2 border-destructive" : "border-2 border-primary/40", children: _jsxs(CardContent, { className: "flex flex-col gap-4 p-4 sm:flex-row sm:flex-wrap sm:items-center sm:justify-between", children: [_jsxs("div", { className: "flex min-w-0 items-start gap-3", children: [jobFailed ? (_jsx(AlertTriangle, { className: "h-6 w-6 shrink-0 text-destructive" })) : jobStatus === "voltooid" ? (_jsx(CheckCircle2, { className: "h-6 w-6 shrink-0 text-primary" })) : (_jsx(Loader2, { className: "h-6 w-6 shrink-0 animate-spin text-muted-foreground" })), _jsxs("div", { className: "min-w-0", children: [_jsxs("p", { className: "truncate text-sm font-medium text-muted-foreground", children: [jobTexts.jobTitle, " ", tracked.documentNumber ? `· ${tracked.documentNumber}` : "", tracked.cartLabel ? ` · ${tracked.cartLabel}` : ""] }), _jsx("p", { className: "text-lg font-semibold text-foreground", children: printJobStatusLabel(jobStatus, t.lang) }), tracked.job?.printer_naam && (_jsxs("p", { className: "truncate text-sm text-muted-foreground", children: [jobTexts.printerLabel, ": ", tracked.job.printer_naam] })), (() => {
1394
+ })] })] }) })), tracked && (_jsx(Card, { className: jobFailed ? "border-2 border-destructive" : "border-2 border-primary/40", children: _jsxs(CardContent, { className: "flex flex-col gap-4 p-4 sm:flex-row sm:flex-wrap sm:items-center sm:justify-between", children: [_jsxs("div", { className: "flex min-w-0 items-start gap-3", children: [jobFailed ? (_jsx(AlertTriangle, { className: "h-6 w-6 shrink-0 text-destructive" })) : jobStatus === "voltooid" ? (_jsx(CheckCircle2, { className: "h-6 w-6 shrink-0 text-primary" })) : (_jsx(Loader2, { className: "h-6 w-6 shrink-0 animate-spin text-muted-foreground" })), _jsxs("div", { className: "min-w-0", children: [_jsxs("p", { className: "truncate text-sm font-medium text-muted-foreground", children: [jobTexts.jobTitle, " ", tracked.documentNumber ? `· ${tracked.documentNumber}` : "", tracked.cartLabel ? ` · ${tracked.cartLabel}` : ""] }), _jsx("p", { className: "text-lg font-semibold text-foreground", children: printStageLabel(tracked.job ?? { status: jobStatus }) }), tracked.job?.printer_naam && (_jsxs("p", { className: "truncate text-sm text-muted-foreground", children: [jobTexts.printerLabel, ": ", tracked.job.printer_naam] })), (() => {
1244
1395
  const minutes = stuckMinutesOf(tracked.job, sentAtRef.current.get(tracked.jobId) ?? null, nowTick);
1245
1396
  if (minutes === null)
1246
1397
  return null;
@@ -1253,15 +1404,21 @@ export function PickScherm({ station, onBack }) {
1253
1404
  }, disabled: (busyId !== null || actionBusy) || !pending, children: [_jsx(Printer, { className: "mr-2 h-5 w-5" }), " ", jobTexts.retry] })), _jsx(Button, { variant: "outline", size: "lg", className: "h-12 w-full sm:w-auto", onClick: () => {
1254
1405
  clearJob();
1255
1406
  clearShippingNotes();
1407
+ setAutoSendOrderId(null);
1408
+ setAutoSend(null);
1256
1409
  }, children: jobTexts.close })] })] }) })), shippingNotes.length > 0 && (_jsx(Card, { className: "border-2 border-primary/20", children: _jsxs(CardContent, { className: "flex flex-col gap-2 p-4", children: [_jsx("p", { className: "text-sm font-semibold text-foreground", children: shippingNotes.every((note) => note.state.status === "geprint")
1257
- ? "Printactie volledig geslaagd — pakbon en verzendbrief zijn geprint."
1258
- : "Printactie nog niet volledig: de verzendbrief is nog niet geprint." }), shippingNotes.map((note) => {
1410
+ ? "Pakbon en verzendbrief zijn naar de printer gestuurd."
1411
+ : autoSendMessage(autoSend, siteTz) ?? "De verzendbrief is nog niet geprint." }), shippingNotes.map((note) => {
1259
1412
  const message = shippingNoteMessage(note.state);
1260
1413
  const failed = note.state.status === "mislukt" || note.state.status === "geblokkeerd";
1261
1414
  const waiting = isWaitingShippingNote(note.state.status);
1262
1415
  const typeLabel = shippingNoteSourceLabel(note.state);
1263
- return (_jsxs("div", { className: "flex items-start gap-3", children: [failed ? (_jsx(AlertTriangle, { className: "mt-0.5 h-5 w-5 shrink-0 text-destructive" })) : note.state.status === "geprint" ? (_jsx(CheckCircle2, { className: "mt-0.5 h-5 w-5 shrink-0 text-primary" })) : (_jsx(Loader2, { className: "mt-0.5 h-5 w-5 shrink-0 animate-spin text-muted-foreground" })), _jsxs("div", { className: "min-w-0", children: [(note.cartLabel || typeLabel) && (_jsx("p", { className: "truncate text-sm text-muted-foreground", children: [note.cartLabel, typeLabel].filter(Boolean).join(" · ") })), _jsx("p", { className: failed ? "text-sm font-medium text-destructive" : "text-sm text-foreground", children: message ?? "Verzendbriefstatus wordt opgehaald." }), waiting && (note.state.attempts !== null || note.state.nextAttemptAt) && (_jsxs("p", { className: "text-xs text-muted-foreground", children: [note.state.attempts !== null ? `Pogingen: ${note.state.attempts}` : "", note.state.attempts !== null && note.state.nextAttemptAt ? " · " : "", note.state.nextAttemptAt
1264
- ? `Volgende poging: ${new Date(note.state.nextAttemptAt).toLocaleTimeString("nl-NL", { hour: "2-digit", minute: "2-digit" })}`
1416
+ return (_jsxs("div", { className: "flex items-start gap-3", children: [failed ? (_jsx(AlertTriangle, { className: "mt-0.5 h-5 w-5 shrink-0 text-destructive" })) : note.state.status === "geprint" ? (_jsx(CheckCircle2, { className: "mt-0.5 h-5 w-5 shrink-0 text-primary" })) : (_jsx(Loader2, { className: "mt-0.5 h-5 w-5 shrink-0 animate-spin text-muted-foreground" })), _jsxs("div", { className: "min-w-0", children: [(note.cartLabel || typeLabel) && (_jsx("p", { className: "truncate text-sm text-muted-foreground", children: [note.cartLabel, typeLabel].filter(Boolean).join(" · ") })), _jsx("p", { className: failed ? "text-sm font-medium text-destructive" : "text-sm text-foreground", children: (note.state.status !== "geprint" && autoSendMessage(autoSend, siteTz)) ||
1417
+ (message ?? "Verzendbriefstatus wordt opgehaald.") }), waiting && (note.state.attempts !== null || note.state.nextAttemptAt) && (_jsxs("p", { className: "text-xs text-muted-foreground", children: [note.state.attempts !== null ? `Pogingen: ${note.state.attempts}` : "", note.state.attempts !== null && note.state.nextAttemptAt ? " · " : "", note.state.nextAttemptAt
1418
+ ? `Volgende poging: ${formatSiteTime(note.state.nextAttemptAt, siteTz)}`
1265
1419
  : ""] }))] })] }, note.jobId));
1266
- })] }) })), _jsx(Card, { children: _jsxs(CardContent, { className: "flex flex-wrap items-center gap-3 p-3 sm:p-4", children: [_jsxs("div", { className: "relative w-full min-w-0 flex-1 sm:min-w-64", children: [_jsx(Search, { 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: t("search"), value: searchInput, onChange: (e) => setSearchInput(e.target.value) })] }), _jsxs(Select, { value: chosenDay ?? "current", onValueChange: (v) => void chooseDay(v), children: [_jsx(SelectTrigger, { className: "h-12 w-full sm:w-56", disabled: loadingDay, children: _jsx(SelectValue, { placeholder: ui.pickDay }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "current", children: ui.pickDayToday }), safePickDays.map((d) => (_jsxs(SelectItem, { value: d.day, children: [dayLabel(d.day), d.orders > 0 ? ` (${d.orders})` : ""] }, d.day)))] })] }), _jsxs(Select, { value: filters.documentTypeId ?? "all", onValueChange: (v) => setFilters((f) => ({ ...f, documentTypeId: v === "all" ? null : v, page: 0 })), children: [_jsx(SelectTrigger, { className: "h-12 w-full sm:w-52", children: _jsx(SelectValue, { placeholder: ui.allDocumentTypes }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "all", children: ui.allDocumentTypes }), (Array.isArray(documentTypes) ? documentTypes : []).map((dt) => (_jsx(SelectItem, { value: dt.id, children: dt.name }, dt.id)))] })] }), _jsx(Button, { variant: filters.onlyPending ? "default" : "outline", className: "h-12", onClick: () => setFilters((f) => ({ ...f, onlyPending: !f.onlyPending, page: 0 })), children: ui.onlyPending }), _jsxs(Button, { variant: filters.urgentOnly ? "default" : "outline", className: "h-12", onClick: () => setFilters((f) => ({ ...f, urgentOnly: !f.urgentOnly, page: 0 })), children: [_jsx(Zap, { className: "mr-2 h-4 w-4" }), ui.onlyUrgent, urgentCount > 0 ? ` (${urgentCount})` : ""] })] }) }), !realtimeOk && (_jsx("p", { className: "text-sm text-muted-foreground", children: ui.connectionLost })), cartsPerPrint != null && (_jsx("div", { className: "flex justify-end md:hidden", children: _jsxs(Button, { className: "h-12", disabled: loading || batchPrinting || (busyId != null || actionBusy), onClick: () => void printNextCarts(), children: [batchPrinting ? (_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" })) : (_jsx(Printer, { className: "mr-2 h-4 w-4" })), "Print eerstvolgende ", cartsPerPrint, " karren"] }) })), _jsx("div", { className: "md:hidden", children: _jsxs(Button, { variant: "outline", className: "h-12 w-full", disabled: fetchingNotes, onClick: () => void fetchShippingNotes(), children: [fetchingNotes ? (_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" })) : (_jsx(FileDown, { className: "mr-2 h-4 w-4" })), fetchingNotes ? "Ophalen…" : "Verzendbrieven ophalen"] }) }), loading ? (_jsxs("div", { className: "flex items-center justify-center py-16 text-muted-foreground", children: [_jsx(Loader2, { className: "mr-2 h-5 w-5 animate-spin" }), " ", t("loadingQueue")] })) : (_jsx(QueueTable, { items: items, lockedIds: lockedIds, statuses: Array.isArray(statuses) ? statuses : [], documentTypes: Array.isArray(documentTypes) ? documentTypes : [], busyId: busyId, readinessById: readinessById, waitingIds: waitingQueueIds, onPick: handlePick, onReprint: handleReprintLocked, onPrintAll: printAllForItem, onSelectPrinted: selectPrintedForReprint, onMarkPrinted: markPrinted, onPreview: setPreviewItem, t: t })), _jsxs("div", { className: "flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between", children: [_jsxs("span", { className: "text-center text-sm text-muted-foreground sm:text-left", children: [t("page"), " ", filters.page + 1, " ", t("of"), " ", totalPages] }), _jsxs("div", { className: "grid grid-cols-2 gap-2 sm:flex", children: [_jsx(Button, { variant: "outline", className: "h-12 w-full sm:w-auto", disabled: filters.page === 0, onClick: () => setFilters((f) => ({ ...f, page: Math.max(0, f.page - 1) })), children: t("previous") }), _jsx(Button, { variant: "outline", className: "h-12 w-full sm:w-auto", disabled: filters.page + 1 >= totalPages, onClick: () => setFilters((f) => ({ ...f, page: f.page + 1 })), children: t("next") })] })] }), _jsx("div", { className: "fixed inset-x-0 bottom-0 z-40 border-t bg-background/95 p-3 pb-[max(0.75rem,env(safe-area-inset-bottom))] backdrop-blur md:hidden", children: _jsxs(Button, { size: "lg", className: "h-14 w-full text-lg", onClick: () => (pending ? confirmPending() : fetchNextLocked()), disabled: (busyId !== null || actionBusy) || fetchingData, children: [(busyId !== null || actionBusy) || fetchingData ? (_jsx(Loader2, { className: "mr-2 h-5 w-5 animate-spin" })) : pending && !readiness.ready ? (_jsx(RefreshCw, { className: "mr-2 h-5 w-5" })) : (_jsx(Printer, { className: "mr-2 h-5 w-5" })), _jsxs("span", { className: "truncate", children: [pending && !readiness.ready ? "Pakbon ophalen" : t("printNext"), pending?.document_number ? ` · ${pending.document_number}` : ""] })] }) }), _jsx(PreviewDialog, { item: previewItem, stationSlug: station.slug, onClose: () => setPreviewItem(null) })] }));
1420
+ })] }) })), _jsxs(Button, { variant: "outline", size: "lg", className: "h-12 w-full sm:hidden", "aria-expanded": filtersOpen, onClick: () => setFiltersOpen((v) => !v), children: [_jsx(Search, { className: "mr-2 h-4 w-4" }), " ", ui.filters] }), _jsx(Card, { className: filtersOpen ? "" : "hidden sm:block", children: _jsxs(CardContent, { className: "flex flex-wrap items-center gap-3 p-3 sm:p-4", children: [_jsxs("div", { className: "relative w-full min-w-0 flex-1 sm:min-w-64", children: [_jsx(Search, { 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: t("search"), value: searchInput, onChange: (e) => setSearchInput(e.target.value) })] }), _jsxs(Select, { value: chosenDay ?? "current", onValueChange: (v) => void chooseDay(v), children: [_jsx(SelectTrigger, { className: "h-12 w-full sm:w-56", disabled: loadingDay, children: _jsx(SelectValue, { placeholder: ui.pickDay }) }), _jsxs(SelectContent, { className: "z-[100] bg-popover opacity-100", children: [_jsx(SelectItem, { value: "current", children: ui.pickDayToday }), safePickDays.map((d) => (_jsxs(SelectItem, { value: d.day, children: [dayLabel(d.day), d.orders > 0 ? ` (${d.orders})` : ""] }, d.day)))] })] }), _jsxs(Select, { value: filters.documentTypeId ?? "all", onValueChange: (v) => setFilters((f) => ({ ...f, documentTypeId: v === "all" ? null : v, page: 0 })), children: [_jsx(SelectTrigger, { className: "h-12 w-full sm:w-52", children: _jsx(SelectValue, { placeholder: ui.allDocumentTypes }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "all", children: ui.allDocumentTypes }), (Array.isArray(documentTypes) ? documentTypes : []).map((dt) => (_jsx(SelectItem, { value: dt.id, children: dt.name }, dt.id)))] })] }), _jsx(Button, { variant: filters.onlyPending ? "default" : "outline", className: "h-12", onClick: () => setFilters((f) => ({ ...f, onlyPending: !f.onlyPending, page: 0 })), children: ui.onlyPending }), _jsxs(Button, { variant: filters.urgentOnly ? "default" : "outline", className: "h-12", onClick: () => setFilters((f) => ({ ...f, urgentOnly: !f.urgentOnly, page: 0 })), children: [_jsx(Zap, { className: "mr-2 h-4 w-4" }), ui.onlyUrgent, urgentCount > 0 ? ` (${urgentCount})` : ""] })] }) }), !realtimeOk && (_jsx("p", { className: "text-sm text-muted-foreground", children: ui.connectionLost })), cartsPerPrint != null && (_jsx("div", { className: "flex justify-end md:hidden", children: _jsxs(Button, { className: "h-12", disabled: loading || batchPrinting || (busyId != null || actionBusy), onClick: () => void printNextCarts(), children: [batchPrinting ? (_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" })) : (_jsx(Printer, { className: "mr-2 h-4 w-4" })), "Volgende printen (", cartsPerPrint, " karren)"] }) })), _jsx("div", { className: "md:hidden", children: _jsxs(Button, { variant: "outline", className: "h-12 w-full", disabled: fetchingNotes, onClick: () => void fetchShippingNotes(), children: [fetchingNotes ? (_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" })) : (_jsx(FileDown, { className: "mr-2 h-4 w-4" })), fetchingNotes ? "Ophalen…" : "Verzendbrieven ophalen"] }) }), loading ? (_jsxs("div", { className: "flex items-center justify-center py-16 text-muted-foreground", children: [_jsx(Loader2, { className: "mr-2 h-5 w-5 animate-spin" }), " ", t("loadingQueue")] })) : (_jsxs(_Fragment, { children: [earlierRows.length > 0 && (_jsxs("section", { className: "mb-4 rounded-lg border border-primary/40 bg-muted/40 p-3", "aria-label": "Nog niet gescand van eerdere dagen", children: [_jsxs("h3", { className: "mb-2 text-sm font-semibold text-foreground", children: ["Nog niet gescand van eerdere dagen (", earlierRows.length, ")"] }), _jsx("ul", { className: "divide-y divide-border", children: earlierRows.map((row) => (_jsxs("li", { className: "flex flex-wrap items-center justify-between gap-2 py-2", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "truncate font-medium", children: row.document_number ?? row.order_number ?? "Pakbon" }), _jsxs("div", { className: "text-xs text-muted-foreground", children: [pickDayShortLabel(pickDayOf(row)), row.customer_name ? ` · ${row.customer_name}` : ""] })] }), _jsx(Button, { size: "sm", variant: "outline", disabled: busyId !== null || actionBusy, onClick: async () => {
1421
+ await printAllForItem(row);
1422
+ void refetchEarlier();
1423
+ }, children: "Opnieuw printen" })] }, row.id))) })] })), _jsx(QueueTable, { items: items, lockedIds: lockedIds, statuses: Array.isArray(statuses) ? statuses : [], documentTypes: Array.isArray(documentTypes) ? documentTypes : [], busyId: busyId, readinessById: readinessById, siteTimeZone: siteTz, pickDay: chosenDay ?? pickDay ?? null, waitingIds: waitingQueueIds, onPick: handlePick, onReprint: handleReprintLocked, onPrintAll: printAllForItem, onSelectPrinted: selectPrintedForReprint, onMarkPrinted: markPrinted, onPreview: setPreviewItem, t: t })] })), _jsxs("div", { className: "flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between", children: [_jsxs("span", { className: "text-center text-sm text-muted-foreground sm:text-left", children: [t("page"), " ", filters.page + 1, " ", t("of"), " ", totalPages] }), _jsxs("div", { className: "grid grid-cols-2 gap-2 sm:flex", children: [_jsx(Button, { variant: "outline", className: "h-12 w-full sm:w-auto", disabled: filters.page === 0, onClick: () => setFilters((f) => ({ ...f, page: Math.max(0, f.page - 1) })), children: t("previous") }), _jsx(Button, { variant: "outline", className: "h-12 w-full sm:w-auto", disabled: filters.page + 1 >= totalPages, onClick: () => setFilters((f) => ({ ...f, page: f.page + 1 })), children: t("next") })] })] }), _jsx("div", { className: "fixed inset-x-0 bottom-0 z-40 border-t bg-background/95 p-3 pb-[max(0.75rem,env(safe-area-inset-bottom))] backdrop-blur md:hidden", children: _jsxs(Button, { size: "lg", className: "h-14 w-full text-lg", onClick: () => (pending ? confirmPending() : fetchNextLocked()), disabled: (busyId !== null || actionBusy) || fetchingData, children: [(busyId !== null || actionBusy) || fetchingData ? (_jsx(Loader2, { className: "mr-2 h-5 w-5 animate-spin" })) : pending && !readiness.ready ? (_jsx(RefreshCw, { className: "mr-2 h-5 w-5" })) : (_jsx(Printer, { className: "mr-2 h-5 w-5" })), _jsxs("span", { className: "truncate", children: [pending && !readiness.ready ? "Pakbon ophalen" : t("printNext"), pending?.document_number ? ` · ${pending.document_number}` : ""] })] }) }), _jsx(PreviewDialog, { item: previewItem, stationSlug: station.slug, onClose: () => setPreviewItem(null) })] }));
1267
1424
  }
@@ -11,7 +11,16 @@ interface Props {
11
11
  ready: boolean;
12
12
  reason: string | null;
13
13
  replaced?: boolean;
14
+ mismatch?: boolean;
15
+ waitUntil?: string | null;
16
+ notPrintable?: boolean;
17
+ notStarted?: string | null;
18
+ lateSignup?: boolean;
14
19
  }>;
20
+ /** Gekozen pickdag (YYYY-MM-DD), voor de vertrekdag. */
21
+ pickDay?: string | null;
22
+ /** Tijdzone van de vestiging; null = tijden onbekend. */
23
+ siteTimeZone?: string | null;
15
24
  /** Rijen met een printopdracht die nog niet voltooid is. */
16
25
  waitingIds?: Set<string>;
17
26
  onPick: (item: PickQueueItem) => void;
@@ -23,6 +32,6 @@ interface Props {
23
32
  readOnly?: boolean;
24
33
  t?: PickTranslator;
25
34
  }
26
- export declare function QueueTable({ items, lockedIds, statuses, documentTypes, busyId, readinessById, waitingIds, onPick, onReprint, onPrintAll, onSelectPrinted, onMarkPrinted, onPreview, readOnly, t: translator, }: Props): import("react").JSX.Element;
35
+ export declare function QueueTable({ items, lockedIds, statuses, documentTypes, busyId, readinessById, waitingIds, onPick, onReprint, onPrintAll, onSelectPrinted, onMarkPrinted, onPreview, readOnly, t: translator, siteTimeZone, pickDay, }: Props): import("react").JSX.Element;
27
36
  export {};
28
37
  //# 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;AAIvF,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,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAChH,4DAA4D;IAC5D,UAAU,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACzB,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,aAAa,EACb,UAAU,EACV,MAAM,EACN,SAAS,EACT,UAAU,EACV,eAAe,EACf,aAAa,EACb,SAAS,EAGT,QAAQ,EACR,CAAC,EAAE,UAAU,GACd,EAAE,KAAK,+BA6cP"}
1
+ {"version":3,"file":"QueueTable.d.ts","sourceRoot":"","sources":["../../../src/components/pick/QueueTable.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAMvF,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,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,YAAY,CAAC,EAAE,OAAO,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,UAAU,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACzO,wDAAwD;IACxD,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,yDAAyD;IACzD,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,4DAA4D;IAC5D,UAAU,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACzB,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;AA8DD,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,SAAS,EACT,QAAQ,EACR,aAAa,EACb,MAAM,EACN,aAAa,EACb,UAAU,EACV,MAAM,EACN,SAAS,EACT,UAAU,EACV,eAAe,EACf,aAAa,EACb,SAAS,EAGT,QAAQ,EACR,CAAC,EAAE,UAAU,EACb,YAAmB,EACnB,OAAc,GACf,EAAE,KAAK,+BAqfP"}