@flowselections/pick-schermen-kwekers 1.0.23 → 1.0.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-lib/components/pick/CartItemRows.d.ts +14 -0
- package/dist-lib/components/pick/CartItemRows.d.ts.map +1 -0
- package/dist-lib/components/pick/CartItemRows.js +23 -0
- package/dist-lib/components/pick/PickScherm.d.ts.map +1 -1
- package/dist-lib/components/pick/PickScherm.js +196 -99
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +4 -31332
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/auth-middleware.js +1 -1
- package/dist-lib/integrations/supabase/client.d.ts +2 -31332
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +2 -31332
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +2742 -39
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/__tests__/pickAlreadyPrinted.test.d.ts +2 -0
- package/dist-lib/lib/__tests__/pickAlreadyPrinted.test.d.ts.map +1 -0
- package/dist-lib/lib/__tests__/pickAlreadyPrinted.test.js +31 -0
- package/dist-lib/lib/__tests__/pickCartResults.test.d.ts +2 -0
- package/dist-lib/lib/__tests__/pickCartResults.test.d.ts.map +1 -0
- package/dist-lib/lib/__tests__/pickCartResults.test.js +68 -0
- package/dist-lib/lib/__tests__/pickSetComponents.test.d.ts +2 -0
- package/dist-lib/lib/__tests__/pickSetComponents.test.d.ts.map +1 -0
- package/dist-lib/lib/__tests__/pickSetComponents.test.js +73 -0
- package/dist-lib/lib/pickApi.d.ts +13 -0
- package/dist-lib/lib/pickApi.d.ts.map +1 -1
- package/dist-lib/lib/pickApi.js +8 -7
- package/dist-lib/lib/pickCartResults.d.ts +59 -0
- package/dist-lib/lib/pickCartResults.d.ts.map +1 -0
- package/dist-lib/lib/pickCartResults.js +150 -0
- package/dist-lib/lib/pickTypes.d.ts +19 -0
- package/dist-lib/lib/pickTypes.d.ts.map +1 -1
- package/dist-lib/lib/pickTypes.js +30 -1
- package/package.json +3 -3
|
@@ -12,6 +12,7 @@ import { sendLogisticsEvent } from "../../lib/logisticsApi";
|
|
|
12
12
|
import { completeLogisticsTaskForItem } from "../../lib/pickPrint";
|
|
13
13
|
import { isPdfOnlyFailure, pickReadiness, printedCartIdsOf, pdfSaveWarning, unitRefOf, unitTitleOf, } from "../../lib/pickTypes";
|
|
14
14
|
import { rowFingerprint, rowsToVerify } from "../../lib/pickVerify";
|
|
15
|
+
import { CART_MISSING_MESSAGE, cartResultLine, cartsForRow, isCartMissingError, reprintCartsIndividually, resettableJobs, runExclusive, resultsFromBatch, resultsFromBatchError, summarizeCartResults, trackableJobs, } from "../../lib/pickCartResults";
|
|
15
16
|
import { getPickTranslator } from "../../lib/pickI18n";
|
|
16
17
|
import { friendlyPickError, getPickUiTexts } from "../../lib/pickUiText";
|
|
17
18
|
import { getPrintJobTexts, printJobStatusLabel } from "../../lib/pickPrintJobText";
|
|
@@ -19,6 +20,7 @@ import { defaultFilters, usePickDays, usePickHeartbeat, usePickQueue, } from "..
|
|
|
19
20
|
import { usePickDocumentTypes, usePickStatuses, useStationPrintPlek, } from "../../hooks/usePickConfig";
|
|
20
21
|
import { usePrintBatch, usePrintJob } from "../../hooks/usePrintJob";
|
|
21
22
|
import { useShippingNoteStatus } from "../../hooks/useShippingNoteStatus";
|
|
23
|
+
import { CartItemRows, PlanWarnings } from "./CartItemRows";
|
|
22
24
|
import { isWaitingShippingNote, shippingNoteMessage, shippingNoteSourceLabel, } from "../../lib/shippingNoteStatus";
|
|
23
25
|
const LANG_STORAGE_KEY = "pick.language";
|
|
24
26
|
/** Melding als de planning voor deze order geen karren heeft vastgelegd. */
|
|
@@ -481,6 +483,50 @@ export function PickScherm({ station, onBack }) {
|
|
|
481
483
|
setBusyId(null);
|
|
482
484
|
}
|
|
483
485
|
}, [station.id, station.lock_timeout_seconds, sessionId, loadCartPlan, refresh, showPrintError]);
|
|
486
|
+
/**
|
|
487
|
+
* Eén vergrendeling voor alle printacties. Gaat direct bij de klik in (ref,
|
|
488
|
+
* niet pas bij de volgende weergave), zodat een dubbelklik nooit een tweede
|
|
489
|
+
* aanvraag start. De knoppen blijven uit tot de hele actie klaar is.
|
|
490
|
+
*/
|
|
491
|
+
const actionLock = useRef(false);
|
|
492
|
+
const [actionBusy, setActionBusy] = useState(false);
|
|
493
|
+
const withActionLock = useCallback((fn) => async (...args) => {
|
|
494
|
+
if (actionLock.current)
|
|
495
|
+
return;
|
|
496
|
+
setActionBusy(true);
|
|
497
|
+
try {
|
|
498
|
+
await runExclusive(actionLock, () => fn(...args));
|
|
499
|
+
}
|
|
500
|
+
finally {
|
|
501
|
+
setActionBusy(false);
|
|
502
|
+
}
|
|
503
|
+
}, []);
|
|
504
|
+
/** De kar van de rij staat niet (meer) in het karplan: verversen nodig. */
|
|
505
|
+
const [cartMissing, setCartMissing] = useState(false);
|
|
506
|
+
/** Per kar het resultaat tonen; bij één kar één korte melding. */
|
|
507
|
+
const showCartResults = useCallback((results) => {
|
|
508
|
+
if (results.length === 0)
|
|
509
|
+
return;
|
|
510
|
+
const s = summarizeCartResults(results);
|
|
511
|
+
const description = s.lines.length > 0 ? s.lines.join("\n") : undefined;
|
|
512
|
+
const opts = description ? { description, duration: 10000 } : undefined;
|
|
513
|
+
if (s.kind === "success")
|
|
514
|
+
toast.success(s.title, opts);
|
|
515
|
+
else if (s.kind === "error")
|
|
516
|
+
toast.error(s.title, opts);
|
|
517
|
+
else
|
|
518
|
+
toast.warning(s.title, opts);
|
|
519
|
+
setLastCartResults(results);
|
|
520
|
+
}, []);
|
|
521
|
+
const [lastCartResults, setLastCartResults] = useState([]);
|
|
522
|
+
/** Alleen echt aangemaakte opdrachten volgen; herprints worden gemarkeerd. */
|
|
523
|
+
const startBatchTracking = useCallback((item, results) => {
|
|
524
|
+
const jobs = trackableJobs(results);
|
|
525
|
+
if (jobs.length === 0)
|
|
526
|
+
return;
|
|
527
|
+
finalizedBatch.current = null;
|
|
528
|
+
trackBatch({ queueItemId: item.id, documentNumber: item.document_number ?? null, jobs });
|
|
529
|
+
}, [trackBatch]);
|
|
484
530
|
/**
|
|
485
531
|
* Zet een nieuwe printopdracht in de wachtrij voor een al geprint document,
|
|
486
532
|
* zonder de wachtrijstatus te wijzigen.
|
|
@@ -508,6 +554,7 @@ export function PickScherm({ station, onBack }) {
|
|
|
508
554
|
}
|
|
509
555
|
}, [t, sessionId, track, showPrintError]);
|
|
510
556
|
/** Open een geprinte order bovenaan zonder de wachtrijstatus te wijzigen. */
|
|
557
|
+
const handleReprintLocked = useMemo(() => withActionLock(handleReprint), [withActionLock, handleReprint]);
|
|
511
558
|
const selectPrintedForReprint = useCallback(async (item) => {
|
|
512
559
|
setBusyId(item.id);
|
|
513
560
|
try {
|
|
@@ -530,34 +577,23 @@ export function PickScherm({ station, onBack }) {
|
|
|
530
577
|
* Is de regel nog niet printklaar, dan wordt er niet geprint maar worden de
|
|
531
578
|
* ontbrekende gegevens bij de bron opgevraagd.
|
|
532
579
|
*/
|
|
533
|
-
const
|
|
580
|
+
const printAllForItemInner = useCallback(async (item) => {
|
|
534
581
|
setBusyId(item.id);
|
|
582
|
+
setCartMissing(false);
|
|
535
583
|
try {
|
|
536
584
|
if (item.printed_at) {
|
|
537
585
|
const { plan, readiness: state } = await loadCartPlan(item);
|
|
538
586
|
setPending(item);
|
|
539
587
|
if (!plan || !state.ready) {
|
|
540
|
-
setBusyId(null);
|
|
541
588
|
toast.warning(state.reason ?? NOT_READY_NOTICE);
|
|
542
589
|
await requestData(item);
|
|
543
590
|
return;
|
|
544
591
|
}
|
|
545
|
-
const
|
|
546
|
-
|
|
547
|
-
const jobId = await pickApi.reprintCart(item.id, sessionId, cart.cartId);
|
|
548
|
-
if (jobId)
|
|
549
|
-
jobs.push({ cartId: cart.cartId, cartLabel: cart.cartLabel, jobId });
|
|
550
|
-
}
|
|
592
|
+
const carts = cartsForRow(item, plan);
|
|
593
|
+
const results = await reprintCartsIndividually(carts, (cartId) => pickApi.reprintCart(item.id, sessionId, cartId));
|
|
551
594
|
setLastPrinted(item);
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
trackBatch({
|
|
555
|
-
queueItemId: item.id,
|
|
556
|
-
documentNumber: item.document_number ?? null,
|
|
557
|
-
jobs,
|
|
558
|
-
});
|
|
559
|
-
}
|
|
560
|
-
toast.success(`${jobs.length} ${jobs.length === 1 ? "pakbon" : "pakbonnen"} ${t("sentToPrinter")}`);
|
|
595
|
+
startBatchTracking(item, results);
|
|
596
|
+
showCartResults(results);
|
|
561
597
|
refresh();
|
|
562
598
|
return;
|
|
563
599
|
}
|
|
@@ -567,11 +603,12 @@ export function PickScherm({ station, onBack }) {
|
|
|
567
603
|
const preflight = await loadCartPlan(item);
|
|
568
604
|
setPending(item);
|
|
569
605
|
if (!preflight.plan || !preflight.readiness.ready) {
|
|
570
|
-
setBusyId(null);
|
|
571
606
|
toast.warning(preflight.readiness.reason ?? NOT_READY_NOTICE);
|
|
572
607
|
await requestData(item);
|
|
573
608
|
return;
|
|
574
609
|
}
|
|
610
|
+
// Rij met eigen kar: alleen die kar, en die moet in het karplan staan.
|
|
611
|
+
cartsForRow(item, preflight.plan);
|
|
575
612
|
const claimed = await pickApi.claim({
|
|
576
613
|
itemId: item.id,
|
|
577
614
|
stationId: station.id,
|
|
@@ -583,27 +620,40 @@ export function PickScherm({ station, onBack }) {
|
|
|
583
620
|
const { plan, readiness: state } = await loadCartPlan(target);
|
|
584
621
|
setPending(target);
|
|
585
622
|
if (!plan || !state.ready) {
|
|
586
|
-
setBusyId(null);
|
|
587
623
|
toast.warning(state.reason ?? NOT_READY_NOTICE);
|
|
588
624
|
await requestData(target);
|
|
589
625
|
return;
|
|
590
626
|
}
|
|
591
|
-
const already =
|
|
592
|
-
const todo = plan.
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
627
|
+
const already = printedCartIdsOf(target);
|
|
628
|
+
const todo = cartsForRow(target, plan).filter((cart) => !already.includes(cart.cartId));
|
|
629
|
+
if (todo.length === 0) {
|
|
630
|
+
toast.info("Alle karren van deze rij zijn al geprint.");
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
let results;
|
|
634
|
+
try {
|
|
635
|
+
const result = await pickApi.completePrintCarts(target.id, sessionId, todo.map((cart) => cart.cartId));
|
|
636
|
+
results = resultsFromBatch(todo, result.carts, already);
|
|
637
|
+
setLastPrinted(result.item);
|
|
638
|
+
setPending(result.item);
|
|
639
|
+
}
|
|
640
|
+
catch (e) {
|
|
641
|
+
results = resultsFromBatchError(todo, e, already);
|
|
642
|
+
showCartResults(results);
|
|
643
|
+
throw e;
|
|
644
|
+
}
|
|
645
|
+
startBatchTracking(target, results);
|
|
646
|
+
showCartResults(results);
|
|
603
647
|
refresh();
|
|
604
648
|
}
|
|
605
649
|
catch (e) {
|
|
606
|
-
|
|
650
|
+
if (isCartMissingError(e)) {
|
|
651
|
+
setCartMissing(true);
|
|
652
|
+
toast.error(CART_MISSING_MESSAGE);
|
|
653
|
+
}
|
|
654
|
+
else {
|
|
655
|
+
showPrintError(e);
|
|
656
|
+
}
|
|
607
657
|
if (!item.printed_at) {
|
|
608
658
|
try {
|
|
609
659
|
await pickApi.releaseLock(item.id);
|
|
@@ -626,11 +676,12 @@ export function PickScherm({ station, onBack }) {
|
|
|
626
676
|
sessionId,
|
|
627
677
|
loadCartPlan,
|
|
628
678
|
requestData,
|
|
629
|
-
|
|
679
|
+
startBatchTracking,
|
|
680
|
+
showCartResults,
|
|
630
681
|
refresh,
|
|
631
|
-
t,
|
|
632
682
|
showPrintError,
|
|
633
683
|
]);
|
|
684
|
+
const printAllForItem = useMemo(() => withActionLock(printAllForItemInner), [withActionLock, printAllForItemInner]);
|
|
634
685
|
/**
|
|
635
686
|
* Batchknop: print de bovenste n printklare regels in de bestaande volgorde
|
|
636
687
|
* (volle karren voor deelkarren, zoals de lijst sorteert). Per regel draait
|
|
@@ -639,7 +690,7 @@ export function PickScherm({ station, onBack }) {
|
|
|
639
690
|
* precies één printactie krijgt. Niet-printklare karren worden overgeslagen
|
|
640
691
|
* met een leesbare melding per kar.
|
|
641
692
|
*/
|
|
642
|
-
const
|
|
693
|
+
const printNextCartsInner = useCallback(async () => {
|
|
643
694
|
if (!cartsPerPrint || batchPrinting)
|
|
644
695
|
return;
|
|
645
696
|
setBatchPrinting(true);
|
|
@@ -674,10 +725,10 @@ export function PickScherm({ station, onBack }) {
|
|
|
674
725
|
toast.warning(`${s.title}: ${s.reason}`);
|
|
675
726
|
const doneUnits = new Set();
|
|
676
727
|
for (const item of targets) {
|
|
677
|
-
const unitKey = `${item.document_type_id ?? "-"}|${
|
|
728
|
+
const unitKey = `${item.document_type_id ?? "-"}|${unitRefOf(item)}`;
|
|
678
729
|
if (doneUnits.has(unitKey))
|
|
679
730
|
continue;
|
|
680
|
-
await
|
|
731
|
+
await printAllForItemInner(item);
|
|
681
732
|
doneUnits.add(unitKey);
|
|
682
733
|
}
|
|
683
734
|
}
|
|
@@ -687,7 +738,8 @@ export function PickScherm({ station, onBack }) {
|
|
|
687
738
|
finally {
|
|
688
739
|
setBatchPrinting(false);
|
|
689
740
|
}
|
|
690
|
-
}, [cartsPerPrint, batchPrinting, items, readinessById,
|
|
741
|
+
}, [cartsPerPrint, batchPrinting, items, readinessById, printAllForItemInner, showPrintError]);
|
|
742
|
+
const printNextCarts = useMemo(() => withActionLock(printNextCartsInner), [withActionLock, printNextCartsInner]);
|
|
691
743
|
const fetchNext = useCallback(async (silent = false) => {
|
|
692
744
|
const requestedDayVersion = daySelectionVersion.current;
|
|
693
745
|
setBusyId("next");
|
|
@@ -743,65 +795,72 @@ export function PickScherm({ station, onBack }) {
|
|
|
743
795
|
setBusyId(null);
|
|
744
796
|
}
|
|
745
797
|
}, [station.id, station.lock_timeout_seconds, sessionId, refresh, t, loadCartPlan, chosenDay, pickDay]);
|
|
798
|
+
const fetchNextLocked = useMemo(() => withActionLock(() => fetchNext()), [withActionLock, fetchNext]);
|
|
746
799
|
/**
|
|
747
800
|
* Print alle nog niet geprinte karren (pakbonnen) van deze order in één keer
|
|
748
801
|
* en haalt daarna een nieuwe order op.
|
|
749
802
|
*/
|
|
750
|
-
const
|
|
803
|
+
const confirmPendingInner = useCallback(async () => {
|
|
751
804
|
const item = pending;
|
|
752
805
|
const plan = cartPlan;
|
|
753
806
|
if (!item)
|
|
754
807
|
return;
|
|
808
|
+
setCartMissing(false);
|
|
755
809
|
if (!plan || !readiness.ready) {
|
|
756
810
|
// Niet printklaar: nooit printen, altijd eerst de gegevens opvragen.
|
|
757
811
|
toast.warning(readiness.reason ?? NOT_READY_NOTICE);
|
|
758
812
|
await requestData(item);
|
|
759
813
|
return;
|
|
760
814
|
}
|
|
761
|
-
|
|
762
|
-
|
|
815
|
+
let rowCarts;
|
|
816
|
+
try {
|
|
817
|
+
rowCarts = cartsForRow(item, plan);
|
|
818
|
+
}
|
|
819
|
+
catch {
|
|
820
|
+
setCartMissing(true);
|
|
821
|
+
toast.error(CART_MISSING_MESSAGE);
|
|
822
|
+
return;
|
|
823
|
+
}
|
|
824
|
+
const already = printedCartIdsOf(item);
|
|
825
|
+
const todo = rowCarts.filter((cart) => !already.includes(cart.cartId));
|
|
763
826
|
if (todo.length === 0) {
|
|
764
|
-
// Alles al geprint: bewuste herdruk
|
|
827
|
+
// Alles al geprint: bewuste herdruk, per kar apart.
|
|
765
828
|
setBusyId("confirm");
|
|
766
829
|
try {
|
|
767
|
-
const
|
|
768
|
-
for (const cart of plan.carts) {
|
|
769
|
-
const jobId = await pickApi.reprintCart(item.id, sessionId, cart.cartId);
|
|
770
|
-
if (jobId)
|
|
771
|
-
jobs.push({ cartId: cart.cartId, cartLabel: cart.cartLabel, jobId });
|
|
772
|
-
}
|
|
830
|
+
const results = await reprintCartsIndividually(rowCarts, (cartId) => pickApi.reprintCart(item.id, sessionId, cartId));
|
|
773
831
|
setLastPrinted(item);
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
trackBatch({ queueItemId: item.id, documentNumber: item.document_number ?? null, jobs });
|
|
777
|
-
}
|
|
778
|
-
toast.success(`${jobs.length} ${jobs.length === 1 ? "pakbon" : "pakbonnen"} ${t("sentToPrinter")}`);
|
|
832
|
+
startBatchTracking(item, results);
|
|
833
|
+
showCartResults(results);
|
|
779
834
|
refresh();
|
|
780
835
|
}
|
|
781
|
-
catch (e) {
|
|
782
|
-
showPrintError(e);
|
|
783
|
-
}
|
|
784
836
|
finally {
|
|
785
837
|
setBusyId(null);
|
|
786
838
|
}
|
|
787
839
|
return;
|
|
788
840
|
}
|
|
789
841
|
setBusyId("confirm");
|
|
842
|
+
let keepBusy = false;
|
|
790
843
|
try {
|
|
791
844
|
const result = await pickApi.completePrintCarts(item.id, sessionId, todo.map((cart) => cart.cartId));
|
|
845
|
+
const results = resultsFromBatch(todo, result.carts, already);
|
|
792
846
|
setLastPrinted(result.item);
|
|
793
847
|
setPending(result.item);
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
});
|
|
800
|
-
toast.success(`${result.jobs.length} ${result.jobs.length === 1 ? "pakbon" : "pakbonnen"} ${t("sentToPrinter")}`);
|
|
848
|
+
const jobs = trackableJobs(results);
|
|
849
|
+
startBatchTracking(item, results);
|
|
850
|
+
showCartResults(results);
|
|
851
|
+
// Met opdrachten blijft de knop uit tot de batch klaar is (zie afronding).
|
|
852
|
+
keepBusy = jobs.length > 0;
|
|
801
853
|
refresh();
|
|
802
854
|
}
|
|
803
855
|
catch (e) {
|
|
804
|
-
|
|
856
|
+
if (isCartMissingError(e)) {
|
|
857
|
+
setCartMissing(true);
|
|
858
|
+
toast.error(CART_MISSING_MESSAGE);
|
|
859
|
+
refresh();
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
862
|
+
showCartResults(resultsFromBatchError(todo, e, already));
|
|
863
|
+
const { expiredClaim: expired } = friendlyPickError(e, t.lang);
|
|
805
864
|
try {
|
|
806
865
|
await pickApi.releaseLock(item.id);
|
|
807
866
|
}
|
|
@@ -823,7 +882,12 @@ export function PickScherm({ station, onBack }) {
|
|
|
823
882
|
}
|
|
824
883
|
}
|
|
825
884
|
}
|
|
826
|
-
|
|
885
|
+
finally {
|
|
886
|
+
if (!keepBusy)
|
|
887
|
+
setBusyId(null);
|
|
888
|
+
}
|
|
889
|
+
}, [pending, cartPlan, readiness, requestData, sessionId, startBatchTracking, showCartResults, t, fetchNext, refresh]);
|
|
890
|
+
const confirmPending = useMemo(() => withActionLock(confirmPendingInner), [withActionLock, confirmPendingInner]);
|
|
827
891
|
/** Rond de order pas af wanneer alle batchopdrachten een eindstatus hebben. */
|
|
828
892
|
useEffect(() => {
|
|
829
893
|
if (!batch || batch.jobs.some((entry) => !entry.job || !["voltooid", "mislukt", "geannuleerd"].includes(entry.job.status)))
|
|
@@ -842,7 +906,19 @@ export function PickScherm({ station, onBack }) {
|
|
|
842
906
|
description: pdfSaveWarning(pdfOnly[0]?.job) ?? undefined,
|
|
843
907
|
});
|
|
844
908
|
}
|
|
845
|
-
const
|
|
909
|
+
const failedAll = ended.filter((entry) => !isPdfOnlyFailure(entry.job));
|
|
910
|
+
// Herprint van een al geprinte kar: nooit terugzetten, alleen melden.
|
|
911
|
+
const failedReprints = failedAll.filter((entry) => entry.reprint === true);
|
|
912
|
+
if (failedReprints.length > 0) {
|
|
913
|
+
toast.error(failedReprints.length === 1
|
|
914
|
+
? `${failedReprints[0].cartLabel ?? failedReprints[0].cartId} — herprint mislukt; de kar blijft geprint staan.`
|
|
915
|
+
: `${failedReprints.length} herprints mislukt; de karren blijven geprint staan.`, {
|
|
916
|
+
description: failedReprints
|
|
917
|
+
.map((e) => `${e.cartLabel ?? e.cartId} — fout: ${e.job?.foutmelding ?? jobTexts.unknown}`)
|
|
918
|
+
.join("\n"),
|
|
919
|
+
});
|
|
920
|
+
}
|
|
921
|
+
const failed = resettableJobs(failedAll);
|
|
846
922
|
if (failed.length > 0) {
|
|
847
923
|
const restored = await pickApi.resetPrintJobs(batch.queueItemId, sessionId, failed.map((entry) => ({ jobId: entry.jobId, cartId: entry.cartId })));
|
|
848
924
|
setPending(restored);
|
|
@@ -895,33 +971,48 @@ export function PickScherm({ station, onBack }) {
|
|
|
895
971
|
}
|
|
896
972
|
}, [sessionId, t, track, showPrintError]);
|
|
897
973
|
/** Eén losse pakbon (kar) printen vanaf de knop op de karregel. */
|
|
898
|
-
const
|
|
974
|
+
const printSingleCartInner = useCallback(async (item, cart) => {
|
|
975
|
+
// Alleen een kar uit het karplan van deze rij; bij een eigen kar alleen die.
|
|
976
|
+
try {
|
|
977
|
+
const allowed = cartsForRow(item, cartPlan);
|
|
978
|
+
if (!allowed.some((c) => c.cartId === cart.cartId))
|
|
979
|
+
throw new Error(CART_MISSING_MESSAGE);
|
|
980
|
+
}
|
|
981
|
+
catch {
|
|
982
|
+
setCartMissing(true);
|
|
983
|
+
toast.error(CART_MISSING_MESSAGE);
|
|
984
|
+
return;
|
|
985
|
+
}
|
|
899
986
|
const done = printedCartIdsOf(item).includes(cart.cartId);
|
|
900
987
|
if (done) {
|
|
901
988
|
await reprintCart(item, cart);
|
|
902
989
|
return;
|
|
903
990
|
}
|
|
904
991
|
setBusyId("confirm");
|
|
992
|
+
const already = printedCartIdsOf(item);
|
|
905
993
|
try {
|
|
906
994
|
const result = await pickApi.completePrintCarts(item.id, sessionId, [cart.cartId]);
|
|
995
|
+
const results = resultsFromBatch([cart], result.carts, already);
|
|
907
996
|
setLastPrinted(result.item);
|
|
908
997
|
setPending(result.item);
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
queueItemId: item.id,
|
|
912
|
-
documentNumber: result.item.document_number ?? null,
|
|
913
|
-
jobs: result.jobs,
|
|
914
|
-
});
|
|
915
|
-
toast.success(`${cart.cartLabel} · ${t("sentToPrinter")}`);
|
|
998
|
+
startBatchTracking(item, results);
|
|
999
|
+
showCartResults(results);
|
|
916
1000
|
refresh();
|
|
917
1001
|
}
|
|
918
1002
|
catch (e) {
|
|
919
|
-
|
|
1003
|
+
if (isCartMissingError(e)) {
|
|
1004
|
+
setCartMissing(true);
|
|
1005
|
+
toast.error(CART_MISSING_MESSAGE);
|
|
1006
|
+
}
|
|
1007
|
+
else {
|
|
1008
|
+
showCartResults(resultsFromBatchError([cart], e, already));
|
|
1009
|
+
}
|
|
920
1010
|
}
|
|
921
1011
|
finally {
|
|
922
1012
|
setBusyId(null);
|
|
923
1013
|
}
|
|
924
|
-
}, [reprintCart, sessionId,
|
|
1014
|
+
}, [cartPlan, reprintCart, sessionId, startBatchTracking, showCartResults, refresh]);
|
|
1015
|
+
const printSingleCart = useMemo(() => withActionLock(printSingleCartInner), [withActionLock, printSingleCartInner]);
|
|
925
1016
|
/**
|
|
926
1017
|
* Handmatig afmelden: het papier is geprint maar de regel bleef openstaan.
|
|
927
1018
|
* Zet de order op "Geprint" zonder nieuwe printopdracht en logt de reden.
|
|
@@ -990,7 +1081,7 @@ export function PickScherm({ station, onBack }) {
|
|
|
990
1081
|
useEffect(() => {
|
|
991
1082
|
if (lang === null || loading)
|
|
992
1083
|
return;
|
|
993
|
-
if (pending !== null || busyId !== null)
|
|
1084
|
+
if (pending !== null || (busyId !== null || actionBusy))
|
|
994
1085
|
return;
|
|
995
1086
|
if (autoFetched.current)
|
|
996
1087
|
return;
|
|
@@ -1030,33 +1121,39 @@ export function PickScherm({ station, onBack }) {
|
|
|
1030
1121
|
: Math.round((checkDone / checkTotal) * 100);
|
|
1031
1122
|
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
|
|
1032
1123
|
? "Wachtrij wordt geladen…"
|
|
1033
|
-
: `Pakbonnen controleren bij de bron (${checkDone} van ${checkTotal})` }), _jsxs("span", { className: "tabular-nums", children: [loadPercent, "%"] })] }), _jsx("div", { className: "h-1.5 w-full overflow-hidden rounded-full bg-muted", children: _jsx("div", { className: "h-full rounded-full bg-primary transition-all duration-300", style: { width: `${Math.max(5, loadPercent)}%` } }) })] })), _jsxs("div", { className: "flex flex-col gap-3 md:flex-row md:flex-wrap md:items-center md:justify-between", children: [_jsxs("div", { className: "grid grid-cols-[auto_minmax(0,1fr)] items-center gap-3", children: [_jsxs(Button, { variant: "outline", size: "lg", className: "h-12 shrink-0 px-3 sm:px-4", onClick: () => setLang(null), "aria-label": t("changeLanguage"), children: [_jsx(Globe, { className: "h-5 w-5 sm:mr-2" }), _jsx("span", { className: "hidden sm:inline", children: t("changeLanguage") })] }), _jsxs("div", { className: "min-w-0", children: [_jsx("h2", { className: "truncate text-lg font-bold text-foreground sm:text-xl", children: station.name }), _jsxs("p", { className: "truncate text-sm text-muted-foreground", children: [pendingCount, " ", t("statusToPrint"), " \u00B7 ", printedCount, " ", t("statusPrinted")] })] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Button, { variant: "outline", size: "lg", className: "h-12 px-3", onClick: () => shiftDay(-1), disabled: loadingDay || !(chosenDay ?? pickDay), "aria-label": ui.pickDayPrev, children: [_jsx(ChevronLeft, { className: "h-5 w-5 sm:mr-1" }), _jsx("span", { className: "hidden sm:inline", children: ui.pickDayPrev })] }), _jsx("div", { className: "flex h-12 items-center rounded-md border bg-muted px-4 text-sm font-semibold text-foreground", children: chosenDay ? dayLabel(chosenDay) : ui.pickDayToday }), _jsxs(Button, { variant: "outline", size: "lg", className: "h-12 px-3", onClick: () => shiftDay(1), disabled: loadingDay || !(chosenDay ?? pickDay), "aria-label": ui.pickDayNext, children: [_jsx("span", { className: "hidden sm:inline", children: ui.pickDayNext }), _jsx(ChevronRight, { className: "h-5 w-5 sm:ml-1" })] }), chosenDay && (_jsx(Button, { variant: "secondary", size: "lg", className: "h-12 px-4", onClick: () => void chooseDay("current"), disabled: loadingDay, children: ui.pickDayBackToToday }))] }), _jsxs("div", { className: "flex items-center gap-2", children: [!online && (_jsxs(Badge, { className: "bg-destructive text-destructive-foreground", children: [_jsx(WifiOff, { className: "mr-1 h-3.5 w-3.5" }), " ", t("offline")] })), _jsx(Button, { variant: "outline", size: "icon", className: "h-12 w-12 shrink-0", onClick: refresh, "aria-label": t("refresh"), children: _jsx(RefreshCw, { className: "h-4 w-4" }) }), _jsxs(Button, { variant: "outline", size: "lg", className: "hidden h-14 px-5 md:inline-flex", disabled: fetchingNotes, onClick: () => void fetchShippingNotes(), children: [fetchingNotes ? (_jsx(Loader2, { className: "mr-2 h-5 w-5 animate-spin" })) : (_jsx(FileDown, { className: "mr-2 h-5 w-5" })), fetchingNotes ? "Ophalen…" : "Verzendbrieven ophalen"] }), cartsPerPrint != null && (_jsxs(Button, { size: "lg", className: "hidden h-14 px-6 text-lg md:inline-flex", disabled: loading || batchPrinting || busyId != null, onClick: () => void printNextCarts(), children: [batchPrinting ? (_jsx(Loader2, { className: "mr-2 h-5 w-5 animate-spin" })) : (_jsx(Printer, { className: "mr-2 h-5 w-5" })), "Print eerstvolgende ", cartsPerPrint, " karren"] })), _jsxs(Button, { size: "lg", className: "hidden h-14 px-8 text-lg md:inline-flex", onClick: () => (pending ? confirmPending() :
|
|
1124
|
+
: `Pakbonnen controleren bij de bron (${checkDone} van ${checkTotal})` }), _jsxs("span", { className: "tabular-nums", children: [loadPercent, "%"] })] }), _jsx("div", { className: "h-1.5 w-full overflow-hidden rounded-full bg-muted", children: _jsx("div", { className: "h-full rounded-full bg-primary transition-all duration-300", style: { width: `${Math.max(5, loadPercent)}%` } }) })] })), _jsxs("div", { className: "flex flex-col gap-3 md:flex-row md:flex-wrap md:items-center md:justify-between", children: [_jsxs("div", { className: "grid grid-cols-[auto_minmax(0,1fr)] items-center gap-3", children: [_jsxs(Button, { variant: "outline", size: "lg", className: "h-12 shrink-0 px-3 sm:px-4", onClick: () => setLang(null), "aria-label": t("changeLanguage"), children: [_jsx(Globe, { className: "h-5 w-5 sm:mr-2" }), _jsx("span", { className: "hidden sm:inline", children: t("changeLanguage") })] }), _jsxs("div", { className: "min-w-0", children: [_jsx("h2", { className: "truncate text-lg font-bold text-foreground sm:text-xl", children: station.name }), _jsxs("p", { className: "truncate text-sm text-muted-foreground", children: [pendingCount, " ", t("statusToPrint"), " \u00B7 ", printedCount, " ", t("statusPrinted")] })] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Button, { variant: "outline", size: "lg", className: "h-12 px-3", onClick: () => shiftDay(-1), disabled: loadingDay || !(chosenDay ?? pickDay), "aria-label": ui.pickDayPrev, children: [_jsx(ChevronLeft, { className: "h-5 w-5 sm:mr-1" }), _jsx("span", { className: "hidden sm:inline", children: ui.pickDayPrev })] }), _jsx("div", { className: "flex h-12 items-center rounded-md border bg-muted px-4 text-sm font-semibold text-foreground", children: chosenDay ? dayLabel(chosenDay) : ui.pickDayToday }), _jsxs(Button, { variant: "outline", size: "lg", className: "h-12 px-3", onClick: () => shiftDay(1), disabled: loadingDay || !(chosenDay ?? pickDay), "aria-label": ui.pickDayNext, children: [_jsx("span", { className: "hidden sm:inline", children: ui.pickDayNext }), _jsx(ChevronRight, { className: "h-5 w-5 sm:ml-1" })] }), chosenDay && (_jsx(Button, { variant: "secondary", size: "lg", className: "h-12 px-4", onClick: () => void chooseDay("current"), disabled: loadingDay, children: ui.pickDayBackToToday }))] }), _jsxs("div", { className: "flex items-center gap-2", children: [!online && (_jsxs(Badge, { className: "bg-destructive text-destructive-foreground", children: [_jsx(WifiOff, { className: "mr-1 h-3.5 w-3.5" }), " ", t("offline")] })), _jsx(Button, { variant: "outline", size: "icon", className: "h-12 w-12 shrink-0", onClick: refresh, "aria-label": t("refresh"), children: _jsx(RefreshCw, { className: "h-4 w-4" }) }), _jsxs(Button, { variant: "outline", size: "lg", className: "hidden h-14 px-5 md:inline-flex", disabled: fetchingNotes, onClick: () => void fetchShippingNotes(), children: [fetchingNotes ? (_jsx(Loader2, { className: "mr-2 h-5 w-5 animate-spin" })) : (_jsx(FileDown, { className: "mr-2 h-5 w-5" })), fetchingNotes ? "Ophalen…" : "Verzendbrieven ophalen"] }), cartsPerPrint != null && (_jsxs(Button, { size: "lg", className: "hidden h-14 px-6 text-lg md:inline-flex", disabled: loading || batchPrinting || (busyId != null || actionBusy), onClick: () => void printNextCarts(), children: [batchPrinting ? (_jsx(Loader2, { className: "mr-2 h-5 w-5 animate-spin" })) : (_jsx(Printer, { className: "mr-2 h-5 w-5" })), "Print eerstvolgende ", cartsPerPrint, " karren"] })), _jsxs(Button, { size: "lg", className: "hidden h-14 px-8 text-lg md:inline-flex", onClick: () => (pending ? confirmPending() : fetchNextLocked()), disabled: (busyId !== null || actionBusy) || fetchingData, children: [(busyId !== null || actionBusy) || fetchingData ? (_jsx(Loader2, { className: "mr-2 h-5 w-5 animate-spin" })) : pending && !readiness.ready ? (_jsx(RefreshCw, { className: "mr-2 h-5 w-5" })) : (_jsx(Printer, { className: "mr-2 h-5 w-5" })), pending && !readiness.ready ? "Pakbon ophalen" : t("printNext"), pending?.document_number ? ` · ${pending.document_number}` : ""] })] })] }), syncError && (_jsxs("div", { role: "alert", className: "flex items-start gap-2 rounded-md border-2 border-destructive/50 bg-destructive/5 p-3 text-sm text-destructive", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0" }), _jsxs("span", { children: ["Pickwachtrij kan niet worden bijgewerkt: ", syncError] })] })), !plekLoading && !printPlek && (_jsxs("div", { className: "flex items-start gap-2 rounded-md border-2 border-destructive/50 bg-destructive/5 p-3 text-sm text-destructive", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0" }), _jsx("span", { children: "Nog geen printplek gekoppeld \u2014 de opdracht blijft in de wachtrij staan." })] })), cartMissing && (_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" }), CART_MISSING_MESSAGE] }), _jsx(Button, { variant: "outline", size: "sm", onClick: () => {
|
|
1125
|
+
setCartMissing(false);
|
|
1126
|
+
setPending(null);
|
|
1127
|
+
setCartPlan(null);
|
|
1128
|
+
setActiveCartId(null);
|
|
1129
|
+
refresh();
|
|
1130
|
+
}, children: "Wachtrij verversen" })] })), lastCartResults.length > 1 && (_jsxs("div", { className: "rounded-md border p-3 text-sm", "aria-label": "Resultaat per kar", children: [_jsx("p", { className: "mb-1 font-semibold text-foreground", children: "Resultaat per kar" }), _jsx("ul", { className: "space-y-0.5 text-muted-foreground", children: lastCartResults.map((r) => (_jsx("li", { className: r.outcome === "created" ? "text-foreground" : r.outcome === "error" ? "text-destructive" : "", children: cartResultLine(r) }, r.cartId))) })] })), _jsx(ShippingNoteScan, { stationId: station.id }), !pending && emptyNotice && (_jsx("p", { className: "text-sm text-muted-foreground", children: emptyNotice })), pending && (_jsxs(Card, { className: "border-2 border-primary", children: [_jsxs(CardContent, { className: "flex flex-col gap-4 p-4 sm:flex-row sm:flex-wrap sm:items-center sm:justify-between sm:p-5", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "text-sm font-medium text-muted-foreground", children: t("confirmPrintTitle") }), _jsx("p", { className: "truncate text-xl font-bold text-foreground sm:text-2xl", children: pending.document_number ?? t("document") }), _jsx("p", { className: "truncate text-sm text-muted-foreground", children: [pending.order_number, pending.customer_name].filter(Boolean).join(" · ") })] }), _jsxs("div", { className: "flex flex-col gap-2 sm:flex-row", children: [_jsx(Button, { variant: "outline", size: "lg", className: "h-12 w-full sm:w-auto", onClick: cancelPending, disabled: (busyId !== null || actionBusy), children: t("cancel") }), _jsxs(Button, { size: "lg", className: "h-14 w-full px-8 text-lg sm:w-auto", onClick: confirmPending, disabled: (busyId !== null || actionBusy) || fetchingData, variant: readiness.ready ? "default" : "secondary", children: [busyId === "confirm" || fetchingData ? (_jsx(Loader2, { className: "mr-2 h-5 w-5 animate-spin" })) : readiness.ready ? (_jsx(Printer, { className: "mr-2 h-5 w-5" })) : (_jsx(RefreshCw, { className: "mr-2 h-5 w-5" })), readiness.ready ? t("print") : "Pakbon ophalen"] })] })] }), !readiness.ready && (_jsx(CardContent, { className: "border-t p-4 sm:p-5", children: _jsxs("div", { className: "flex items-start gap-2 rounded-md border bg-muted/30 p-3 text-sm text-muted-foreground", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0" }), _jsx("span", { children: fetchingData
|
|
1034
1131
|
? "De pakbon wordt bij de bron opgevraagd. Zodra alles compleet is, verschijnt de printknop weer."
|
|
1035
1132
|
: planReason
|
|
1036
1133
|
? `${planReason} Klik op "Pakbon ophalen" om hem bij de bron op te vragen.`
|
|
1037
|
-
: NOT_READY_NOTICE })] }) })), cartPlan && (
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
setActiveCartId(cart.cartId);
|
|
1044
|
-
}
|
|
1045
|
-
}, className: "flex w-full cursor-pointer items-center justify-between gap-2 rounded-md border p-3 text-left transition hover:bg-muted/50 " +
|
|
1046
|
-
(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, onClick: (event) => {
|
|
1047
|
-
event.stopPropagation();
|
|
1134
|
+
: NOT_READY_NOTICE })] }) })), cartPlan && (_jsxs(CardContent, { className: "border-t p-4 sm:p-5", children: [_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) => {
|
|
1135
|
+
const done = printedCartIdsOf(pending).includes(cart.cartId);
|
|
1136
|
+
const active = cart.cartId === activeCartId;
|
|
1137
|
+
return (_jsxs("div", { role: "button", tabIndex: 0, onClick: () => setActiveCartId(cart.cartId), onKeyDown: (event) => {
|
|
1138
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
1139
|
+
event.preventDefault();
|
|
1048
1140
|
setActiveCartId(cart.cartId);
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1141
|
+
}
|
|
1142
|
+
}, className: "flex w-full cursor-pointer items-center justify-between gap-2 rounded-md border p-3 text-left transition hover:bg-muted/50 " +
|
|
1143
|
+
(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), onClick: (event) => {
|
|
1144
|
+
event.stopPropagation();
|
|
1145
|
+
setActiveCartId(cart.cartId);
|
|
1146
|
+
void printSingleCart(pending, cart);
|
|
1147
|
+
}, children: [_jsx(Printer, { className: "mr-2 h-4 w-4" }), done ? "Opnieuw" : "Printen"] })] }, cart.cartId));
|
|
1148
|
+
}) })] }), activeCartId && (() => {
|
|
1149
|
+
const activeCart = cartPlan.carts.find((cart) => cart.cartId === activeCartId);
|
|
1150
|
+
if (!activeCart)
|
|
1151
|
+
return null;
|
|
1152
|
+
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 }) })] }));
|
|
1153
|
+
})()] })] }))] })), 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` : ""] })] })] }) })), tracked && (_jsx(Card, { className: jobFailed ? "border-2 border-destructive" : "border-2 border-primary/40", children: _jsxs(CardContent, { className: "flex flex-col gap-4 p-4 sm:flex-row sm:flex-wrap sm:items-center sm:justify-between", children: [_jsxs("div", { className: "flex min-w-0 items-start gap-3", children: [jobFailed ? (_jsx(AlertTriangle, { className: "h-6 w-6 shrink-0 text-destructive" })) : jobStatus === "voltooid" ? (_jsx(CheckCircle2, { className: "h-6 w-6 shrink-0 text-primary" })) : (_jsx(Loader2, { className: "h-6 w-6 shrink-0 animate-spin text-muted-foreground" })), _jsxs("div", { className: "min-w-0", children: [_jsxs("p", { className: "truncate text-sm font-medium text-muted-foreground", children: [jobTexts.jobTitle, " ", tracked.documentNumber ? `· ${tracked.documentNumber}` : "", tracked.cartLabel ? ` · ${tracked.cartLabel}` : ""] }), _jsx("p", { className: "text-lg font-semibold text-foreground", children: printJobStatusLabel(jobStatus, t.lang) }), tracked.job?.printer_naam && (_jsxs("p", { className: "truncate text-sm text-muted-foreground", children: [jobTexts.printerLabel, ": ", tracked.job.printer_naam] })), 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-12 w-full sm:w-auto", onClick: () => {
|
|
1057
1154
|
clearJob();
|
|
1058
1155
|
void confirmPending();
|
|
1059
|
-
}, disabled: busyId !== null || !pending, children: [_jsx(Printer, { className: "mr-2 h-5 w-5" }), " ", jobTexts.retry] })), _jsx(Button, { variant: "outline", size: "lg", className: "h-12 w-full sm:w-auto", onClick: () => {
|
|
1156
|
+
}, disabled: (busyId !== null || actionBusy) || !pending, children: [_jsx(Printer, { className: "mr-2 h-5 w-5" }), " ", jobTexts.retry] })), _jsx(Button, { variant: "outline", size: "lg", className: "h-12 w-full sm:w-auto", onClick: () => {
|
|
1060
1157
|
clearJob();
|
|
1061
1158
|
clearShippingNotes();
|
|
1062
1159
|
}, 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")
|
|
@@ -1069,5 +1166,5 @@ export function PickScherm({ station, onBack }) {
|
|
|
1069
1166
|
return (_jsxs("div", { className: "flex items-start gap-3", children: [failed ? (_jsx(AlertTriangle, { className: "mt-0.5 h-5 w-5 shrink-0 text-destructive" })) : note.state.status === "geprint" ? (_jsx(CheckCircle2, { className: "mt-0.5 h-5 w-5 shrink-0 text-primary" })) : (_jsx(Loader2, { className: "mt-0.5 h-5 w-5 shrink-0 animate-spin text-muted-foreground" })), _jsxs("div", { className: "min-w-0", children: [(note.cartLabel || typeLabel) && (_jsx("p", { className: "truncate text-sm text-muted-foreground", children: [note.cartLabel, typeLabel].filter(Boolean).join(" · ") })), _jsx("p", { className: failed ? "text-sm font-medium text-destructive" : "text-sm text-foreground", children: message ?? "Verzendbriefstatus wordt opgehaald." }), waiting && (note.state.attempts !== null || note.state.nextAttemptAt) && (_jsxs("p", { className: "text-xs text-muted-foreground", children: [note.state.attempts !== null ? `Pogingen: ${note.state.attempts}` : "", note.state.attempts !== null && note.state.nextAttemptAt ? " · " : "", note.state.nextAttemptAt
|
|
1070
1167
|
? `Volgende poging: ${new Date(note.state.nextAttemptAt).toLocaleTimeString("nl-NL", { hour: "2-digit", minute: "2-digit" })}`
|
|
1071
1168
|
: ""] }))] })] }, note.jobId));
|
|
1072
|
-
})] }) })), _jsx(Card, { children: _jsxs(CardContent, { className: "flex flex-wrap items-center gap-3 p-3 sm:p-4", children: [_jsxs("div", { className: "relative w-full min-w-0 flex-1 sm:min-w-64", children: [_jsx(Search, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { className: "h-12 pl-9 text-base", placeholder: t("search"), value: searchInput, onChange: (e) => setSearchInput(e.target.value) })] }), _jsxs(Select, { value: chosenDay ?? "current", onValueChange: (v) => void chooseDay(v), children: [_jsx(SelectTrigger, { className: "h-12 w-full sm:w-56", disabled: loadingDay, children: _jsx(SelectValue, { placeholder: ui.pickDay }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "current", children: ui.pickDayToday }), safePickDays.map((d) => (_jsxs(SelectItem, { value: d.day, children: [dayLabel(d.day), d.orders > 0 ? ` (${d.orders})` : ""] }, d.day)))] })] }), _jsxs(Select, { value: filters.documentTypeId ?? "all", onValueChange: (v) => setFilters((f) => ({ ...f, documentTypeId: v === "all" ? null : v, page: 0 })), children: [_jsx(SelectTrigger, { className: "h-12 w-full sm:w-52", children: _jsx(SelectValue, { placeholder: ui.allDocumentTypes }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "all", children: ui.allDocumentTypes }), (Array.isArray(documentTypes) ? documentTypes : []).map((dt) => (_jsx(SelectItem, { value: dt.id, children: dt.name }, dt.id)))] })] }), _jsx(Button, { variant: filters.onlyPending ? "default" : "outline", className: "h-12", onClick: () => setFilters((f) => ({ ...f, onlyPending: !f.onlyPending, page: 0 })), children: ui.onlyPending }), _jsxs(Button, { variant: filters.urgentOnly ? "default" : "outline", className: "h-12", onClick: () => setFilters((f) => ({ ...f, urgentOnly: !f.urgentOnly, page: 0 })), children: [_jsx(Zap, { className: "mr-2 h-4 w-4" }), ui.onlyUrgent, urgentCount > 0 ? ` (${urgentCount})` : ""] })] }) }), !realtimeOk && (_jsx("p", { className: "text-sm text-muted-foreground", children: ui.connectionLost })), cartsPerPrint != null && (_jsx("div", { className: "flex justify-end md:hidden", children: _jsxs(Button, { className: "h-12", disabled: loading || batchPrinting || busyId != null, onClick: () => void printNextCarts(), children: [batchPrinting ? (_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" })) : (_jsx(Printer, { className: "mr-2 h-4 w-4" })), "Print eerstvolgende ", cartsPerPrint, " karren"] }) })), _jsx("div", { className: "md:hidden", children: _jsxs(Button, { variant: "outline", className: "h-12 w-full", disabled: fetchingNotes, onClick: () => void fetchShippingNotes(), children: [fetchingNotes ? (_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" })) : (_jsx(FileDown, { className: "mr-2 h-4 w-4" })), fetchingNotes ? "Ophalen…" : "Verzendbrieven ophalen"] }) }), loading ? (_jsxs("div", { className: "flex items-center justify-center py-16 text-muted-foreground", children: [_jsx(Loader2, { className: "mr-2 h-5 w-5 animate-spin" }), " ", t("loadingQueue")] })) : (_jsx(QueueTable, { items: items, lockedIds: lockedIds, statuses: Array.isArray(statuses) ? statuses : [], documentTypes: Array.isArray(documentTypes) ? documentTypes : [], busyId: busyId, readinessById: readinessById, onPick: handlePick, onReprint:
|
|
1169
|
+
})] }) })), _jsx(Card, { children: _jsxs(CardContent, { className: "flex flex-wrap items-center gap-3 p-3 sm:p-4", children: [_jsxs("div", { className: "relative w-full min-w-0 flex-1 sm:min-w-64", children: [_jsx(Search, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { className: "h-12 pl-9 text-base", placeholder: t("search"), value: searchInput, onChange: (e) => setSearchInput(e.target.value) })] }), _jsxs(Select, { value: chosenDay ?? "current", onValueChange: (v) => void chooseDay(v), children: [_jsx(SelectTrigger, { className: "h-12 w-full sm:w-56", disabled: loadingDay, children: _jsx(SelectValue, { placeholder: ui.pickDay }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "current", children: ui.pickDayToday }), safePickDays.map((d) => (_jsxs(SelectItem, { value: d.day, children: [dayLabel(d.day), d.orders > 0 ? ` (${d.orders})` : ""] }, d.day)))] })] }), _jsxs(Select, { value: filters.documentTypeId ?? "all", onValueChange: (v) => setFilters((f) => ({ ...f, documentTypeId: v === "all" ? null : v, page: 0 })), children: [_jsx(SelectTrigger, { className: "h-12 w-full sm:w-52", children: _jsx(SelectValue, { placeholder: ui.allDocumentTypes }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "all", children: ui.allDocumentTypes }), (Array.isArray(documentTypes) ? documentTypes : []).map((dt) => (_jsx(SelectItem, { value: dt.id, children: dt.name }, dt.id)))] })] }), _jsx(Button, { variant: filters.onlyPending ? "default" : "outline", className: "h-12", onClick: () => setFilters((f) => ({ ...f, onlyPending: !f.onlyPending, page: 0 })), children: ui.onlyPending }), _jsxs(Button, { variant: filters.urgentOnly ? "default" : "outline", className: "h-12", onClick: () => setFilters((f) => ({ ...f, urgentOnly: !f.urgentOnly, page: 0 })), children: [_jsx(Zap, { className: "mr-2 h-4 w-4" }), ui.onlyUrgent, urgentCount > 0 ? ` (${urgentCount})` : ""] })] }) }), !realtimeOk && (_jsx("p", { className: "text-sm text-muted-foreground", children: ui.connectionLost })), cartsPerPrint != null && (_jsx("div", { className: "flex justify-end md:hidden", children: _jsxs(Button, { className: "h-12", disabled: loading || batchPrinting || (busyId != null || actionBusy), onClick: () => void printNextCarts(), children: [batchPrinting ? (_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" })) : (_jsx(Printer, { className: "mr-2 h-4 w-4" })), "Print eerstvolgende ", cartsPerPrint, " karren"] }) })), _jsx("div", { className: "md:hidden", children: _jsxs(Button, { variant: "outline", className: "h-12 w-full", disabled: fetchingNotes, onClick: () => void fetchShippingNotes(), children: [fetchingNotes ? (_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" })) : (_jsx(FileDown, { className: "mr-2 h-4 w-4" })), fetchingNotes ? "Ophalen…" : "Verzendbrieven ophalen"] }) }), loading ? (_jsxs("div", { className: "flex items-center justify-center py-16 text-muted-foreground", children: [_jsx(Loader2, { className: "mr-2 h-5 w-5 animate-spin" }), " ", t("loadingQueue")] })) : (_jsx(QueueTable, { items: items, lockedIds: lockedIds, statuses: Array.isArray(statuses) ? statuses : [], documentTypes: Array.isArray(documentTypes) ? documentTypes : [], busyId: busyId, readinessById: readinessById, 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) })] }));
|
|
1073
1170
|
}
|