@flowselections/pick-schermen-kwekers 1.0.62 → 1.0.64

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/dist-lib/components/pick/PickScherm.d.ts.map +1 -1
  2. package/dist-lib/components/pick/PickScherm.js +18 -7
  3. package/dist-lib/components/pick/QueueTable.d.ts +5 -1
  4. package/dist-lib/components/pick/QueueTable.d.ts.map +1 -1
  5. package/dist-lib/components/pick/QueueTable.js +14 -31
  6. package/dist-lib/hooks/usePickPrintStatus.d.ts +14 -0
  7. package/dist-lib/hooks/usePickPrintStatus.d.ts.map +1 -0
  8. package/dist-lib/hooks/usePickPrintStatus.js +47 -0
  9. package/dist-lib/hooks/usePickQueue.d.ts +2 -0
  10. package/dist-lib/hooks/usePickQueue.d.ts.map +1 -1
  11. package/dist-lib/hooks/usePickQueue.js +29 -8
  12. package/dist-lib/integrations/supabase/types.d.ts +504 -2
  13. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  14. package/dist-lib/integrations/supabase/types.js +1 -0
  15. package/dist-lib/lib/__tests__/pick143.test.js +1 -1
  16. package/dist-lib/lib/__tests__/pick163.test.d.ts +2 -0
  17. package/dist-lib/lib/__tests__/pick163.test.d.ts.map +1 -0
  18. package/dist-lib/lib/__tests__/pick163.test.js +68 -0
  19. package/dist-lib/lib/pick143.d.ts +7 -2
  20. package/dist-lib/lib/pick143.d.ts.map +1 -1
  21. package/dist-lib/lib/pick143.js +24 -5
  22. package/dist-lib/lib/pick159.js +1 -1
  23. package/dist-lib/lib/pickPerf.d.ts +11 -0
  24. package/dist-lib/lib/pickPerf.d.ts.map +1 -0
  25. package/dist-lib/lib/pickPerf.js +35 -0
  26. package/dist-lib/lib/pickPrintStatus.d.ts +36 -0
  27. package/dist-lib/lib/pickPrintStatus.d.ts.map +1 -0
  28. package/dist-lib/lib/pickPrintStatus.js +98 -0
  29. package/dist-lib/lib/pickRowState.d.ts +2 -1
  30. package/dist-lib/lib/pickRowState.d.ts.map +1 -1
  31. package/dist-lib/lib/pickRowState.js +4 -2
  32. package/package.json +1 -1
@@ -1 +1 @@
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;AA2D7B,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,+BAyqEpD"}
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;AA6D7B,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,+BA0rEpD"}
@@ -21,6 +21,8 @@ import { getPickTranslator } from "../../lib/pickI18n.js";
21
21
  import { friendlyPickError, getPickUiTexts } from "../../lib/pickUiText.js";
22
22
  import { getPrintJobTexts } from "../../lib/pickPrintJobText.js";
23
23
  import { defaultFilters, usePickDays, usePickHeartbeat, usePickQueue, } from "../../hooks/usePickQueue.js";
24
+ import { usePickPrintStatus } from "../../hooks/usePickPrintStatus.js";
25
+ import { activeDeviceKey } from "../../lib/pick159.js";
24
26
  import { usePickDocumentTypes, usePickStatuses, useStationPrintPlek, } from "../../hooks/usePickConfig.js";
25
27
  import { usePrintBatch, usePrintJob } from "../../hooks/usePrintJob.js";
26
28
  import { useShippingNoteStatus } from "../../hooks/useShippingNoteStatus.js";
@@ -219,12 +221,17 @@ export function PickScherm({ station, onBack }) {
219
221
  return () => clearTimeout(timer);
220
222
  }, [searchInput]);
221
223
  const effectiveFilters = useMemo(() => ({ ...filters, country: null, sortRules: [] }), [filters]);
222
- const { items, total, loading, lockedIds, refresh, pendingCount, printedCount, urgentCount, realtimeOk, pickDay, rebuiltAt, syncError, } = usePickQueue({
224
+ const { items, total, loading, lockedIds, refresh, pendingCount, printedCount, countsLoading, urgentCount, realtimeOk, pickDay, rebuiltAt, syncError, } = usePickQueue({
223
225
  stationId: station.id,
224
226
  filters: effectiveFilters,
225
227
  enabled: lang !== null,
226
228
  pickDayOverride: chosenDay,
227
229
  });
230
+ // 1.0.63: printstatus uit de Engine, één aanroep voor alle regels in beeld.
231
+ const printStatusIds = useMemo(() => (Array.isArray(items) ? items : []).map((i) => i.id), [items]);
232
+ const { result: printStatus, loading: printStatusLoading, inPrint, } = usePickPrintStatus(printStatusIds, activeDeviceKey() ? station.slug ?? null : null, lang !== null);
233
+ const inPrintRef = useRef(inPrint);
234
+ inPrintRef.current = inPrint;
228
235
  /** Geprint maar nog niet gescand, van een eerdere pickdag (blijft open tot de scan). */
229
236
  const viewDay = chosenDay ?? pickDay ?? null;
230
237
  const { data: earlierRaw, refetch: refetchEarlier } = useQuery({
@@ -667,6 +674,10 @@ export function PickScherm({ station, onBack }) {
667
674
  toast.warning(block);
668
675
  return;
669
676
  }
677
+ if (inPrintRef.current.has(item.id)) {
678
+ toast.info("Deze pakbon wordt al geprint. Wacht tot de printer klaar is.");
679
+ return;
680
+ }
670
681
  setBusyId(item.id);
671
682
  try {
672
683
  const claimed = await pickApi.claim({
@@ -918,7 +929,7 @@ export function PickScherm({ station, onBack }) {
918
929
  return;
919
930
  setBatchPrinting(true);
920
931
  try {
921
- const visible = (Array.isArray(items) ? items : []).filter((item) => !item.printed_at);
932
+ const visible = (Array.isArray(items) ? items : []).filter((item) => !item.printed_at && !inPrintRef.current.has(item.id));
922
933
  const targets = [];
923
934
  const skipped = [];
924
935
  for (const item of visible) {
@@ -972,7 +983,7 @@ export function PickScherm({ station, onBack }) {
972
983
  setBusyId("next");
973
984
  let claimed = null;
974
985
  try {
975
- const target = firstVisibleTarget(itemsRef.current, lockedIds);
986
+ const target = firstVisibleTarget(itemsRef.current, lockedIds, inPrintRef.current);
976
987
  const filtered = !!filters.search.trim() || !!filters.documentTypeId || filters.urgentOnly || !!filters.country;
977
988
  if (target) {
978
989
  // De rij die in beeld staat (ook met zoekfilter), niet de bovenste van de hele wachtrij.
@@ -1366,7 +1377,7 @@ export function PickScherm({ station, onBack }) {
1366
1377
  ? "Pickwachtrij laden…"
1367
1378
  : checkDone > 0
1368
1379
  ? `Pakbonnen controleren (${checkDone} van ${checkTotal})…`
1369
- : `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: () => {
1380
+ : `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: [countsLoading ? "…" : pendingCount, " ", t("statusToPrint"), " \u00B7 ", countsLoading ? "…" : 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 || printStatusLoading || 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 || (!pending && printStatusLoading), 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: () => {
1370
1381
  setCartMissing(false);
1371
1382
  setRowGone(false);
1372
1383
  setPending(null);
@@ -1378,7 +1389,7 @@ export function PickScherm({ station, onBack }) {
1378
1389
  const cart = plannedCart ?? { cartId: r.cartId, cartLabel: r.cartLabel ?? r.cartId };
1379
1390
  setLastCartResults((prev) => prev.filter((x) => x.cartId !== r.cartId));
1380
1391
  void reprintCart(lastPrinted, cart);
1381
- }, 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(VroegeStickerWaarschuwing, { stationId: station.id }), _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
1392
+ }, 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(VroegeStickerWaarschuwing, { stationId: station.id }), _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 || (!pending && printStatusLoading), 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
1382
1393
  ? "De pakbon wordt opgevraagd. Zodra alles compleet is, verschijnt de printknop weer."
1383
1394
  : planReason
1384
1395
  ? `${planReason} Klik op "Pakbon ophalen" om hem op te vragen.`
@@ -1434,8 +1445,8 @@ export function PickScherm({ station, onBack }) {
1434
1445
  (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
1435
1446
  ? `Volgende poging: ${formatSiteTime(note.state.nextAttemptAt, siteTz)}`
1436
1447
  : ""] }))] })] }, note.jobId));
1437
- })] }) })), _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 () => {
1448
+ })] }) })), _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 || printStatusLoading || 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 () => {
1438
1449
  await printAllForItem(row);
1439
1450
  void refetchEarlier();
1440
- }, children: "Opnieuw printen" })] }, row.id))) })] })), _jsx(QueueTable, { items: items.filter((item) => readinessById[item.id]?.replaced !== true), 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) })] }));
1451
+ }, children: "Opnieuw printen" })] }, row.id))) })] })), _jsx(QueueTable, { items: items.filter((item) => readinessById[item.id]?.replaced !== true), lockedIds: lockedIds, statuses: Array.isArray(statuses) ? statuses : [], documentTypes: Array.isArray(documentTypes) ? documentTypes : [], busyId: busyId, readinessById: readinessById, siteTimeZone: siteTz, pickDay: chosenDay ?? pickDay ?? null, waitingIds: waitingQueueIds, printStatus: printStatus, printStatusLoading: printStatusLoading, 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 || (!pending && printStatusLoading), 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) })] }));
1441
1452
  }
@@ -1,4 +1,5 @@
1
1
  import type { PickDocumentType, PickQueueItem, PickStatus } from "../../lib/pickTypes.js";
2
+ import { type PrintStatusResult } from "../../lib/pickPrintStatus.js";
2
3
  import { type PickTranslator } from "../../lib/pickI18n.js";
3
4
  interface Props {
4
5
  items: PickQueueItem[];
@@ -23,6 +24,9 @@ interface Props {
23
24
  siteTimeZone?: string | null;
24
25
  /** Rijen met een printopdracht die nog niet voltooid is. */
25
26
  waitingIds?: Set<string>;
27
+ /** 1.0.63: printstatus uit de Engine (één aanroep per scherm). */
28
+ printStatus?: PrintStatusResult | null;
29
+ printStatusLoading?: boolean;
26
30
  onPick: (item: PickQueueItem) => void;
27
31
  onReprint?: (item: PickQueueItem) => void;
28
32
  onPrintAll?: (item: PickQueueItem) => void;
@@ -32,6 +36,6 @@ interface Props {
32
36
  readOnly?: boolean;
33
37
  t?: PickTranslator;
34
38
  }
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;
39
+ export declare function QueueTable({ items, lockedIds, statuses, documentTypes, busyId, readinessById, waitingIds, printStatus, printStatusLoading, onPick, onReprint, onPrintAll, onSelectPrinted, onMarkPrinted, onPreview, readOnly, t: translator, siteTimeZone, pickDay, }: Props): import("react").JSX.Element;
36
40
  export {};
37
41
  //# sourceMappingURL=QueueTable.d.ts.map
@@ -1 +1 @@
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;AAUvF,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,+BAggBP"}
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;AAKvF,OAAO,EAA+B,KAAK,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAGhG,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,kEAAkE;IAClE,WAAW,CAAC,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACvC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,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,WAAkB,EAClB,kBAA0B,EAC1B,MAAM,EACN,SAAS,EACT,UAAU,EACV,eAAe,EACf,aAAa,EACb,SAAS,EAGT,QAAQ,EACR,CAAC,EAAE,UAAU,EACb,YAAmB,EACnB,OAAc,GACf,EAAE,KAAK,+BA2fP"}
@@ -5,9 +5,8 @@ import { Badge, Button, Table, TableBody, TableCell, TableHead, TableHeader, Tab
5
5
  import { AlertTriangle, Check, Eye, Loader2, Lock, Printer, RefreshCw, RotateCcw } from "lucide-react";
6
6
  import { cartColumnInfo, comparePickQueue, dedupeByUnit, displayCartCount, displayPageCount, sinceLabel, unitTitleOf } from "../../lib/pickTypes.js";
7
7
  import { duplicateNameTag } from "../../lib/pickRowState.js";
8
- import { useQuery } from "@tanstack/react-query";
9
- import { pickApi } from "../../lib/pickApi.js";
10
- import { PRINT_MANUAL, PRINT_NOT_NEEDED_HINT, printStage, printStageLabel } from "../../lib/pick142.js";
8
+ import { PRINT_MANUAL } from "../../lib/pick142.js";
9
+ import { isInPrint, printStatusLabel } from "../../lib/pickPrintStatus.js";
11
10
  import { CART_PLAN_MISMATCH_MESSAGE, orderNumbersLabel } from "../../lib/pick142.js";
12
11
  import { isRowPrintable, rowWaitMessage, rowWaitOf } from "../../lib/pick143.js";
13
12
  import { getPickTranslator } from "../../lib/pickI18n.js";
@@ -70,21 +69,8 @@ function priorityTone(priority, urgent) {
70
69
  return "bg-secondary text-secondary-foreground";
71
70
  return "bg-muted text-muted-foreground";
72
71
  }
73
- export function QueueTable({ items, lockedIds, statuses, documentTypes, busyId, readinessById, waitingIds, onPick, onReprint, onPrintAll, onSelectPrinted, onMarkPrinted, onPreview, readOnly, t: translator, siteTimeZone = null, pickDay = null, }) {
72
+ export function QueueTable({ items, lockedIds, statuses, documentTypes, busyId, readinessById, waitingIds, printStatus = null, printStatusLoading = false, onPick, onReprint, onPrintAll, onSelectPrinted, onMarkPrinted, onPreview, readOnly, t: translator, siteTimeZone = null, pickDay = null, }) {
74
73
  const t = translator ?? getPickTranslator("nl");
75
- // Echte printstatus per regel uit de printopdrachten (één aanvraag, ververst mee).
76
- const jobIds = Array.from(new Set((Array.isArray(items) ? items : []).filter((i) => i.printed_at && i.print_job_id).map((i) => i.print_job_id))).sort();
77
- const jobsQuery = useQuery({
78
- queryKey: ["pick", "rowPrintJobs", jobIds.join(",")],
79
- enabled: jobIds.length > 0,
80
- queryFn: () => pickApi.printJobsStatus(jobIds),
81
- refetchInterval: (q) => {
82
- const data = Array.isArray(q.state.data) ? q.state.data : [];
83
- return data.some((j) => printStage(j) === "rendering") ? 5000 : 30000;
84
- },
85
- });
86
- const jobsLoaded = jobsQuery.isSuccess;
87
- const jobsById = new Map((Array.isArray(jobsQuery.data) ? jobsQuery.data : []).map((j) => [j.id, j]));
88
74
  // Volgorde identiek aan de keuze van "volgende uitprinten" (zie
89
75
  // comparePickQueue): niet-geprinte regels bovenaan, en bij gelijke stand op
90
76
  // kar-nummer, zodat K1 boven K2 boven K3 staat en het printen van boven naar
@@ -117,7 +103,7 @@ export function QueueTable({ items, lockedIds, statuses, documentTypes, busyId,
117
103
  }, children: [_jsxs(TableCell, { className: "font-medium", children: [_jsxs("div", { className: "flex items-center gap-2", children: [unitTitleOf(item), dupTag && _jsxs("span", { className: "text-xs font-normal text-muted-foreground", children: ["(", dupTag, ")"] }), replaced && (_jsx("span", { className: "rounded bg-destructive/10 px-1.5 py-0.5 text-xs font-semibold text-destructive", title: readiness?.reason ?? undefined, children: "Vervangen" })), replaced && readiness?.reason && (_jsx("span", { className: "text-xs font-normal text-destructive", children: readiness.reason })), waiting && (_jsx("span", { className: "rounded bg-muted px-1.5 py-0.5 text-xs font-semibold text-muted-foreground", children: "Wacht op printer" })), sentElsewhereLines(item).map((line) => (_jsx("span", { className: "text-xs font-normal text-muted-foreground", children: line }, line))), warnings.length > 0 && (_jsx(AlertTriangle, { className: "h-4 w-4 text-destructive", "aria-label": t("warning") }))] }), _jsx("div", { className: "text-xs text-muted-foreground", children: doc?.name ?? "-" })] }), _jsx(TableCell, { children: orderNumbersLabel(item) }), _jsx(TableCell, { children: item.customer_name }), _jsx(TableCell, { className: "text-right", children: item.line_count }), _jsx(TableCell, { className: "text-right", children: (() => {
118
104
  const info = cartColumnInfo(item);
119
105
  return (_jsxs("div", { className: "flex flex-col items-end", children: [_jsx("span", { children: info.carts }), info.cartLabel && (_jsx("span", { className: "text-xs text-muted-foreground", children: info.cartLabel })), info.reason && (_jsx("span", { className: "text-xs text-muted-foreground", children: info.reason }))] }));
120
- })() }), _jsx(TableCell, { className: "text-right", children: printedSlipCount(item) ?? "—" }), _jsx(TableCell, { className: "text-right", children: displayPageCount(item.line_count) }), _jsxs(TableCell, { children: [_jsx(RowPrintBadge, { printed: printed, hasJob: !!item.print_job_id, job: item.print_job_id ? jobsById.get(item.print_job_id) : undefined, jobsLoaded: jobsLoaded, toPrintLabel: t("statusToPrint") }), printed && (_jsx("div", { className: "mt-1 text-xs text-muted-foreground", children: new Date(item.printed_at).toLocaleTimeString([], {
106
+ })() }), _jsx(TableCell, { className: "text-right", children: printedSlipCount(item) ?? "—" }), _jsx(TableCell, { className: "text-right", children: displayPageCount(item.line_count) }), _jsxs(TableCell, { children: [_jsx(EnginePrintBadge, { item: item, result: printStatus, loading: printStatusLoading }), printed && (_jsx("div", { className: "mt-1 text-xs text-muted-foreground", children: new Date(item.printed_at).toLocaleTimeString([], {
121
107
  hour: "2-digit",
122
108
  minute: "2-digit",
123
109
  }) }))] }), _jsx(TableCell, { children: _jsx("span", { className: "rounded px-2 py-1 text-xs font-semibold " +
@@ -134,7 +120,7 @@ export function QueueTable({ items, lockedIds, statuses, documentTypes, busyId,
134
120
  }, children: _jsx(Check, { className: "h-5 w-5" }) })), blockedMessage(readinessById[item.id], siteTimeZone, item) && !printed ? (_jsx("span", { className: "max-w-56 text-right text-xs font-medium text-destructive", children: blockedMessage(readinessById[item.id], siteTimeZone, item) })) : printed ? (_jsxs(Button, { size: "sm", variant: "ghost", disabled: readOnly || busyId === item.id || checking || replaced, onClick: (e) => {
135
121
  e.stopPropagation();
136
122
  (onPrintAll ?? onReprint ?? onPick)(item);
137
- }, children: [checking ? (_jsx(Loader2, { className: "mr-1 h-4 w-4 animate-spin" })) : ready ? (_jsx(RotateCcw, { className: "mr-1 h-4 w-4" })) : (_jsx(RefreshCw, { className: "mr-1 h-4 w-4" })), replaced ? "Vervangen" : checking ? "Pakbon controleren" : ready ? t("print") : "Pakbon ophalen"] })) : locked ? (_jsxs("span", { className: "inline-flex items-center gap-1 text-xs text-muted-foreground", children: [_jsx(Lock, { className: "h-3.5 w-3.5" }), " ", t("inProgress")] })) : (_jsxs(Button, { size: "sm", variant: "outline", disabled: readOnly || busyId === item.id || checking || replaced, onClick: (e) => {
123
+ }, children: [checking ? (_jsx(Loader2, { className: "mr-1 h-4 w-4 animate-spin" })) : ready ? (_jsx(RotateCcw, { className: "mr-1 h-4 w-4" })) : (_jsx(RefreshCw, { className: "mr-1 h-4 w-4" })), replaced ? "Vervangen" : checking ? "Pakbon controleren" : ready ? t("print") : "Pakbon ophalen"] })) : isInPrint(printStatus?.byId.get(item.id)) || (printStatusLoading && !printed) ? (_jsxs(Button, { size: "sm", variant: "outline", disabled: true, onClick: (e) => e.stopPropagation(), children: [_jsx(Loader2, { className: "mr-1 h-4 w-4 animate-spin" }), printStatusLoading ? "…" : "Wordt geprint"] })) : locked ? (_jsxs("span", { className: "inline-flex items-center gap-1 text-xs text-muted-foreground", children: [_jsx(Lock, { className: "h-3.5 w-3.5" }), " ", t("inProgress")] })) : (_jsxs(Button, { size: "sm", variant: "outline", disabled: readOnly || busyId === item.id || checking || replaced, onClick: (e) => {
138
124
  e.stopPropagation();
139
125
  (onPrintAll ?? onPick)(item);
140
126
  }, children: [checking ? (_jsx(Loader2, { className: "mr-1 h-4 w-4 animate-spin" })) : !ready ? (_jsx(RefreshCw, { className: "mr-1 h-4 w-4" })) : (_jsx(Printer, { className: "mr-1 h-4 w-4" })), replaced ? "Vervangen" : checking ? "Pakbon controleren" : ready ? t("print") : "Pakbon ophalen"] }))] }) })] }, item.id));
@@ -161,7 +147,7 @@ export function QueueTable({ items, lockedIds, statuses, documentTypes, busyId,
161
147
  }, children: [_jsxs("div", { className: "grid grid-cols-[minmax(0,1fr)_auto] items-start gap-3", children: [_jsxs("div", { className: "min-w-0", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [_jsxs("span", { className: "truncate text-lg font-bold text-foreground", children: [unitTitleOf(item), dupTag && _jsxs("span", { className: "text-xs font-normal text-muted-foreground", children: ["(", dupTag, ")"] }), replaced && (_jsx("span", { className: "rounded bg-destructive/10 px-1.5 py-0.5 text-xs font-semibold text-destructive", title: readiness?.reason ?? undefined, children: "Vervangen" })), replaced && readiness?.reason && (_jsx("span", { className: "text-xs font-normal text-destructive", children: readiness.reason })), waiting && (_jsx("span", { className: "rounded bg-muted px-1.5 py-0.5 text-xs font-semibold text-muted-foreground", children: "Wacht op printer" }))] }), warnings.length > 0 && (_jsx(AlertTriangle, { className: "h-4 w-4 shrink-0 text-destructive", "aria-label": t("warning") }))] }), _jsx("p", { className: "truncate text-xs text-muted-foreground", children: doc?.name ?? "-" })] }), _jsxs("div", { className: "flex shrink-0 items-start gap-1", children: [onPreview && (_jsx("button", { type: "button", "aria-label": "Voorbeeld " + item.document_number, title: "Voorbeeld " + item.document_number, className: "inline-flex h-11 w-11 shrink-0 items-center justify-center rounded-md text-muted-foreground hover:bg-muted hover:text-foreground", onClick: (e) => {
162
148
  e.stopPropagation();
163
149
  onPreview(item);
164
- }, children: _jsx(Eye, { className: "h-5 w-5" }) })), _jsxs("div", { className: "flex shrink-0 flex-col items-end gap-1", children: [_jsx(RowPrintBadge, { printed: printed, hasJob: !!item.print_job_id, job: item.print_job_id ? jobsById.get(item.print_job_id) : undefined, jobsLoaded: jobsLoaded, toPrintLabel: t("statusToPrint") }), printed && (_jsx("span", { className: "text-xs text-muted-foreground", children: new Date(item.printed_at).toLocaleTimeString([], {
150
+ }, children: _jsx(Eye, { className: "h-5 w-5" }) })), _jsxs("div", { className: "flex shrink-0 flex-col items-end gap-1", children: [_jsx(EnginePrintBadge, { item: item, result: printStatus, loading: printStatusLoading }), printed && (_jsx("span", { className: "text-xs text-muted-foreground", children: new Date(item.printed_at).toLocaleTimeString([], {
165
151
  hour: "2-digit",
166
152
  minute: "2-digit",
167
153
  }) })), _jsx("span", { className: "rounded px-2 py-0.5 text-xs font-semibold " +
@@ -185,7 +171,7 @@ export function QueueTable({ items, lockedIds, statuses, documentTypes, busyId,
185
171
  })(), _jsx(CartSteps, { printed: printed, waiting: waiting }), _jsxs("div", { className: "mt-3", children: [blockedMessage(readiness, siteTimeZone, item) && !printed ? (_jsx("p", { className: "rounded-md border border-destructive/40 bg-destructive/5 p-3 text-sm font-medium text-destructive", children: blockedMessage(readiness, siteTimeZone, item) })) : printed ? (_jsxs(Button, { size: "lg", variant: "outline", className: "h-12 w-full", disabled: readOnly || busyId === item.id || checking || replaced, onClick: (e) => {
186
172
  e.stopPropagation();
187
173
  (onPrintAll ?? onReprint ?? onPick)(item);
188
- }, children: [checking ? (_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" })) : ready ? (_jsx(RotateCcw, { className: "mr-2 h-4 w-4" })) : (_jsx(RefreshCw, { className: "mr-2 h-4 w-4" })), replaced ? "Vervangen" : checking ? "Pakbon controleren" : ready ? t("print") : "Pakbon ophalen"] })) : locked ? (_jsxs("span", { className: "inline-flex items-center gap-1 text-xs text-muted-foreground", children: [_jsx(Lock, { className: "h-3.5 w-3.5" }), " ", t("inProgress")] })) : (_jsxs(Button, { size: "lg", className: "h-12 w-full", disabled: readOnly || busyId === item.id || checking || replaced, onClick: (e) => {
174
+ }, children: [checking ? (_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" })) : ready ? (_jsx(RotateCcw, { className: "mr-2 h-4 w-4" })) : (_jsx(RefreshCw, { className: "mr-2 h-4 w-4" })), replaced ? "Vervangen" : checking ? "Pakbon controleren" : ready ? t("print") : "Pakbon ophalen"] })) : isInPrint(printStatus?.byId.get(item.id)) || (printStatusLoading && !printed) ? (_jsxs(Button, { size: "sm", variant: "outline", disabled: true, onClick: (e) => e.stopPropagation(), children: [_jsx(Loader2, { className: "mr-1 h-4 w-4 animate-spin" }), printStatusLoading ? "…" : "Wordt geprint"] })) : locked ? (_jsxs("span", { className: "inline-flex items-center gap-1 text-xs text-muted-foreground", children: [_jsx(Lock, { className: "h-3.5 w-3.5" }), " ", t("inProgress")] })) : (_jsxs(Button, { size: "lg", className: "h-12 w-full", disabled: readOnly || busyId === item.id || checking || replaced, onClick: (e) => {
189
175
  e.stopPropagation();
190
176
  (onPrintAll ?? onPick)(item);
191
177
  }, children: [checking ? (_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" })) : !ready ? (_jsx(RefreshCw, { className: "mr-2 h-4 w-4" })) : (_jsx(Printer, { className: "mr-2 h-4 w-4" })), replaced ? "Vervangen" : checking ? "Pakbon controleren" : ready ? t("print") : "Pakbon ophalen"] })), !printed && onMarkPrinted && (_jsxs("details", { className: "mt-2 text-sm", onClick: (e) => e.stopPropagation(), children: [_jsx("summary", { className: "cursor-pointer select-none text-muted-foreground", children: "Meer" }), _jsxs(Button, { size: "sm", variant: "ghost", className: "mt-1 w-full", disabled: readOnly || busyId === item.id, onClick: (e) => {
@@ -196,16 +182,13 @@ export function QueueTable({ items, lockedIds, statuses, documentTypes, busyId,
196
182
  }), safeItems.length === 0 && (_jsx("div", { className: "rounded-lg border py-10 text-center text-muted-foreground", children: t("emptyQueue") }))] })] }));
197
183
  }
198
184
  /** Statuslabel van een regel: alleen de echte stand uit de printopdracht, nooit een aanname. */
199
- function RowPrintBadge({ printed, hasJob, job, jobsLoaded, toPrintLabel }) {
200
- if (!printed)
201
- return _jsx(Badge, { variant: "default", children: toPrintLabel });
202
- if (!hasJob)
185
+ function EnginePrintBadge({ item, result, loading }) {
186
+ const status = result?.byId.get(item.id);
187
+ // Handmatig gemarkeerd: geprint zonder printopdracht.
188
+ if (!loading && status?.code === "geen" && item.printed_at && !item.print_job_id) {
203
189
  return _jsx(Badge, { variant: "secondary", children: PRINT_MANUAL });
204
- if (!job && !jobsLoaded)
205
- return _jsx(Badge, { variant: "secondary", children: "Status laden\u2026" });
206
- const stage = printStage(job);
207
- if (stage === "not_needed") {
208
- return _jsx(Badge, { variant: "outline", className: "text-muted-foreground", title: PRINT_NOT_NEEDED_HINT, children: printStageLabel(job) });
209
190
  }
210
- return (_jsx(Badge, { variant: stage === "failed" ? "destructive" : "secondary", children: printStageLabel(job) }));
191
+ const l = printStatusLabel({ status, loading: loading && !result, beschikbaar: result ? result.beschikbaar : true, reden: result?.reden ?? null });
192
+ const variant = l.tone === "failed" ? "destructive" : l.tone === "todo" ? "default" : l.tone === "unknown" ? "outline" : "secondary";
193
+ return (_jsxs("div", { className: "flex flex-col items-start gap-0.5", children: [_jsx(Badge, { variant: variant, title: l.reason ?? undefined, children: l.label }), l.reason && _jsx("span", { className: "max-w-56 text-xs text-muted-foreground", children: l.reason })] }));
211
194
  }
@@ -0,0 +1,14 @@
1
+ import { type PrintStatusResult } from "../lib/pickPrintStatus.js";
2
+ /**
3
+ * Eén aanroep voor alle zichtbare regels. Openbaar station (slug):
4
+ * pickstation_print_status; ingelogd scherm: logistics_print_status direct.
5
+ * Fout: "niet beschikbaar" met reden, nooit terugvallen op printed_at.
6
+ */
7
+ export declare function fetchPrintStatus(ids: string[], slug: string | null): Promise<PrintStatusResult>;
8
+ export declare function usePickPrintStatus(ids: string[], slug: string | null, enabled?: boolean): {
9
+ result: PrintStatusResult | null;
10
+ loading: boolean;
11
+ inPrint: Set<string>;
12
+ refetch: (options?: import("@tanstack/query-core").RefetchOptions) => Promise<import("@tanstack/query-core").QueryObserverResult<PrintStatusResult, Error>>;
13
+ };
14
+ //# sourceMappingURL=usePickPrintStatus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usePickPrintStatus.d.ts","sourceRoot":"","sources":["../../src/hooks/usePickPrintStatus.ts"],"names":[],"mappings":"AAIA,OAAO,EAAgC,KAAK,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAQ9F;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAgBrG;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,UAAO;;;;;EAkBpF"}
@@ -0,0 +1,47 @@
1
+ import { useMemo } from "react";
2
+ import { useQuery } from "@tanstack/react-query";
3
+ import { supabase } from "@flowselections/core";
4
+ import { keyedRpc } from "../lib/pick159.js";
5
+ import { inPrintIds, parsePrintStatus } from "../lib/pickPrintStatus.js";
6
+ import { pickMeasure } from "../lib/pickPerf.js";
7
+ const db = keyedRpc(supabase);
8
+ const MAX_IDS = 500;
9
+ /**
10
+ * Eén aanroep voor alle zichtbare regels. Openbaar station (slug):
11
+ * pickstation_print_status; ingelogd scherm: logistics_print_status direct.
12
+ * Fout: "niet beschikbaar" met reden, nooit terugvallen op printed_at.
13
+ */
14
+ export async function fetchPrintStatus(ids, slug) {
15
+ const list = ids.slice(0, MAX_IDS);
16
+ const { data, error } = await pickMeasure("printstatus", () => slug
17
+ ? db.rpc("pickstation_print_status", { p_slug: slug, p_queue_item_ids: list })
18
+ : db.rpc("logistics_print_status", { p_queue_item_ids: list }));
19
+ if (error) {
20
+ const missing = /could not find|does not exist|PGRST202/i.test(error.message);
21
+ return {
22
+ beschikbaar: false,
23
+ reden: missing ? "Printstatus nog niet beschikbaar: de Logistiek Engine is te oud." : "Printstatus kon niet worden opgehaald.",
24
+ byId: new Map(),
25
+ };
26
+ }
27
+ return parsePrintStatus(data);
28
+ }
29
+ export function usePickPrintStatus(ids, slug, enabled = true) {
30
+ const sorted = useMemo(() => Array.from(new Set(ids)).sort(), [ids]);
31
+ const q = useQuery({
32
+ queryKey: ["pick", "printStatus", slug ?? "-", sorted.join(",")],
33
+ enabled: enabled && sorted.length > 0,
34
+ queryFn: () => fetchPrintStatus(sorted, slug),
35
+ refetchInterval: (query) => {
36
+ const d = query.state.data;
37
+ return d && inPrintIds(d).size > 0 ? 3000 : 20000;
38
+ },
39
+ });
40
+ const inPrint = useMemo(() => inPrintIds(q.data), [q.data]);
41
+ return {
42
+ result: q.data ?? null,
43
+ loading: sorted.length > 0 && q.data === undefined,
44
+ inPrint,
45
+ refetch: q.refetch,
46
+ };
47
+ }
@@ -29,6 +29,8 @@ export declare function usePickQueue(args: {
29
29
  pendingCount: number;
30
30
  printedCount: number;
31
31
  urgentCount: number;
32
+ /** Tellingen nog niet geladen: kopregel toont "…". */
33
+ countsLoading: boolean;
32
34
  loading: boolean;
33
35
  error: string | null;
34
36
  lockedIds: Set<string>;
@@ -1 +1 @@
1
- {"version":3,"file":"usePickQueue.d.ts","sourceRoot":"","sources":["../../src/hooks/usePickQueue.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAY,aAAa,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAG9E,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,UAAU,EAAE,OAAO,CAAC;IACpB,mDAAmD;IACnD,WAAW,EAAE,OAAO,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,YAAY,EAAE,CAAC;CAC3B;AAED,eAAO,MAAM,cAAc,EAAE,YAS5B,CAAC;AAEF;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,YAAY,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,2EAA2E;IAC3E,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;;;;;;;;;;;IAoMG,gEAAgE;;IAEhE,6EAA6E;;;EAIhF;AAED,qEAAqE;AACrE,wBAAgB,gBAAgB,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,EAAE,SAAS,CAAC,EAAE,MAAM;;EAuBhG;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,iFAOnF;AACD,iEAAiE;AACjE,wBAAgB,WAAW,CAAC,OAAO,UAAO;;;YAQzC;AAED,+EAA+E;AAC/E,wBAAgB,cAAc,CAAC,CAAC,EAAE,SAAS,OAAO,EAAE,EAAE,CAAC,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAEpF"}
1
+ {"version":3,"file":"usePickQueue.d.ts","sourceRoot":"","sources":["../../src/hooks/usePickQueue.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAY,aAAa,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAK9E,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,UAAU,EAAE,OAAO,CAAC;IACpB,mDAAmD;IACnD,WAAW,EAAE,OAAO,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,YAAY,EAAE,CAAC;CAC3B;AAED,eAAO,MAAM,cAAc,EAAE,YAS5B,CAAC;AAEF;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,YAAY,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,2EAA2E;IAC3E,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;;;;;;;IAqNG,sDAAsD;;;;;;IAMtD,gEAAgE;;IAEhE,6EAA6E;;;EAIhF;AAED,qEAAqE;AACrE,wBAAgB,gBAAgB,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,EAAE,SAAS,CAAC,EAAE,MAAM;;EAuBhG;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,iFAOnF;AACD,iEAAiE;AACjE,wBAAgB,WAAW,CAAC,OAAO,UAAO;;;YAQzC;AAED,+EAA+E;AAC/E,wBAAgB,cAAc,CAAC,CAAC,EAAE,SAAS,OAAO,EAAE,EAAE,CAAC,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAEpF"}
@@ -1,7 +1,9 @@
1
1
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
2
- import { keepPreviousData, useQuery, useQueryClient } from "@tanstack/react-query";
2
+ import { useQuery, useQueryClient } from "@tanstack/react-query";
3
3
  import { pickApi, pickDb } from "../lib/pickApi.js";
4
4
  import { safeArray } from "../lib/pickTypes.js";
5
+ import { isRowOpen } from "../lib/pick143.js";
6
+ import { pickMark, pickMeasure } from "../lib/pickPerf.js";
5
7
  export const defaultFilters = {
6
8
  country: null,
7
9
  search: "",
@@ -64,7 +66,7 @@ export function usePickQueue(args) {
64
66
  const pickDayQuery = useQuery({
65
67
  queryKey: ["pick", "pickDay"],
66
68
  enabled: enabled && !syncQuery.isLoading,
67
- queryFn: () => pickApi.currentPickDay(),
69
+ queryFn: () => pickMeasure("pickdag", () => pickApi.currentPickDay()),
68
70
  staleTime: 30000,
69
71
  });
70
72
  const pickDay = pickDayOverride ?? pickDayQuery.data ?? null;
@@ -96,7 +98,7 @@ export function usePickQueue(args) {
96
98
  queryKey: key,
97
99
  // Pas laden als de pickdag bekend is, anders flitst een oude lijst.
98
100
  enabled: enabled && (pickDayOverride != null || pickDayQuery.isFetched),
99
- queryFn: () => pickApi.queue({
101
+ queryFn: () => pickMeasure("wachtrij", () => pickApi.queue({
100
102
  stationId,
101
103
  screenId,
102
104
  country: filters.country,
@@ -108,15 +110,16 @@ export function usePickQueue(args) {
108
110
  sortRules: filters.sortRules,
109
111
  page: filters.page,
110
112
  pageSize: filters.pageSize,
111
- }),
113
+ })),
112
114
  // Vorige lijst alleen vasthouden bij filteren/bladeren binnen hetzelfde station en dezelfde pickdag.
113
115
  placeholderData: (prev, prevQuery) => (prevQuery && sameQueueScope(prevQuery.queryKey, key) ? prev : undefined),
114
116
  });
115
117
  const countsQuery = useQuery({
116
118
  queryKey: ["pick", "dayCounts", stationId ?? "-", screenId ?? "-", pickDay ?? "-"],
117
- enabled: enabled && !pickDayQuery.isLoading,
118
- queryFn: () => pickApi.dayCounts({ stationId, screenId, pickDay }),
119
- placeholderData: keepPreviousData,
119
+ // 1.0.63: pas tellen als de pickdag bekend is (anders telt de database alle
120
+ // dagen en springt de kopregel), en geen oude telling van een andere dag tonen.
121
+ enabled: enabled && pickDay != null,
122
+ queryFn: () => pickMeasure("dagtelling", () => pickApi.dayCounts({ stationId, screenId, pickDay })),
120
123
  });
121
124
  // Locks komen primair via realtime binnen; deze poll is alleen terugval.
122
125
  const locksQuery = useQuery({
@@ -131,6 +134,7 @@ export function usePickQueue(args) {
131
134
  queryClient.invalidateQueries({ queryKey: ["pick", "dayCounts"] });
132
135
  queryClient.invalidateQueries({ queryKey: ["pick", "countryCounts"] });
133
136
  queryClient.invalidateQueries({ queryKey: ["pick", "dashboard"] });
137
+ queryClient.invalidateQueries({ queryKey: ["pick", "printStatus"] });
134
138
  }, [queryClient]);
135
139
  /**
136
140
  * Realtime-meldingen komen bij een Engine-sync in tientallen tegelijk binnen.
@@ -175,14 +179,31 @@ export function usePickQueue(args) {
175
179
  pickDb.removeChannel(channel);
176
180
  };
177
181
  }, [enabled, stationId, invalidate]);
182
+ // Laadtijd: van openen tot de eerste lijst binnen is.
183
+ useEffect(() => {
184
+ if (enabled)
185
+ pickMark("start");
186
+ }, [enabled]);
187
+ const firstReady = useRef(false);
188
+ useEffect(() => {
189
+ if (!firstReady.current && queueQuery.isSuccess) {
190
+ firstReady.current = true;
191
+ pickMark("lijst-klaar");
192
+ }
193
+ }, [queueQuery.isSuccess]);
194
+ // Afgeronde of geannuleerde rijen (bv. na een kar-vervanging) nooit tonen,
195
+ // ook niet uit de vastgehouden vorige lijst.
196
+ const openItems = useMemo(() => safeArray(queueQuery.data?.items).filter((i) => isRowOpen(i)), [queueQuery.data]);
178
197
  const lockedIds = useMemo(() => new Set(safeArray(locksQuery.data).map((l) => l.queue_item_id)), [locksQuery.data]);
179
198
  return {
180
- items: safeArray(queueQuery.data?.items),
199
+ items: openItems,
181
200
  total: queueQuery.data?.total ?? 0,
182
201
  pickDay,
183
202
  pendingCount: countsQuery.data?.pending ?? 0,
184
203
  printedCount: countsQuery.data?.printed ?? 0,
185
204
  urgentCount: countsQuery.data?.urgent ?? 0,
205
+ /** Tellingen nog niet geladen: kopregel toont "…". */
206
+ countsLoading: countsQuery.data === undefined,
186
207
  loading: queueQuery.isPending,
187
208
  error: queueQuery.error instanceof Error ? queueQuery.error.message : null,
188
209
  lockedIds,