@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.
- package/dist-lib/components/fs-ui/DataTable.js +2 -2
- package/dist-lib/components/fs-ui/TableSkeleton.js +1 -1
- package/dist-lib/components/pick/PickScherm.d.ts.map +1 -1
- package/dist-lib/components/pick/PickScherm.js +377 -159
- package/dist-lib/components/pick/werkvloer/Werkvloer.d.ts +52 -0
- package/dist-lib/components/pick/werkvloer/Werkvloer.d.ts.map +1 -0
- package/dist-lib/components/pick/werkvloer/Werkvloer.js +33 -0
- package/dist-lib/components/settings/ReferentiedataPickdocumentenCard.d.ts.map +1 -1
- package/dist-lib/components/settings/ReferentiedataPickdocumentenCard.js +10 -3
- package/dist-lib/hooks/usePickPrintStatus.d.ts +1 -1
- package/dist-lib/hooks/usePickPrintStatus.d.ts.map +1 -1
- package/dist-lib/hooks/usePickPrintStatus.js +4 -4
- package/dist-lib/hooks/useShippingNoteStatus.d.ts +1 -1
- package/dist-lib/hooks/useShippingNoteStatus.d.ts.map +1 -1
- package/dist-lib/hooks/useShippingNoteStatus.js +11 -9
- package/dist-lib/hooks/useWerkvloer.d.ts +20 -0
- package/dist-lib/hooks/useWerkvloer.d.ts.map +1 -0
- package/dist-lib/hooks/useWerkvloer.js +98 -0
- package/dist-lib/integrations/supabase/types.d.ts +69 -0
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/__tests__/pickPublicStatus.test.d.ts +2 -0
- package/dist-lib/lib/__tests__/pickPublicStatus.test.d.ts.map +1 -0
- package/dist-lib/lib/__tests__/pickPublicStatus.test.js +48 -0
- package/dist-lib/lib/labels.d.ts +2 -0
- package/dist-lib/lib/labels.d.ts.map +1 -1
- package/dist-lib/lib/labels.js +41 -0
- package/dist-lib/lib/pick142.d.ts.map +1 -1
- package/dist-lib/lib/pick142.js +7 -10
- package/dist-lib/lib/pick156.d.ts.map +1 -1
- package/dist-lib/lib/pick156.js +6 -20
- package/dist-lib/lib/pick159.js +1 -1
- package/dist-lib/lib/pickApi.d.ts +20 -0
- package/dist-lib/lib/pickApi.d.ts.map +1 -1
- package/dist-lib/lib/pickApi.js +27 -0
- package/dist-lib/lib/shippingNoteStatus.d.ts +1 -1
- package/dist-lib/lib/shippingNoteStatus.d.ts.map +1 -1
- package/dist-lib/lib/shippingNoteStatus.js +5 -2
- package/dist-lib/lib/werkvloer.d.ts +40 -0
- package/dist-lib/lib/werkvloer.d.ts.map +1 -0
- package/dist-lib/lib/werkvloer.js +121 -0
- package/dist-lib/lib/werkvloer.test.d.ts +2 -0
- package/dist-lib/lib/werkvloer.test.d.ts.map +1 -0
- package/dist-lib/lib/werkvloer.test.js +49 -0
- package/package.json +2 -2
- package/schema/pickschermen.sql +176 -0
|
@@ -1,42 +1,39 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs
|
|
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 {
|
|
6
|
-
import {
|
|
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 {
|
|
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,
|
|
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,
|
|
33
|
-
import {
|
|
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 {
|
|
38
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
852
|
-
|
|
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
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
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
|
-
|
|
888
|
-
|
|
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
|
-
|
|
881
|
+
partial = results;
|
|
882
|
+
if (!quiet)
|
|
883
|
+
showCartResults(results);
|
|
900
884
|
throw e;
|
|
901
885
|
}
|
|
902
|
-
|
|
903
|
-
|
|
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
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
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
|
}
|