@flowselections/pick-schermen-kwekers 1.0.82 → 1.0.87

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 (45) hide show
  1. package/dist-lib/components/fs-ui/DataTable.js +2 -2
  2. package/dist-lib/components/fs-ui/TableSkeleton.js +1 -1
  3. package/dist-lib/components/pick/PickScherm.d.ts.map +1 -1
  4. package/dist-lib/components/pick/PickScherm.js +377 -159
  5. package/dist-lib/components/pick/werkvloer/Werkvloer.d.ts +52 -0
  6. package/dist-lib/components/pick/werkvloer/Werkvloer.d.ts.map +1 -0
  7. package/dist-lib/components/pick/werkvloer/Werkvloer.js +33 -0
  8. package/dist-lib/components/settings/ReferentiedataPickdocumentenCard.d.ts.map +1 -1
  9. package/dist-lib/components/settings/ReferentiedataPickdocumentenCard.js +10 -3
  10. package/dist-lib/hooks/usePickPrintStatus.d.ts +1 -1
  11. package/dist-lib/hooks/usePickPrintStatus.d.ts.map +1 -1
  12. package/dist-lib/hooks/usePickPrintStatus.js +4 -4
  13. package/dist-lib/hooks/useShippingNoteStatus.d.ts +1 -1
  14. package/dist-lib/hooks/useShippingNoteStatus.d.ts.map +1 -1
  15. package/dist-lib/hooks/useShippingNoteStatus.js +11 -9
  16. package/dist-lib/hooks/useWerkvloer.d.ts +20 -0
  17. package/dist-lib/hooks/useWerkvloer.d.ts.map +1 -0
  18. package/dist-lib/hooks/useWerkvloer.js +98 -0
  19. package/dist-lib/integrations/supabase/types.d.ts +69 -0
  20. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  21. package/dist-lib/lib/__tests__/pickPublicStatus.test.d.ts +2 -0
  22. package/dist-lib/lib/__tests__/pickPublicStatus.test.d.ts.map +1 -0
  23. package/dist-lib/lib/__tests__/pickPublicStatus.test.js +48 -0
  24. package/dist-lib/lib/labels.d.ts +2 -0
  25. package/dist-lib/lib/labels.d.ts.map +1 -1
  26. package/dist-lib/lib/labels.js +41 -0
  27. package/dist-lib/lib/pick142.d.ts.map +1 -1
  28. package/dist-lib/lib/pick142.js +7 -10
  29. package/dist-lib/lib/pick156.d.ts.map +1 -1
  30. package/dist-lib/lib/pick156.js +6 -20
  31. package/dist-lib/lib/pick159.js +1 -1
  32. package/dist-lib/lib/pickApi.d.ts +20 -0
  33. package/dist-lib/lib/pickApi.d.ts.map +1 -1
  34. package/dist-lib/lib/pickApi.js +27 -0
  35. package/dist-lib/lib/shippingNoteStatus.d.ts +1 -1
  36. package/dist-lib/lib/shippingNoteStatus.d.ts.map +1 -1
  37. package/dist-lib/lib/shippingNoteStatus.js +5 -2
  38. package/dist-lib/lib/werkvloer.d.ts +40 -0
  39. package/dist-lib/lib/werkvloer.d.ts.map +1 -0
  40. package/dist-lib/lib/werkvloer.js +121 -0
  41. package/dist-lib/lib/werkvloer.test.d.ts +2 -0
  42. package/dist-lib/lib/werkvloer.test.d.ts.map +1 -0
  43. package/dist-lib/lib/werkvloer.test.js +49 -0
  44. package/package.json +2 -2
  45. package/schema/pickschermen.sql +176 -0
@@ -1,42 +1,39 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
3
- import { showSyncingNotice } from "../../lib/pickLoadOrder.js";
4
3
  import { useQuery } from "@tanstack/react-query";
5
- import { Badge, Button, Card, CardContent, Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, toast, } from "@flowselections/core";
6
- import { AlertTriangle, ArrowLeft, CheckCircle2, ChevronLeft, ChevronRight, FileDown, Globe, Loader2, Printer, RefreshCw, Search, WifiOff, Zap, } from "lucide-react";
4
+ import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, Button, Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, Input, toast, } from "@flowselections/core";
5
+ import { ArrowLeft, ChevronLeft, ChevronRight, FileDown, Globe, Loader2, MoreHorizontal, Printer, Search, } from "lucide-react";
7
6
  import { LanguageTiles } from "./LanguageTiles.js";
8
- import { QueueTable } from "./QueueTable.js";
9
7
  import { isRowPrintable, pickDayOf, pickDayShortLabel, printBlockMessage, rowWaitOf, setKnownSiteZone } from "../../lib/pick143.js";
10
8
  import { isPickWindowNotStarted } from "../../lib/pick144.js";
11
9
  import { isForcedFor, lateSignupOf, releaseInfoOf, releaseMessage } from "../../lib/pick145.js";
12
10
  import { PreviewDialog } from "./PreviewDialog.js";
13
11
  import { ShippingNoteScan } from "./ShippingNoteScan.js";
12
+ import { MeldingsPlek, WerkvloerKaart } from "./werkvloer/Werkvloer.js";
13
+ import { useBrowserOnline, useIdle, useStableList } from "../../hooks/useWerkvloer.js";
14
+ import { label, wv } from "../../lib/labels.js";
15
+ import { ACCEPTED_STATUSES, cardSummary, groupCards, partialFailText, reprintQuestion, sentText, todoCountText, } from "../../lib/werkvloer.js";
14
16
  import { getSessionId, pickApi } from "../../lib/pickApi.js";
15
- import { sendLogisticsEvent } from "../../lib/logisticsApi.js";
16
- import { CART_PLAN_MISMATCH_MESSAGE, formatSiteTime, WORKDAYS_UNKNOWN, autoSendMessage, fetchAutoSend, isPickWorkday, mismatchReason, nextWorkday, printErrorText, PRINT_FAILED_HINT, printStageLabel, siteTimeZone, siteToday, } from "../../lib/pick142.js";
17
+ import { CART_PLAN_MISMATCH_MESSAGE, WORKDAYS_UNKNOWN, fetchAutoSend, isPickWorkday, mismatchReason, nextWorkday, printErrorText, PRINT_FAILED_HINT, siteTimeZone, siteToday, } from "../../lib/pick142.js";
17
18
  import { completeLogisticsTaskForItem, documentTypeOf } from "../../lib/pickPrint.js";
18
19
  import { cartRowOf, isPdfOnlyFailure, pickReadiness, printedCartIdsOf, pdfSaveWarning, unitRefOf, statusRefOf, unitTitleOf, } from "../../lib/pickTypes.js";
19
20
  import { rowFingerprint, rowsToVerify } from "../../lib/pickVerify.js";
20
21
  import { VERIFY_SLOW_MESSAGE, VERIFY_SLOW_TOAST_ID, verifyDelayMs } from "../../lib/pickBackoff.js";
21
- import { alreadyPrintedResults, CART_MISSING_MESSAGE, cartResultLine, cartsForRow, isCartMissingError, reprintCartsIndividually, resettableJobs, runExclusive, resultsFromBatch, resultsFromBatchError, summarizeCartResults, trackableJobs, } from "../../lib/pickCartResults.js";
22
+ import { alreadyPrintedResults, CART_MISSING_MESSAGE, cartsForRow, isCartMissingError, reprintCartsIndividually, resettableJobs, runExclusive, resultsFromBatch, resultsFromBatchError, summarizeCartResults, trackableJobs, } from "../../lib/pickCartResults.js";
22
23
  import { getPickTranslator } from "../../lib/pickI18n.js";
23
24
  import { friendlyPickError, getPickUiTexts } from "../../lib/pickUiText.js";
24
25
  import { getPrintJobTexts } from "../../lib/pickPrintJobText.js";
25
26
  import { defaultFilters, usePickDays, usePickHeartbeat, usePickQueue, } from "../../hooks/usePickQueue.js";
26
27
  import { usePickPrintStatus } from "../../hooks/usePickPrintStatus.js";
27
28
  import { useCartStickerStates } from "../../hooks/useCartStickerStates.js";
28
- import { activeDeviceKey } from "../../lib/pick159.js";
29
29
  import { usePickDocumentTypes, usePickStatuses, useStationPrintPlek, } from "../../hooks/usePickConfig.js";
30
30
  import { usePrintBatch, usePrintJob } from "../../hooks/usePrintJob.js";
31
31
  import { useShippingNoteStatus } from "../../hooks/useShippingNoteStatus.js";
32
- import { firstVisibleTarget, isJobFinished, isRowGoneError, NO_ROW_IN_FILTER_MESSAGE, RELEASE_PENDING_MESSAGE, ROW_GONE_MESSAGE, stuckMessage, stuckMinutesOf, } from "../../lib/pickRowState.js";
33
- import { CartItemRows, PlanWarnings } from "./CartItemRows.js";
34
- import { CartStickerState, OrderStickerMeldingen, StickerFitMeldingen, VroegeStickerWaarschuwing } from "./StickerState.js";
35
- import { StickerReprintButton } from "./StickerReprintButton.js";
32
+ import { firstVisibleTarget, isJobFinished, isRowGoneError, NO_ROW_IN_FILTER_MESSAGE, RELEASE_PENDING_MESSAGE, ROW_GONE_MESSAGE, } from "../../lib/pickRowState.js";
33
+ import { VroegeStickerWaarschuwing } from "./StickerState.js";
36
34
  import { isJobFailed } from "../../lib/pick142.js";
37
- import { isPrintBlocked } from "../../lib/pick156.js";
38
- import { DocumentChoiceMelding, useDocumentChoice } from "./DocumentChoiceMelding.js";
39
- import { isWaitingShippingNote, shippingNoteMessage, shippingNoteSourceLabel, } from "../../lib/shippingNoteStatus.js";
35
+ import { useDocumentChoice } from "./DocumentChoiceMelding.js";
36
+ const cardKeyFn = (c) => c.key;
40
37
  const LANG_STORAGE_KEY = "pick.language";
41
38
  /** Taal per station op dit apparaat. */
42
39
  export const langStorageKey = (stationId) => `${LANG_STORAGE_KEY}.${stationId}`;
@@ -141,7 +138,7 @@ export function PickScherm({ station, onBack }) {
141
138
  const { data: printPlek, isLoading: plekLoading } = useStationPrintPlek(station.id);
142
139
  const { tracked, track: trackJobRaw, clear: clearJob } = usePrintJob();
143
140
  const { batch, trackBatch: trackBatchRaw, clearBatch } = usePrintBatch();
144
- const { shippingNotes, watchShippingNotes, clearShippingNotes, refreshShippingNotesNow, } = useShippingNoteStatus();
141
+ const { shippingNotes, watchShippingNotes, clearShippingNotes, refreshShippingNotesNow, } = useShippingNoteStatus(station.slug ?? null);
145
142
  const finalizedBatch = useRef(null);
146
143
  const [fetchingNotes, setFetchingNotes] = useState(false);
147
144
  /**
@@ -236,7 +233,7 @@ export function PickScherm({ station, onBack }) {
236
233
  });
237
234
  // 1.0.63: printstatus uit de Engine, één aanroep voor alle regels in beeld.
238
235
  const printStatusIds = useMemo(() => (Array.isArray(items) ? items : []).map((i) => i.id), [items]);
239
- const { result: printStatus, loading: printStatusLoading, inPrint, } = usePickPrintStatus(printStatusIds, activeDeviceKey() ? station.slug ?? null : null, lang !== null);
236
+ const { result: printStatus, loading: printStatusLoading, inPrint, } = usePickPrintStatus(printStatusIds, station.slug ?? null, lang !== null);
240
237
  const inPrintRef = useRef(inPrint);
241
238
  inPrintRef.current = inPrint;
242
239
  // 1.0.65: stickerstand van alle karren van de order in één aanroep (openbaar station).
@@ -515,30 +512,12 @@ export function PickScherm({ station, onBack }) {
515
512
  */
516
513
  const requestData = useCallback(async (item) => {
517
514
  setFetchingData(true);
518
- const ref = unitRefOf(item);
519
515
  try {
520
516
  try {
521
517
  await pickApi.reportMissingCartPlans();
522
518
  }
523
- catch {
524
- /* bewaking is aanvullend, nooit blokkerend */
525
- }
526
- if (ref) {
527
- try {
528
- await sendLogisticsEvent({
529
- event_type: "order.changed",
530
- order_ref: ref,
531
- shadow: false,
532
- payload: {
533
- reason: "pick.data_incomplete",
534
- missing: pickReadiness(cartPlan, planStatus, item).missing,
535
- queue_item_id: item.id,
536
- },
537
- });
538
- }
539
- catch {
540
- /* het seintje is aanvullend op de bewaking */
541
- }
519
+ catch (error) {
520
+ console.error("Pickstation: ontbrekend karplan kon niet worden gemeld", error);
542
521
  }
543
522
  let lastMismatches = [];
544
523
  for (let attempt = 0; attempt < 20; attempt += 1) {
@@ -586,27 +565,8 @@ export function PickScherm({ station, onBack }) {
586
565
  try {
587
566
  await pickApi.reportMissingCartPlans();
588
567
  }
589
- catch {
590
- /* bewaking is aanvullend */
591
- }
592
- if (ref) {
593
- try {
594
- await sendLogisticsEvent({
595
- event_type: "order.changed",
596
- order_ref: ref,
597
- shadow: false,
598
- payload: {
599
- reason: "pick.cart_plan_mismatch",
600
- detail: reason,
601
- mismatches: lastMismatches,
602
- queue_item_id: item.id,
603
- idempotency_key: `pick.cart_plan_mismatch:${item.id}:${reason}`,
604
- },
605
- });
606
- }
607
- catch {
608
- /* het seintje is aanvullend op de bewaking */
609
- }
568
+ catch (error) {
569
+ console.error("Pickstation: ontbrekend karplan kon niet worden gemeld", error);
610
570
  }
611
571
  return false;
612
572
  }
@@ -828,11 +788,18 @@ export function PickScherm({ station, onBack }) {
828
788
  * Is de regel nog niet printklaar, dan wordt er niet geprint maar worden de
829
789
  * ontbrekende gegevens bij de bron opgevraagd.
830
790
  */
831
- const printAllForItemInner = useCallback(async (item) => {
791
+ const printAllForItemInner = useCallback(async (item, opts) => {
792
+ const quiet = opts?.quiet === true;
793
+ let partial = [];
832
794
  const block = printBlockMessage(item, siteTz);
833
795
  if (block) {
796
+ if (quiet) {
797
+ if (opts?.preClaimed)
798
+ await releaseQuietly(item.id);
799
+ return { results: [], notReady: block };
800
+ }
834
801
  toast.warning(block);
835
- return;
802
+ return { results: [] };
836
803
  }
837
804
  setBusyId(item.id);
838
805
  setCartMissing(false);
@@ -841,17 +808,21 @@ export function PickScherm({ station, onBack }) {
841
808
  const { plan, readiness: state } = await loadCartPlan(item);
842
809
  setPending(item);
843
810
  if (!plan || !state.ready) {
811
+ if (quiet)
812
+ return { results: [], notReady: state.reason ?? NOT_READY_NOTICE };
844
813
  toast.warning(state.reason ?? NOT_READY_NOTICE);
845
814
  await requestData(item);
846
- return;
815
+ return { results: [] };
847
816
  }
848
817
  const carts = cartsForRow(item, plan);
849
818
  const results = await reprintCartsIndividually(carts, (cartId) => pickApi.reprintCart(item.id, sessionId, cartId));
850
819
  setLastPrinted(item);
851
- startBatchTracking(item, results);
852
- showCartResults(results);
820
+ if (!quiet) {
821
+ startBatchTracking(item, results);
822
+ showCartResults(results);
823
+ }
853
824
  refresh();
854
- return;
825
+ return { results, item };
855
826
  }
856
827
  // Ook bij een rij die zojuist als klaar is getoond, direct vóór het
857
828
  // claimen opnieuw bij de Engine controleren. Een verouderde rij kan zo
@@ -859,33 +830,44 @@ export function PickScherm({ station, onBack }) {
859
830
  const preflight = await loadCartPlan(item);
860
831
  setPending(item);
861
832
  if (!preflight.plan || !preflight.readiness.ready) {
833
+ if (quiet) {
834
+ if (opts?.preClaimed)
835
+ await releaseQuietly(item.id);
836
+ return { results: [], notReady: preflight.readiness.reason ?? NOT_READY_NOTICE };
837
+ }
862
838
  toast.warning(preflight.readiness.reason ?? NOT_READY_NOTICE);
863
839
  await requestData(item);
864
- return;
840
+ return { results: [] };
865
841
  }
866
842
  // Rij met eigen kar: alleen die kar, en die moet in het karplan staan.
867
843
  cartsForRow(item, preflight.plan);
868
- const claimed = await pickApi.claim({
869
- itemId: item.id,
870
- stationId: station.id,
871
- screenId: null,
872
- sessionId,
873
- timeout: station.lock_timeout_seconds ?? 120,
874
- });
844
+ // 1.0.85: door pick_claim_order_v1 al vastgezet (zelfde sessie) → niet opnieuw claimen.
845
+ const claimed = opts?.preClaimed
846
+ ? item
847
+ : await pickApi.claim({
848
+ itemId: item.id,
849
+ stationId: station.id,
850
+ screenId: null,
851
+ sessionId,
852
+ timeout: station.lock_timeout_seconds ?? 120,
853
+ });
875
854
  const target = claimed ?? item;
876
855
  const { plan, readiness: state } = await loadCartPlan(target);
877
856
  setPending(target);
878
857
  if (!plan || !state.ready) {
879
- toast.warning(state.reason ?? NOT_READY_NOTICE);
880
858
  await releaseQuietly(target.id);
859
+ if (quiet)
860
+ return { results: [], notReady: state.reason ?? NOT_READY_NOTICE };
861
+ toast.warning(state.reason ?? NOT_READY_NOTICE);
881
862
  await requestData(target);
882
- return;
863
+ return { results: [] };
883
864
  }
884
865
  const already = printedCartIdsOf(target);
885
866
  const todo = cartsForRow(target, plan).filter((cart) => !already.includes(cart.cartId));
886
867
  if (todo.length === 0) {
887
- toast.info("Alle karren van deze rij zijn al geprint.");
888
- return;
868
+ if (!quiet)
869
+ toast.info("Alle karren van deze rij zijn al geprint.");
870
+ return { results: [], item: target, alreadyDone: already.length };
889
871
  }
890
872
  let results;
891
873
  try {
@@ -896,14 +878,25 @@ export function PickScherm({ station, onBack }) {
896
878
  }
897
879
  catch (e) {
898
880
  results = resultsFromBatchError(todo, e, already);
899
- showCartResults(results);
881
+ partial = results;
882
+ if (!quiet)
883
+ showCartResults(results);
900
884
  throw e;
901
885
  }
902
- startBatchTracking(target, results);
903
- showCartResults(results);
886
+ if (!quiet) {
887
+ startBatchTracking(target, results);
888
+ showCartResults(results);
889
+ }
904
890
  refresh();
891
+ return { results, item: target, alreadyDone: already.length };
905
892
  }
906
893
  catch (e) {
894
+ if (quiet) {
895
+ if (!item.printed_at)
896
+ await releaseQuietly(item.id);
897
+ refresh();
898
+ return { results: partial, error: e };
899
+ }
907
900
  if (isCartMissingError(e)) {
908
901
  setCartMissing(true);
909
902
  toast.error(CART_MISSING_MESSAGE);
@@ -923,6 +916,7 @@ export function PickScherm({ station, onBack }) {
923
916
  setActiveCartId(null);
924
917
  }
925
918
  refresh();
919
+ return { results: partial, error: e };
926
920
  }
927
921
  finally {
928
922
  setBusyId(null);
@@ -1376,6 +1370,303 @@ export function PickScherm({ station, onBack }) {
1376
1370
  if (pendingCount === 0)
1377
1371
  autoFetched.current = false;
1378
1372
  }, [pendingCount]);
1373
+ // ===== 1.0.85 werkvloer: één knop, kaarten per order, vaste meldingsplek =====
1374
+ const browserOnline = useBrowserOnline();
1375
+ const isOnline = online && browserOnline;
1376
+ const [tab, setTab] = useState("todo");
1377
+ const [meerOpen, setMeerOpen] = useState(false);
1378
+ const [showLead, setShowLead] = useState(false);
1379
+ const [slot, setSlot] = useState({ kind: "none" });
1380
+ const [orderBusy, setOrderBusy] = useState(false);
1381
+ const [reprintAsk, setReprintAsk] = useState(null);
1382
+ const ownClaimIds = useRef(new Set());
1383
+ const orderLock = useRef(false);
1384
+ const [orderTrack, setOrderTrack] = useState(null);
1385
+ // Tabblad bepaalt server-side of alleen open regels komen.
1386
+ useEffect(() => {
1387
+ setFilters((f) => (f.onlyPending === (tab === "todo") ? f : { ...f, onlyPending: tab === "todo", page: 0 }));
1388
+ }, [tab]);
1389
+ const openRows = useMemo(() => (Array.isArray(items) ? items : []).filter((i) => readinessById[i.id]?.replaced !== true), [items, readinessById]);
1390
+ const todoCards = useMemo(() => groupCards(openRows.filter((i) => !i.printed_at)), [openRows]);
1391
+ const printedCards = useMemo(() => groupCards(openRows.filter((i) => !!i.printed_at)), [openRows]);
1392
+ const cardStateOf = useCallback((card, mode) => {
1393
+ const rows = card.rows;
1394
+ if (rows.some((r) => inPrint.has(r.id)))
1395
+ return { disabledLabel: wv("inPrint"), notReadyReason: null, statusLine: null };
1396
+ if (rows.some((r) => lockedIds.has(r.id) && !ownClaimIds.current.has(r.id)))
1397
+ return { disabledLabel: wv("lockedOther"), notReadyReason: null, statusLine: null };
1398
+ const failed = rows.map((r) => printStatus?.byId.get(r.id)).find((st) => st?.code === "mislukt");
1399
+ const statusLine = failed ? `Vorige print mislukt${failed.melding ? `: ${failed.melding}` : ""}. Druk op Print.` : null;
1400
+ if (mode === "printed")
1401
+ return { disabledLabel: null, notReadyReason: null, statusLine };
1402
+ for (const r of rows) {
1403
+ const block = printBlockMessage(r, siteTz);
1404
+ if (block)
1405
+ return { disabledLabel: null, notReadyReason: block, statusLine };
1406
+ const st = readinessById[r.id];
1407
+ if (!st || st.checking)
1408
+ return { disabledLabel: wv("print"), notReadyReason: null, statusLine };
1409
+ if (!st.ready)
1410
+ return { disabledLabel: null, notReadyReason: st.notStarted ?? st.reason ?? NOT_READY_NOTICE, statusLine };
1411
+ }
1412
+ return { disabledLabel: null, notReadyReason: null, statusLine };
1413
+ }, [inPrint, lockedIds, printStatus, readinessById, siteTz]);
1414
+ const readyCards = useMemo(() => todoCards.filter((c) => { const st = cardStateOf(c, "todo"); return !st.disabledLabel && !st.notReadyReason; }), [todoCards, cardStateOf]);
1415
+ const nextCard = readyCards[0] ?? null;
1416
+ const printerName = printPlek?.naam ?? null;
1417
+ /** Vaste plek: 4 s groen, rood blijft tot de volgende actie of een tik. */
1418
+ useEffect(() => {
1419
+ if (slot.kind !== "success")
1420
+ return;
1421
+ const t = window.setTimeout(() => setSlot((s) => (s.kind === "success" ? { kind: "none" } : s)), 4000);
1422
+ return () => window.clearTimeout(t);
1423
+ }, [slot]);
1424
+ /** Opdrachten van de laatste order volgen tot de printer ze heeft ontvangen of ze klaar zijn. */
1425
+ useEffect(() => {
1426
+ if (!orderTrack || orderTrack.done)
1427
+ return;
1428
+ let stopped = false;
1429
+ const ids = orderTrack.groups.flatMap((g) => g.jobs.map((j) => j.jobId));
1430
+ const read = async () => {
1431
+ let statuses = [];
1432
+ try {
1433
+ statuses = (await pickApi.printJobsStatus(ids));
1434
+ }
1435
+ catch (e) {
1436
+ console.error("[pickscherm] printstatus lezen mislukt", e);
1437
+ return;
1438
+ }
1439
+ if (stopped)
1440
+ return;
1441
+ const byId = new Map(statuses.map((st) => [st.id, st]));
1442
+ setOrderTrack((cur) => {
1443
+ if (!cur || cur.key !== orderTrack.key)
1444
+ return cur;
1445
+ const groups = cur.groups.map((g) => ({
1446
+ ...g,
1447
+ jobs: g.jobs.map((j) => ({ ...j, status: byId.get(j.jobId)?.status ?? j.status, fout: byId.get(j.jobId)?.foutmelding ?? j.fout })),
1448
+ }));
1449
+ return { ...cur, groups };
1450
+ });
1451
+ };
1452
+ void read();
1453
+ const timer = window.setInterval(() => void read(), 2000);
1454
+ return () => {
1455
+ stopped = true;
1456
+ window.clearInterval(timer);
1457
+ };
1458
+ }, [orderTrack?.key, orderTrack?.done]);
1459
+ /** Afronden: ontvangen → groen; mislukt → alleen die karren terug in de lijst, rood vlak. */
1460
+ useEffect(() => {
1461
+ if (!orderTrack || orderTrack.done)
1462
+ return;
1463
+ const jobs = orderTrack.groups.flatMap((g) => g.jobs);
1464
+ const failed = jobs.filter((j) => isJobFailed({ status: j.status }));
1465
+ const final = jobs.every((j) => j.status && (["voltooid", "mislukt", "geannuleerd"].includes(j.status) || ACCEPTED_STATUSES.has(j.status)));
1466
+ const elapsed = Date.now() - orderTrack.startedAt;
1467
+ if (!final) {
1468
+ if (elapsed >= 15000) {
1469
+ setOrderBusy(false);
1470
+ setSlot({
1471
+ kind: "busy",
1472
+ lines: jobs.map((j) => `${j.cartLabel ?? "Kar"}: ${label("printStatus", j.status ?? "wachtend")}`),
1473
+ });
1474
+ }
1475
+ if (elapsed < 15000) {
1476
+ const t = window.setTimeout(() => setOrderTrack((c) => (c ? { ...c } : c)), 15000 - elapsed + 50);
1477
+ return () => window.clearTimeout(t);
1478
+ }
1479
+ return;
1480
+ }
1481
+ setOrderTrack({ ...orderTrack, done: true });
1482
+ setOrderBusy(false);
1483
+ void (async () => {
1484
+ for (const g of orderTrack.groups) {
1485
+ const bad = resettableJobs(g.jobs.filter((j) => isJobFailed({ status: j.status })));
1486
+ if (bad.length > 0) {
1487
+ try {
1488
+ await pickApi.resetPrintJobs(g.queueItemId, sessionId, bad.map((j) => ({ jobId: j.jobId, cartId: j.cartId })));
1489
+ }
1490
+ catch (e) {
1491
+ console.error("[pickscherm] mislukte kar terugzetten mislukt", e);
1492
+ }
1493
+ }
1494
+ else if (g.item) {
1495
+ try {
1496
+ await completeLogisticsTaskForItem(g.item, g.jobs[g.jobs.length - 1]?.jobId ?? null);
1497
+ }
1498
+ catch (e) {
1499
+ console.error("[pickscherm] Engine-taak afronden mislukt", e);
1500
+ }
1501
+ }
1502
+ }
1503
+ refresh();
1504
+ })();
1505
+ const total = orderTrack.totalCarts;
1506
+ const failedCount = failed.length + orderTrack.immediateFailed;
1507
+ if (failedCount > 0) {
1508
+ const reason = printErrorText(failed[0]?.fout ?? null)?.message ?? orderTrack.immediateReason ?? null;
1509
+ setSlot({ kind: "error", text: partialFailText(total - failedCount, total, reason) });
1510
+ }
1511
+ else {
1512
+ setSlot({ kind: "success", text: sentText(printerName, jobs.length) });
1513
+ }
1514
+ }, [orderTrack, sessionId, refresh, printerName]);
1515
+ /**
1516
+ * Eén druk: de order (of de eerste vrije uit `cards`) in één stap vastzetten
1517
+ * op de server, daarna alle karren via de bestaande printroute.
1518
+ */
1519
+ const printOrder = useCallback(async (cards) => {
1520
+ if (orderLock.current || cards.length === 0)
1521
+ return;
1522
+ if (!isOnline) {
1523
+ setSlot({ kind: "offline" });
1524
+ return;
1525
+ }
1526
+ if (!printPlek) {
1527
+ setSlot({ kind: "error", text: wv("noPrinter") });
1528
+ return;
1529
+ }
1530
+ orderLock.current = true;
1531
+ setOrderBusy(true);
1532
+ setSlot({ kind: "none" });
1533
+ const key = typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `${Date.now()}-${Math.random()}`;
1534
+ let keepBusy = false;
1535
+ try {
1536
+ let claim = null;
1537
+ // Haperende verbinding: dezelfde sleutel opnieuw, nooit een tweede claim.
1538
+ for (let attempt = 0; attempt < 3 && !claim; attempt += 1) {
1539
+ try {
1540
+ claim = await pickApi.claimOrder({
1541
+ stationId: station.id,
1542
+ sessionId,
1543
+ unitRefs: cards.map((c) => c.key),
1544
+ idempotencyKey: key,
1545
+ pickDay: selectedDayRef.current,
1546
+ timeout: 120,
1547
+ });
1548
+ }
1549
+ catch (e) {
1550
+ const msg = e instanceof Error ? e.message : String(e);
1551
+ if (attempt < 2 && /fetch|network|timeout|Failed to/i.test(msg)) {
1552
+ await new Promise((r) => setTimeout(r, 1000 * (attempt + 1)));
1553
+ continue;
1554
+ }
1555
+ throw e;
1556
+ }
1557
+ }
1558
+ if (!claim || !claim.ok) {
1559
+ setSlot({ kind: "info", text: claim?.reden ?? wv("noNext") });
1560
+ refresh();
1561
+ return;
1562
+ }
1563
+ const ids = claim.queueItemIds;
1564
+ ids.forEach((id) => ownClaimIds.current.add(id));
1565
+ const byId = new Map(itemsRef.current.map((i) => [i.id, i]));
1566
+ const groups = [];
1567
+ let totalCarts = 0;
1568
+ let immediateFailed = 0;
1569
+ let immediateReason = null;
1570
+ let notReady = null;
1571
+ for (const id of ids) {
1572
+ const row = byId.get(id);
1573
+ if (!row) {
1574
+ // Regel niet in beeld (andere pagina): vrijgeven, komt vanzelf terug.
1575
+ await releaseQuietly(id);
1576
+ continue;
1577
+ }
1578
+ const out = await printAllForItemInner(row, { preClaimed: true, quiet: true });
1579
+ if (out.notReady)
1580
+ notReady = notReady ?? out.notReady;
1581
+ totalCarts += out.results.length + (out.alreadyDone ?? 0);
1582
+ const failedNow = out.results.filter((r) => r.outcome === "error");
1583
+ immediateFailed += failedNow.length;
1584
+ if (out.error && out.results.length === 0) {
1585
+ immediateFailed += 1;
1586
+ totalCarts += 1;
1587
+ }
1588
+ if (out.error || failedNow.length > 0) {
1589
+ const { message } = friendlyPickError(out.error ?? failedNow[0]?.reason ?? null, t.lang);
1590
+ immediateReason = immediateReason ?? message;
1591
+ }
1592
+ const jobs = trackableJobs(out.results).map((j) => ({ ...j, reprint: j.reprint === true, status: null, fout: null }));
1593
+ if (jobs.length > 0)
1594
+ groups.push({ queueItemId: row.id, item: out.item ?? row, jobs });
1595
+ }
1596
+ const sent = groups.reduce((n, g) => n + g.jobs.length, 0);
1597
+ if (sent === 0) {
1598
+ setSlot(immediateFailed > 0
1599
+ ? { kind: "error", text: partialFailText(0, Math.max(totalCarts, 1), immediateReason) }
1600
+ : { kind: "info", text: notReady ?? wv("noNext") });
1601
+ refresh();
1602
+ return;
1603
+ }
1604
+ keepBusy = true;
1605
+ setOrderTrack({ key, startedAt: Date.now(), groups, totalCarts, immediateFailed, immediateReason, done: false });
1606
+ refresh();
1607
+ }
1608
+ catch (e) {
1609
+ console.error("[pickscherm] printen mislukt", e);
1610
+ const { message } = friendlyPickError(e, t.lang);
1611
+ setSlot({ kind: "error", text: `${message} ${wv("callLead")}` });
1612
+ refresh();
1613
+ }
1614
+ finally {
1615
+ orderLock.current = false;
1616
+ ownClaimIds.current.clear();
1617
+ if (!keepBusy)
1618
+ setOrderBusy(false);
1619
+ }
1620
+ }, [isOnline, printPlek, station.id, sessionId, refresh, printAllForItemInner, t]);
1621
+ /** Opnieuw printen (tabblad Geprint vandaag), na bevestiging. */
1622
+ const reprintOrder = useCallback(async (card) => {
1623
+ if (orderLock.current || !isOnline)
1624
+ return;
1625
+ orderLock.current = true;
1626
+ setOrderBusy(true);
1627
+ try {
1628
+ let docs = 0;
1629
+ let failed = 0;
1630
+ let reason = null;
1631
+ for (const row of card.rows) {
1632
+ const out = await printAllForItemInner(row, { quiet: true });
1633
+ docs += trackableJobs(out.results).length;
1634
+ const bad = out.results.filter((r) => r.outcome === "error").length + (out.error && out.results.length === 0 ? 1 : 0);
1635
+ failed += bad;
1636
+ if (bad > 0)
1637
+ reason = reason ?? friendlyPickError(out.error ?? null, t.lang).message;
1638
+ }
1639
+ setSlot(failed > 0 ? { kind: "error", text: partialFailText(docs, docs + failed, reason) } : { kind: "success", text: sentText(printerName, docs) });
1640
+ }
1641
+ finally {
1642
+ orderLock.current = false;
1643
+ setOrderBusy(false);
1644
+ refresh();
1645
+ }
1646
+ }, [isOnline, printAllForItemInner, printerName, refresh, t]);
1647
+ const selectedDayRef = useRef(null);
1648
+ selectedDayRef.current = chosenDay ?? pickDay ?? null;
1649
+ useEffect(() => {
1650
+ if (!isOnline)
1651
+ setSlot({ kind: "offline" });
1652
+ else
1653
+ setSlot((s) => (s.kind === "offline" ? { kind: "none" } : s));
1654
+ }, [isOnline]);
1655
+ /** 10 minuten niets: terug naar de beginstand; taal blijft bewaard. */
1656
+ useIdle(10 * 60000, () => {
1657
+ setTab("todo");
1658
+ setMeerOpen(false);
1659
+ setShowLead(false);
1660
+ setReprintAsk(null);
1661
+ setSearchInput("");
1662
+ setFilters((f) => ({ ...f, search: "", documentTypeId: null, page: 0 }));
1663
+ if (chosenDay)
1664
+ void chooseDay("current");
1665
+ window.scrollTo({ top: 0 });
1666
+ });
1667
+ const listCards = tab === "todo" ? todoCards : printedCards;
1668
+ const { shown, handlers: touchHandlers } = useStableList(listCards, cardKeyFn);
1669
+ const showNotes = pickModeInfo?.autoSendLoading !== true;
1379
1670
  // Stap 1 — taalselectie
1380
1671
  if (!langLoaded)
1381
1672
  return null;
@@ -1400,83 +1691,10 @@ export function PickScherm({ station, onBack }) {
1400
1691
  : checkTotal === 0
1401
1692
  ? 100
1402
1693
  : Math.round((checkDone / checkTotal) * 100);
1403
- 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
1404
- ? "Pickwachtrij laden…"
1405
- : checkDone > 0
1406
- ? `Pakbonnen controleren (${checkDone} van ${checkTotal})…`
1407
- : `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-14 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-14 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-14 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-14 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-14 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-14 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")] })] })] }), showSyncingNotice({ syncing, syncError }) && (_jsx("div", { role: "status", "aria-live": "polite", className: "px-4 py-1 text-xs text-muted-foreground", children: "Wachtrij wordt bijgewerkt\u2026" })), 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: () => {
1408
- setCartMissing(false);
1409
- setRowGone(false);
1410
- setPending(null);
1411
- setCartPlan(null);
1412
- setActiveCartId(null);
1413
- refresh();
1414
- }, children: "Wachtrij verversen" })] })), lastCartResults.some((r) => r.sentElsewhere) && lastPrinted && (_jsx("div", { className: "rounded-md border border-primary/40 bg-primary/5 p-3 text-sm", "aria-label": "Pakbon al verstuurd", children: _jsx("ul", { className: "space-y-2", children: lastCartResults.filter((r) => r.sentElsewhere).map((r) => (_jsxs("li", { className: "flex flex-wrap items-center justify-between gap-2", children: [_jsx("span", { className: "text-foreground", children: cartResultLine(r) }), _jsx(Button, { size: "sm", variant: "outline", disabled: busyId !== null, onClick: () => {
1415
- const plannedCart = (Array.isArray(cartPlan?.carts) ? cartPlan.carts : []).find((c) => c.cartId === r.cartId);
1416
- const cart = plannedCart ?? { cartId: r.cartId, cartLabel: r.cartLabel ?? r.cartId };
1417
- setLastCartResults((prev) => prev.filter((x) => x.cartId !== r.cartId));
1418
- void reprintCart(lastPrinted, cart);
1419
- }, 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-14 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
1420
- ? "De pakbon wordt opgevraagd. Zodra alles compleet is, verschijnt de printknop weer."
1421
- : planReason
1422
- ? `${planReason} Klik op "Pakbon ophalen" om hem op te vragen.`
1423
- : NOT_READY_NOTICE })] }) })), cartPlan && (_jsxs(CardContent, { className: "border-t p-4 sm:p-5", children: [_jsx(DocumentChoiceMelding, { choice: documentChoice.data, isError: documentChoice.isError }), _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) => {
1424
- const done = printedCartIdsOf(pending).includes(cart.cartId);
1425
- const active = cart.cartId === activeCartId;
1426
- return (_jsxs("div", { role: "button", tabIndex: 0, onClick: () => setActiveCartId(cart.cartId), onKeyDown: (event) => {
1427
- if (event.key === "Enter" || event.key === " ") {
1428
- event.preventDefault();
1429
- setActiveCartId(cart.cartId);
1430
- }
1431
- }, className: "flex w-full cursor-pointer items-center justify-between gap-2 rounded-md border p-3 text-left transition hover:bg-muted/50 " +
1432
- (active ? "border-primary bg-primary/5" : ""), children: [_jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "truncate text-sm font-semibold text-foreground", children: cart.cartLabel }), _jsxs("p", { className: "text-xs text-muted-foreground", children: [cart.isFullCart ? "Volle kar" : "Halve kar", " · ", cart.items.length, " ", cart.items.length === 1 ? "product" : "producten", cart.isShared ? " · gedeelde kar" : "", done ? " · geprint" : ""] })] }), _jsxs(Button, { type: "button", variant: done ? "outline" : "secondary", size: "sm", disabled: busyId !== null || actionBusy || isPrintBlocked(documentChoice.data, done), onClick: (event) => {
1433
- event.stopPropagation();
1434
- setActiveCartId(cart.cartId);
1435
- void printSingleCart(pending, cart);
1436
- }, children: [_jsx(Printer, { className: "mr-2 h-4 w-4" }), done ? "Opnieuw" : "Printen"] })] }, cart.cartId));
1437
- }) })] }), activeCartId && (() => {
1438
- const activeCart = cartPlan.carts.find((cart) => cart.cartId === activeCartId);
1439
- if (!activeCart)
1440
- return null;
1441
- return (_jsxs("div", { className: "rounded-md border bg-muted/20 p-4", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [_jsxs("div", { children: [_jsx("p", { className: "text-lg font-bold text-foreground", children: activeCart.cartLabel }), _jsxs("p", { className: "text-sm text-muted-foreground", children: [activeCart.isFullCart ? "Volle kar" : "Halve kar (deel van de order)", activeCart.isShared ? " · gedeeld" : ""] })] }), activeCart.barcode && _jsx(Badge, { variant: "outline", children: activeCart.barcode })] }), _jsx("div", { className: "mt-4 divide-y rounded-md border bg-background", children: _jsx(CartItemRows, { items: activeCart.items, cartId: activeCart.cartId }) }), _jsx(StickerFitMeldingen, { cartId: activeCart.cartId, slug: station.slug ?? null }), _jsx(CartStickerState, { cartId: activeCart.cartId, slug: station.slug ?? null }), _jsx("div", { "data-slot": "sticker-acties", className: "empty:hidden", children: _jsx(StickerReprintButton, { cartId: activeCart.cartId, slug: station.slug ?? null }) })] }));
1442
- })()] })] }))] })), batch && (_jsx(Card, { className: batchFailed > 0 ? "border-2 border-destructive" : "border-2 border-primary/40", children: _jsxs(CardContent, { className: "flex items-center gap-3 p-4", children: [batchCompleted + batchFailed === batch.jobs.length ? (batchFailed > 0 ? _jsx(AlertTriangle, { className: "h-6 w-6 shrink-0 text-destructive" }) : _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 text-muted-foreground", children: [jobTexts.jobTitle, batch.documentNumber ? ` · ${batch.documentNumber}` : ""] }), _jsxs("p", { className: "font-semibold text-foreground", children: [batchCompleted, " van ", batch.jobs.length, " pakbonnen voltooid", batchFailed > 0 ? ` · ${batchFailed} mislukt` : ""] }), batch.jobs.map((entry) => {
1443
- const minutes = stuckMinutesOf(entry.job, sentAtRef.current.get(entry.jobId) ?? null, nowTick);
1444
- if (minutes === null)
1445
- return null;
1446
- const cart = cartPlan?.carts.find((c) => c.cartId === entry.cartId) ?? null;
1447
- const item = lastPrinted ?? pending;
1448
- 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));
1449
- })] })] }) })), 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] })), (() => {
1450
- const minutes = stuckMinutesOf(tracked.job, sentAtRef.current.get(tracked.jobId) ?? null, nowTick);
1451
- if (minutes === null)
1452
- return null;
1453
- const cart = cartPlan?.carts.find((c) => c.cartId === tracked.cartId) ?? null;
1454
- const item = lastPrinted ?? pending;
1455
- return (_jsxs("div", { className: "flex flex-wrap items-center gap-2 text-sm font-medium text-destructive", children: [_jsxs("span", { children: [stuckMessage(minutes), tracked.job?.printer_naam ? ` (${tracked.job.printer_naam})` : ""] }), cart && item && (_jsx(Button, { size: "sm", variant: "outline", disabled: busyId !== null || actionBusy, onClick: () => void reprintCart(item, cart), children: "Opnieuw proberen" }))] }));
1456
- })(), jobFailed && (_jsxs("p", { className: "text-sm text-destructive", children: [tracked.job?.foutmelding ?? jobTexts.unknown, " \u00B7 ", jobTexts.requeued] })), !jobFailed && pdfSaveWarning(tracked.job) && (_jsx("p", { className: "text-sm font-medium text-destructive", children: pdfSaveWarning(tracked.job) }))] })] }), _jsxs("div", { className: "flex flex-col gap-2 sm:flex-row", children: [jobFailed && lastPrinted && (_jsxs(Button, { size: "lg", className: "h-14 w-full sm:w-auto", onClick: () => {
1457
- clearJob();
1458
- void confirmPending();
1459
- }, 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-14 w-full sm:w-auto", onClick: () => {
1460
- clearJob();
1461
- clearShippingNotes();
1462
- setAutoSendOrderId(null);
1463
- setAutoSend(null);
1464
- }, 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")
1465
- ? "Pakbon en verzendbrief zijn naar de printer gestuurd."
1466
- : autoSendMessage(autoSend, siteTz) ?? "De verzendbrief is nog niet geprint." }), shippingNotes.map((note) => {
1467
- const message = shippingNoteMessage(note.state);
1468
- const failed = note.state.status === "mislukt" || note.state.status === "geblokkeerd";
1469
- const waiting = isWaitingShippingNote(note.state.status);
1470
- const typeLabel = shippingNoteSourceLabel(note.state);
1471
- 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)) ||
1472
- (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
1473
- ? `Volgende poging: ${formatSiteTime(note.state.nextAttemptAt, siteTz)}`
1474
- : ""] }))] })] }, note.jobId));
1475
- })] }) })), _jsxs(Button, { variant: "outline", size: "lg", className: "h-14 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-14 pl-9 text-lg", 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-14 w-full text-lg 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)))] })] }), _jsx("div", { role: "group", "aria-label": ui.allDocumentTypes, className: "flex w-full flex-wrap gap-2 sm:w-auto", children: [{ id: null, name: ui.allDocumentTypes }, ...(Array.isArray(documentTypes) ? documentTypes : []).map((dt) => ({ id: dt.id, name: dt.name }))].map((dt) => {
1476
- const active = (filters.documentTypeId ?? null) === dt.id;
1477
- return (_jsx(Button, { type: "button", "aria-pressed": active, variant: active ? "default" : "outline", className: "h-14 px-4 text-lg", onClick: () => setFilters((f) => ({ ...f, documentTypeId: dt.id, page: 0 })), children: dt.name }, dt.id ?? "all"));
1478
- }) }), _jsx(Button, { variant: filters.onlyPending ? "default" : "outline", className: "h-14 text-lg", onClick: () => setFilters((f) => ({ ...f, onlyPending: !f.onlyPending, page: 0 })), children: ui.onlyPending }), _jsxs(Button, { variant: filters.urgentOnly ? "default" : "outline", className: "h-14 text-lg", 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-14 text-lg", 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-14 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 () => {
1479
- await printAllForItem(row);
1480
- void refetchEarlier();
1481
- }, 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-14 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-14 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) })] }));
1694
+ const dayText = chosenDay ? dayLabel(chosenDay) : wv("today");
1695
+ const allLoaded = total <= filters.pageSize;
1696
+ return (_jsxs("div", { className: "space-y-4 pb-8 text-lg", children: [_jsxs("header", { className: "flex items-center gap-3", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("h2", { className: "truncate text-2xl font-bold text-foreground", children: [station.name, " ", _jsxs("span", { className: "font-semibold text-muted-foreground", children: ["\u00B7 ", dayText] })] }), tab === "todo" && !loading && allLoaded && (_jsx("p", { className: "text-lg tabular-nums text-muted-foreground", children: todoCountText(todoCards.length) }))] }), _jsx(Button, { variant: "outline", className: "h-14 w-14 shrink-0 [&_svg]:!size-6", onClick: () => setLang(null), "aria-label": wv("language"), children: _jsx(Globe, { className: "h-6 w-6" }) }), _jsxs(Button, { variant: "outline", className: "h-14 shrink-0 px-5 text-lg [&_svg]:!size-6", onClick: () => setMeerOpen(true), children: [_jsx(MoreHorizontal, { className: "mr-2 h-6 w-6" }), " ", wv("more")] })] }), _jsxs("div", { children: [_jsxs(Button, { className: "h-24 min-h-[80px] w-full text-2xl font-bold sm:text-3xl [&_svg]:!size-8", disabled: !isOnline || orderBusy || actionBusy || !nextCard, onClick: () => void printOrder(readyCards), children: [orderBusy ? _jsx(Loader2, { className: "mr-3 h-8 w-8 animate-spin" }) : _jsx(Printer, { className: "mr-3 h-8 w-8" }), wv("printNext")] }), _jsx("p", { className: "mt-2 min-h-[1.75rem] truncate text-center text-xl font-semibold text-foreground", children: nextCard ? cardSummary(nextCard) : !loading && todoCards.length === 0 ? wv("emptyTodo") : "" })] }), _jsx(MeldingsPlek, { slot: slot, onDismiss: () => setSlot(isOnline ? { kind: "none" } : { kind: "offline" }) }), _jsx("div", { role: "tablist", className: "grid grid-cols-2 gap-2", children: ["todo", "printed"].map((k) => (_jsx(Button, { role: "tab", "aria-selected": tab === k, variant: tab === k ? "default" : "outline", className: "h-14 text-xl", onClick: () => setTab(k), children: k === "todo" ? wv("tabTodo") : wv("tabPrinted") }, k))) }), _jsx(VroegeStickerWaarschuwing, { stationId: station.id }), loading && shown.length === 0 ? (_jsx("ul", { className: "space-y-3", "aria-hidden": true, children: [0, 1, 2].map((i) => (_jsx("li", { className: "h-28 rounded-xl bg-muted" }, i))) })) : shown.length === 0 ? (_jsx("p", { className: "py-10 text-center text-xl text-muted-foreground", children: tab === "todo" ? wv("emptyTodo") : wv("emptyPrinted") })) : (_jsx("ul", { className: "space-y-3", ...touchHandlers, children: shown.map(({ item: card, leaving }) => (_jsx(WerkvloerKaart, { card: card, state: cardStateOf(card, tab), tz: siteTz, mode: tab, leaving: leaving, showLead: showLead, disabled: !isOnline || orderBusy || actionBusy, onPrint: (c) => (tab === "printed" ? setReprintAsk(c) : void printOrder([c])), onFetch: (c) => void requestData(c.rows[0]), onPreview: (c) => setPreviewItem(c.rows[0] ?? null), onMarkPrinted: (c) => void (async () => { for (const r of c.rows)
1697
+ await markPrinted(r); })() }, card.key))) })), totalPages > 1 && (_jsxs("div", { className: "grid grid-cols-[1fr_auto_1fr] items-center gap-2", children: [_jsx(Button, { variant: "outline", className: "h-14", disabled: filters.page === 0, onClick: () => setFilters((f) => ({ ...f, page: Math.max(0, f.page - 1) })), children: t("previous") }), _jsxs("span", { className: "text-center tabular-nums text-muted-foreground", children: [filters.page + 1, " / ", totalPages] }), _jsx(Button, { variant: "outline", className: "h-14", disabled: filters.page + 1 >= totalPages, onClick: () => setFilters((f) => ({ ...f, page: f.page + 1 })), children: t("next") })] })), _jsx(AlertDialog, { open: reprintAsk !== null, onOpenChange: (o) => { if (!o)
1698
+ setReprintAsk(null); }, children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { className: "text-2xl", children: reprintAsk ? cardSummary(reprintAsk) : "" }), _jsx(AlertDialogDescription, { className: "text-xl text-foreground", children: reprintAsk ? reprintQuestion(reprintAsk.printedAt, siteTz) : "" })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { className: "h-14 text-lg", children: wv("cancel") }), _jsx(AlertDialogAction, { className: "h-14 text-lg", onClick: () => { const c = reprintAsk; setReprintAsk(null); if (c)
1699
+ void reprintOrder(c); }, children: wv("reprint") })] })] }) }), _jsx(Dialog, { open: meerOpen, onOpenChange: setMeerOpen, children: _jsxs(DialogContent, { className: "max-h-[90vh] overflow-y-auto sm:max-w-2xl", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { className: "text-2xl", children: wv("more") }) }), _jsxs("div", { className: "space-y-6 text-lg", children: [_jsxs("section", { className: "space-y-2", children: [_jsx("h3", { className: "font-semibold text-foreground", children: wv("pickDay") }), _jsxs("div", { className: "grid grid-cols-3 gap-2", children: [_jsxs(Button, { variant: "outline", className: "h-14", disabled: loadingDay, onClick: () => void shiftDay(-1), children: [_jsx(ChevronLeft, { className: "mr-1 h-5 w-5" }), " ", ui.pickDayPrev] }), _jsx(Button, { variant: chosenDay ? "outline" : "default", className: "h-14", disabled: loadingDay, onClick: () => void chooseDay("current"), children: wv("today") }), _jsxs(Button, { variant: "outline", className: "h-14", disabled: loadingDay, onClick: () => void shiftDay(1), children: [ui.pickDayNext, " ", _jsx(ChevronRight, { className: "ml-1 h-5 w-5" })] })] }), _jsx("p", { className: "text-muted-foreground", children: dayText })] }), _jsxs("section", { className: "space-y-2", children: [_jsx("label", { htmlFor: "pick-search", className: "font-semibold text-foreground", children: wv("search") }), _jsxs("div", { className: "relative", children: [_jsx(Search, { className: "pointer-events-none absolute left-3 top-1/2 h-5 w-5 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { id: "pick-search", className: "h-14 pl-10 text-lg", value: searchInput, onChange: (e) => setSearchInput(e.target.value) })] })] }), _jsxs("section", { className: "space-y-2", children: [_jsx("h3", { className: "font-semibold text-foreground", children: wv("documentFilter") }), _jsx("div", { className: "flex flex-wrap gap-2", children: [{ id: null, name: wv("allDocuments") }, ...(Array.isArray(documentTypes) ? documentTypes : []).map((dt) => ({ id: dt.id, name: dt.name }))].map((dt) => (_jsx(Button, { "aria-pressed": (filters.documentTypeId ?? null) === dt.id, variant: (filters.documentTypeId ?? null) === dt.id ? "default" : "outline", className: "h-14 px-4 text-lg", onClick: () => setFilters((f) => ({ ...f, documentTypeId: dt.id, page: 0 })), children: dt.name }, dt.id ?? "all"))) })] }), _jsx("section", { children: _jsxs(Button, { variant: showLead ? "default" : "outline", className: "h-14 w-full text-lg", "aria-pressed": showLead, onClick: () => setShowLead((v) => !v), children: [wv("markPrinted"), " \u2014 ", wv("leadActions")] }) }), showNotes && (_jsx("section", { children: _jsxs(Button, { variant: "outline", className: "h-14 w-full text-lg", 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" }), wv("fetchNotes")] }) })), _jsxs("section", { className: "space-y-2", children: [_jsx("h3", { className: "font-semibold text-foreground", children: wv("scan") }), _jsx(ShippingNoteScan, { stationId: station.id })] }), earlierRows.length > 0 && (_jsxs("section", { className: "space-y-2", "aria-label": wv("earlier"), children: [_jsxs("h3", { className: "font-semibold text-foreground", children: [wv("earlier"), " (", 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("span", { className: "min-w-0 truncate", children: [row.order_number ?? row.document_number ?? "—", " \u00B7 ", pickDayShortLabel(pickDayOf(row)), row.customer_name ? ` · ${row.customer_name}` : ""] }), _jsx(Button, { variant: "outline", className: "h-14", disabled: busyId !== null || actionBusy || orderBusy, onClick: async () => { await printAllForItem(row); void refetchEarlier(); }, children: wv("reprint") })] }, row.id))) })] }))] }), _jsx(DialogFooter, { children: _jsx(Button, { className: "h-14 w-full text-lg", onClick: () => setMeerOpen(false), children: wv("close") }) })] }) }), _jsx(PreviewDialog, { item: previewItem, stationSlug: station.slug, onClose: () => setPreviewItem(null) })] }));
1482
1700
  }