@seedtactics/insight-client 16.8.4-beta.10 → 16.8.4-beta.11

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.
@@ -49,6 +49,7 @@ export declare const serialInMaterialDialog: Atom<Promise<string | null>>;
49
49
  export declare const serialInMaterialDialogUnwrapped: Atom<string | null>;
50
50
  export declare const serialInMaterialDialogState: Atom<AsyncAtomState<string | null>>;
51
51
  export declare const workorderInMaterialDialog: Atom<Promise<string | null>>;
52
+ export declare const materialInDialogLocalEvents: Atom<readonly Readonly<ILogEntry>[]>;
52
53
  export declare const materialInDialogEvents: Atom<readonly Readonly<ILogEntry>[]>;
53
54
  export type LargestUsedProces = {
54
55
  readonly process: number;
@@ -96,9 +97,12 @@ export interface PrintLabelData {
96
97
  readonly proc: number;
97
98
  }
98
99
  export declare function usePrintLabel(): [(data: PrintLabelData) => void, boolean, boolean];
99
- export declare function useRemoveFromQueue(): [(matId: number, operator: string | null) => void, boolean];
100
+ export declare function useRemoveFromQueue(): [
101
+ (matId: number, operator: string | null) => Promise<void>,
102
+ boolean
103
+ ];
100
104
  export declare function useSignalForQuarantine(): [
101
- (matId: number, operator: string | null, reason: string) => void,
105
+ (matId: number, operator: string | null, reason: string) => Promise<void>,
102
106
  boolean
103
107
  ];
104
108
  export interface AddExistingMaterialToQueueData {
@@ -108,7 +112,7 @@ export interface AddExistingMaterialToQueueData {
108
112
  readonly operator: string | null;
109
113
  }
110
114
  export declare function useAddExistingMaterialToQueue(): [
111
- (d: AddExistingMaterialToQueueData) => void,
115
+ (d: AddExistingMaterialToQueueData) => Promise<void>,
112
116
  boolean
113
117
  ];
114
118
  export interface AddNewMaterialToQueueData {
@@ -122,7 +126,10 @@ export interface AddNewMaterialToQueueData {
122
126
  readonly onNewMaterial?: (mat: Readonly<IInProcessMaterial>) => void;
123
127
  readonly onError?: (reason: unknown) => void;
124
128
  }
125
- export declare function useAddNewMaterialToQueue(): [(d: AddNewMaterialToQueueData) => void, boolean];
129
+ export declare function useAddNewMaterialToQueue(): [
130
+ (d: AddNewMaterialToQueueData) => Promise<Readonly<IInProcessMaterial>>,
131
+ boolean
132
+ ];
126
133
  export interface AddNewCastingToQueueData {
127
134
  readonly casting: string;
128
135
  readonly quantity: number;
@@ -133,4 +140,7 @@ export interface AddNewCastingToQueueData {
133
140
  readonly onNewMaterial?: (mats: ReadonlyArray<Readonly<IInProcessMaterial>>) => void;
134
141
  readonly onError?: (reason: unknown) => void;
135
142
  }
136
- export declare function useAddNewCastingToQueue(): [(d: AddNewCastingToQueueData) => void, boolean];
143
+ export declare function useAddNewCastingToQueue(): [
144
+ (d: AddNewCastingToQueueData) => Promise<ReadonlyArray<Readonly<IInProcessMaterial>>>,
145
+ boolean
146
+ ];
@@ -121,8 +121,9 @@ export const inProcessMaterialInDialog = atom(async (get) => {
121
121
  const toShow = get(matToShow);
122
122
  if (toShow === null)
123
123
  return null;
124
- if (toShow.type === "InProcMat")
125
- return toShow.inproc;
124
+ if (toShow.type === "InProcMat") {
125
+ return status.material.find((m) => m.materialID === toShow.inproc.materialID) ?? null;
126
+ }
126
127
  const matId = (await get(materialInDialogInfo))?.materialID ?? null;
127
128
  return matId !== null && matId >= 0
128
129
  ? (status.material.find((m) => m.materialID === matId) ?? null)
@@ -210,6 +211,9 @@ const otherMatEvents = atom(async (get, { signal }) => {
210
211
  return evts;
211
212
  });
212
213
  const otherMatEventsUnwrapped = unwrap(otherMatEvents, (prev) => prev ?? []);
214
+ export const materialInDialogLocalEvents = atom((get) => {
215
+ return LazySeq.of(get(localMatEventsUnwrapped)).concat(get(extraLogEventsFromUpdates)).toRArray();
216
+ });
213
217
  export const materialInDialogEvents = atom((get) => {
214
218
  const evtsFromUpdate = get(extraLogEventsFromUpdates);
215
219
  return LazySeq.of(get(localMatEventsUnwrapped))
@@ -373,66 +377,83 @@ export function usePrintLabel() {
373
377
  }
374
378
  export function useRemoveFromQueue() {
375
379
  const [updating, setUpdating] = useState(false);
376
- const callback = useCallback((matId, operator) => {
380
+ const callback = useCallback(async (matId, operator) => {
377
381
  setUpdating(true);
378
- JobsBackend.removeMaterialFromAllQueues(matId, operator ?? undefined)
379
- .catch(console.log)
380
- .finally(() => setUpdating(false));
382
+ try {
383
+ await JobsBackend.removeMaterialFromAllQueues(matId, operator ?? undefined);
384
+ }
385
+ finally {
386
+ setUpdating(false);
387
+ }
381
388
  }, []);
382
389
  return [callback, updating];
383
390
  }
384
391
  export function useSignalForQuarantine() {
385
392
  const [updating, setUpdating] = useState(false);
386
- const callback = useCallback((matId, operator, reason) => {
393
+ const callback = useCallback(async (matId, operator, reason) => {
387
394
  setUpdating(true);
388
- JobsBackend.signalMaterialForQuarantine(matId, operator, reason === "" ? undefined : reason)
389
- .catch(console.log)
390
- .finally(() => setUpdating(false));
395
+ try {
396
+ await JobsBackend.signalMaterialForQuarantine(matId, operator, reason === "" ? undefined : reason);
397
+ }
398
+ finally {
399
+ setUpdating(false);
400
+ }
391
401
  }, []);
392
402
  return [callback, updating];
393
403
  }
394
404
  export function useAddExistingMaterialToQueue() {
395
405
  const [updating, setUpdating] = useState(false);
396
- const callback = useCallback((d) => {
406
+ const callback = useCallback(async (d) => {
397
407
  setUpdating(true);
398
- JobsBackend.setMaterialInQueue(d.materialId, d.operator, new QueuePosition({
399
- queue: d.queue,
400
- position: d.queuePosition,
401
- }))
402
- .catch(console.log)
403
- .finally(() => setUpdating(false));
408
+ try {
409
+ await JobsBackend.setMaterialInQueue(d.materialId, d.operator, new QueuePosition({
410
+ queue: d.queue,
411
+ position: d.queuePosition,
412
+ }));
413
+ }
414
+ finally {
415
+ setUpdating(false);
416
+ }
404
417
  }, []);
405
418
  return [callback, updating];
406
419
  }
407
420
  export function useAddNewMaterialToQueue() {
408
421
  const [updating, setUpdating] = useState(false);
409
- const callback = useCallback((d) => {
422
+ const callback = useCallback(async (d) => {
410
423
  setUpdating(true);
411
- JobsBackend.addUnprocessedMaterialToQueue(d.jobUnique, d.lastCompletedProcess, d.queue, d.queuePosition, d.operator, d.workorder, d.serial || "")
412
- .then((m) => {
413
- if (d.onNewMaterial && m) {
414
- d.onNewMaterial(m);
415
- }
416
- else if (d.onError) {
417
- d.onError("No material returned");
418
- }
419
- }, d.onError)
420
- .catch(console.log)
421
- .finally(() => setUpdating(false));
424
+ try {
425
+ const material = await JobsBackend.addUnprocessedMaterialToQueue(d.jobUnique, d.lastCompletedProcess, d.queue, d.queuePosition, d.operator, d.workorder, d.serial || "");
426
+ if (material === undefined)
427
+ throw new Error("No material returned");
428
+ d.onNewMaterial?.(material);
429
+ return material;
430
+ }
431
+ catch (e) {
432
+ d.onError?.(e);
433
+ throw e;
434
+ }
435
+ finally {
436
+ setUpdating(false);
437
+ }
422
438
  }, []);
423
439
  return [callback, updating];
424
440
  }
425
441
  export function useAddNewCastingToQueue() {
426
442
  const [updating, setUpdating] = useState(false);
427
- const callback = useCallback((d) => {
443
+ const callback = useCallback(async (d) => {
428
444
  setUpdating(true);
429
- JobsBackend.addUnallocatedCastingToQueue(d.casting, d.queue, d.quantity, d.operator, d.workorder, [...(d.serials || [])])
430
- .then((ms) => {
431
- if (d.onNewMaterial)
432
- d.onNewMaterial(ms);
433
- }, d.onError)
434
- .catch(console.log)
435
- .finally(() => setUpdating(false));
445
+ try {
446
+ const material = await JobsBackend.addUnallocatedCastingToQueue(d.casting, d.queue, d.quantity, d.operator, d.workorder, [...(d.serials || [])]);
447
+ d.onNewMaterial?.(material);
448
+ return material;
449
+ }
450
+ catch (e) {
451
+ d.onError?.(e);
452
+ throw e;
453
+ }
454
+ finally {
455
+ setUpdating(false);
456
+ }
436
457
  }, []);
437
458
  return [callback, updating];
438
459
  }
@@ -43,6 +43,7 @@ import { InvalidateCycleDialogButton, InvalidateCycleDialogContent, SwapMaterial
43
43
  import { horizontalListSortingStrategy, SortableContext, useSortable, verticalListSortingStrategy, } from "@dnd-kit/sortable";
44
44
  import { closestCenter, DndContext, DragOverlay, getFirstCollision, MeasuringStrategy, pointerWithin, rectIntersection, } from "@dnd-kit/core";
45
45
  import { QuarantineMatButton } from "../station-monitor/QuarantineButton.js";
46
+ import { CancelLoadButton } from "../station-monitor/CancelLoadButton.js";
46
47
  import { useSetTitle } from "../routes.js";
47
48
  import { useAtom, useAtomValue, useSetAtom } from "jotai";
48
49
  import { fmsInformation } from "../../network/server-settings.js";
@@ -152,7 +153,7 @@ const AllMatDialog = memo(function AllMatDialog() {
152
153
  setSwapSt(null);
153
154
  setInvalidateSt(null);
154
155
  }
155
- return (_jsx(MaterialDialog, { onClose: onClose, allowNote: true, highlightProcsGreaterOrEqualTo: invalidateSt?.process ?? undefined, extraDialogElements: _jsxs(_Fragment, { children: [_jsx(SwapMaterialDialogContent, { st: swapSt, setState: setSwapSt }), invalidateSt !== null ? (_jsx(InvalidateCycleDialogContent, { st: invalidateSt, setState: setInvalidateSt })) : null] }), buttons: _jsxs(_Fragment, { children: [_jsx(QuarantineMatButton, { onClose: onClose, ignoreOperator: true }), _jsx(SwapMaterialButtons, { st: swapSt, setState: setSwapSt, onClose: onClose, ignoreOperator: true }), _jsx(InvalidateCycleDialogButton, { st: invalidateSt, setState: setInvalidateSt, onClose: onClose, ignoreOperator: true })] }) }));
156
+ return (_jsx(MaterialDialog, { onClose: onClose, allowNote: true, highlightProcsGreaterOrEqualTo: invalidateSt?.process ?? undefined, extraDialogElements: _jsxs(_Fragment, { children: [_jsx(SwapMaterialDialogContent, { st: swapSt, setState: setSwapSt }), invalidateSt !== null ? (_jsx(InvalidateCycleDialogContent, { st: invalidateSt, setState: setInvalidateSt })) : null] }), buttons: _jsxs(_Fragment, { children: [_jsx(QuarantineMatButton, { onClose: onClose, ignoreOperator: true }), _jsx(CancelLoadButton, { onClose: onClose, ignoreOperator: true }), _jsx(SwapMaterialButtons, { st: swapSt, setState: setSwapSt, onClose: onClose, ignoreOperator: true }), _jsx(InvalidateCycleDialogButton, { st: invalidateSt, setState: setInvalidateSt, onClose: onClose, ignoreOperator: true })] }) }));
156
157
  });
157
158
  function useCollisionDetection(allBins) {
158
159
  return useCallback((args) => {
@@ -260,12 +261,12 @@ export function AllMaterial(props) {
260
261
  ? findQueueInQuarantineQueues(over.id, allBins)
261
262
  : findMaterialInQuarantineQueues(over.id, allBins);
262
263
  if (overCol) {
263
- addExistingMatToQueue({
264
+ void addExistingMatToQueue({
264
265
  materialId: activeDrag.mat.materialID,
265
266
  queue: overCol.bin.queueName,
266
267
  queuePosition: overCol.idx,
267
268
  operator: null,
268
- });
269
+ }).catch(console.error);
269
270
  reorderQueuedMat({
270
271
  queue: overCol.bin.queueName,
271
272
  matId: activeDrag.mat.materialID,
@@ -122,20 +122,20 @@ export const BulkAddCastingWithoutSerialDialog = memo(function BulkAddCastingWit
122
122
  enteredQty !== null &&
123
123
  !isNaN(enteredQty) &&
124
124
  enteredQty > 0) {
125
- addNewCasting({
125
+ void addNewCasting({
126
126
  casting: selectedCasting,
127
127
  quantity: enteredQty,
128
128
  queue: queue,
129
129
  workorder: null,
130
130
  operator: fmsInfo.requireOperatorNamePromptWhenAddingMaterial ? enteredOperator : operator,
131
- });
131
+ }).catch(console.log);
132
132
  }
133
133
  close();
134
134
  }
135
135
  function addAndPrint() {
136
136
  if (queue !== null && selectedCasting !== null && enteredQty !== null && !isNaN(enteredQty)) {
137
137
  setAdding(true);
138
- addNewCasting({
138
+ void addNewCasting({
139
139
  casting: selectedCasting,
140
140
  quantity: enteredQty,
141
141
  queue: queue,
@@ -154,7 +154,7 @@ export const BulkAddCastingWithoutSerialDialog = memo(function BulkAddCastingWit
154
154
  onError: () => {
155
155
  close();
156
156
  },
157
- });
157
+ }).catch(() => { });
158
158
  }
159
159
  }
160
160
  return (_jsxs(_Fragment, { children: [_jsxs(Dialog, { open: queue !== null, onClose: () => close(), maxWidth: "md", children: [_jsx(DialogTitle, { children: "Add Raw Material" }), _jsx(DialogContent, { children: _jsxs(Stack, { direction: "column", spacing: 2, sx: {
@@ -0,0 +1,4 @@
1
+ export declare function CancelLoadButton({ onClose, ignoreOperator, }: {
2
+ readonly onClose?: () => void;
3
+ readonly ignoreOperator?: boolean;
4
+ }): import("react").JSX.Element | null;
@@ -0,0 +1,59 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Button, Dialog, DialogActions, DialogContent, DialogTitle, TextField, Tooltip, } from "@mui/material";
3
+ import { useAtomValue, useSetAtom } from "jotai";
4
+ import { useState } from "react";
5
+ import { LazySeq } from "@seedtactics/immutable-collections";
6
+ import { currentStatus } from "../../cell-status/current-status.js";
7
+ import { inProcessMaterialInDialog, materialDialogOpen, } from "../../cell-status/material-details.js";
8
+ import { currentOperator } from "../../data/operators.js";
9
+ import { JobsBackend } from "../../network/backend.js";
10
+ import { ApiException, CancelLoadRequest } from "../../network/api.js";
11
+ export function CancelLoadButton({ onClose, ignoreOperator = false, }) {
12
+ const material = useAtomValue(inProcessMaterialInDialog);
13
+ const status = useAtomValue(currentStatus);
14
+ const operator = useAtomValue(currentOperator);
15
+ const setMaterialDialogOpen = useSetAtom(materialDialogOpen);
16
+ const [open, setOpen] = useState(false);
17
+ const [reason, setReason] = useState("");
18
+ const [updating, setUpdating] = useState(false);
19
+ const [error, setError] = useState(null);
20
+ const [snapshot, setSnapshot] = useState(null);
21
+ const cancellationId = material?.action.loadCancellationId ?? null;
22
+ const currentSnapshot = material === null || cancellationId === null
23
+ ? null
24
+ : {
25
+ material,
26
+ cancellationId,
27
+ group: LazySeq.of(status.material)
28
+ .filter((candidate) => candidate.action.loadCancellationId === cancellationId)
29
+ .toRArray(),
30
+ };
31
+ if (currentSnapshot === null && snapshot === null)
32
+ return null;
33
+ async function cancelLoad() {
34
+ if (snapshot === null)
35
+ return;
36
+ setUpdating(true);
37
+ setError(null);
38
+ try {
39
+ await JobsBackend.cancelLoad(snapshot.material.materialID, ignoreOperator ? null : operator, new CancelLoadRequest({
40
+ expectedLoadCancellationId: snapshot.cancellationId,
41
+ reason: reason || undefined,
42
+ }));
43
+ setMaterialDialogOpen(null);
44
+ onClose?.();
45
+ }
46
+ catch (e) {
47
+ setError(ApiException.isApiException(e) ? e.response : e instanceof Error ? e.message : String(e));
48
+ }
49
+ finally {
50
+ setUpdating(false);
51
+ }
52
+ }
53
+ return (_jsxs(_Fragment, { children: [currentSnapshot ? (_jsx(Tooltip, { title: "Cancel the displayed load instruction", children: _jsx(Button, { color: "primary", disabled: updating, onClick: () => {
54
+ setSnapshot(currentSnapshot);
55
+ setReason("");
56
+ setError(null);
57
+ setOpen(true);
58
+ }, children: "Cancel Load" }) })) : null, _jsxs(Dialog, { open: open && snapshot !== null, onClose: () => setOpen(false), children: [_jsx(DialogTitle, { children: "Cancel Load" }), _jsxs(DialogContent, { children: [_jsx("p", { children: "The displayed load instruction will be cancelled and recalculated for:" }), _jsx("ul", { children: snapshot?.group.map((candidate) => (_jsx("li", { children: candidate.serial || `Material ID ${candidate.materialID}` }, candidate.materialID))) }), _jsx(TextField, { label: "Reason (optional)", value: reason, onChange: (e) => setReason(e.target.value), fullWidth: true, multiline: true }), error ? _jsx("p", { role: "alert", children: error }) : null] }), _jsxs(DialogActions, { children: [_jsx(Button, { color: "primary", disabled: updating, onClick: cancelLoad, children: "Cancel Load" }), _jsx(Button, { color: "secondary", disabled: updating, onClick: () => setOpen(false), children: "Keep Load" })] })] })] }));
59
+ }
@@ -44,6 +44,7 @@ import { materialDialogOpen, materialInDialogInfo, materialInDialogLargestUsedPr
44
44
  import { last30MaterialSummary, } from "../../cell-status/material-summary.js";
45
45
  import { instructionUrl } from "../../network/backend.js";
46
46
  import { QuarantineMatButton } from "./QuarantineButton.js";
47
+ import { CancelLoadButton } from "./CancelLoadButton.js";
47
48
  import { useIsDemo, useSetTitle } from "../routes.js";
48
49
  import { atom, useAtom, useAtomValue, useSetAtom } from "jotai";
49
50
  function CompleteButton() {
@@ -108,7 +109,7 @@ function AssignWorkorderButton() {
108
109
  return (_jsx(Button, { color: "primary", onClick: () => setWorkorderDialogOpen(true), children: "Assign Workorder" }));
109
110
  }
110
111
  const CloseoutMaterialDialog = memo(function CloseoutDialog() {
111
- return (_jsx(MaterialDialog, { allowNote: true, buttons: _jsxs(_Fragment, { children: [_jsx(InstrButton, {}), _jsx(QuarantineMatButton, {}), _jsx(AssignWorkorderButton, {}), _jsx(CompleteButton, {})] }) }));
112
+ return (_jsx(MaterialDialog, { allowNote: true, buttons: _jsxs(_Fragment, { children: [_jsx(InstrButton, {}), _jsx(QuarantineMatButton, {}), _jsx(CancelLoadButton, {}), _jsx(AssignWorkorderButton, {}), _jsx(CompleteButton, {})] }) }));
112
113
  });
113
114
  const currentNearestMinutes = atom(new Date());
114
115
  currentNearestMinutes.onMount = (set) => {
@@ -43,6 +43,7 @@ import { last30MaterialSummary } from "../../cell-status/material-summary.js";
43
43
  import { LazySeq } from "@seedtactics/immutable-collections";
44
44
  import { instructionUrl } from "../../network/backend.js";
45
45
  import { QuarantineMatButton } from "./QuarantineButton.js";
46
+ import { CancelLoadButton } from "./CancelLoadButton.js";
46
47
  import { useIsDemo, useSetTitle } from "../routes.js";
47
48
  import { useAtomValue, useSetAtom } from "jotai";
48
49
  function InspButtons(props) {
@@ -67,7 +68,7 @@ function InspButtons(props) {
67
68
  setMatToShow(null);
68
69
  }
69
70
  const url = instructionUrl(material.partName, props.inspection_type, material.materialID, null, maxProc, operator);
70
- return (_jsxs(_Fragment, { children: [material.partName !== "" ? (demo ? (_jsx(Button, { color: "primary", children: "Instructions" })) : (_jsx(Button, { href: url, target: "bms-instructions", color: "primary", children: "Instructions" }))) : undefined, _jsx(QuarantineMatButton, {}), _jsxs(Button, { color: "primary", disabled: completeInspUpdating, onClick: () => markInspComplete(true), children: ["Mark ", props.inspection_type, " Success"] }), _jsxs(Button, { color: "primary", disabled: completeInspUpdating, onClick: () => markInspComplete(false), children: ["Mark ", props.inspection_type, " Failed"] })] }));
71
+ return (_jsxs(_Fragment, { children: [material.partName !== "" ? (demo ? (_jsx(Button, { color: "primary", children: "Instructions" })) : (_jsx(Button, { href: url, target: "bms-instructions", color: "primary", children: "Instructions" }))) : undefined, _jsx(QuarantineMatButton, {}), _jsx(CancelLoadButton, {}), _jsxs(Button, { color: "primary", disabled: completeInspUpdating, onClick: () => markInspComplete(true), children: ["Mark ", props.inspection_type, " Success"] }), _jsxs(Button, { color: "primary", disabled: completeInspUpdating, onClick: () => markInspComplete(false), children: ["Mark ", props.inspection_type, " Failed"] })] }));
71
72
  }
72
73
  function DialogBodyInspButtons({ focusInspectionType }) {
73
74
  const material = useAtomValue(matDetails.materialInDialogInspections);
@@ -1,15 +1,18 @@
1
- import { IInProcessMaterial } from "../../network/api.js";
1
+ import { IInProcessMaterial, ILogEntry } from "../../network/api.js";
2
+ import { HashMap } from "@seedtactics/immutable-collections";
2
3
  import { ReactNode } from "react";
3
4
  export type InvalidateCycleState = {
4
5
  readonly process: number | null;
5
6
  readonly changeRawMat: string | null;
6
7
  readonly changeJobUnique: string | null;
7
8
  readonly updating: boolean;
9
+ readonly error: string | null;
8
10
  };
9
11
  export type InvalidateCycleProps = {
10
12
  readonly st: InvalidateCycleState | null;
11
13
  readonly setState: (s: InvalidateCycleState | null) => void;
12
14
  };
15
+ export declare function affectedMaterialForInvalidation(events: ReadonlyArray<Readonly<ILogEntry>>, materialId: number, process: number | null): HashMap<number, string | null>;
13
16
  export declare function InvalidateCycleDialogContent(props: InvalidateCycleProps): import("react").JSX.Element;
14
17
  export declare function InvalidateCycleDialogButton(props: InvalidateCycleProps & {
15
18
  readonly onClose: () => void;
@@ -34,17 +34,41 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34
34
  import { Box, Button, ListItemIcon, ListItemText, ListSubheader, Stack } from "@mui/material";
35
35
  import { MenuItem } from "@mui/material";
36
36
  import { TextField } from "@mui/material";
37
- import { ActionType, LocType } from "../../network/api.js";
37
+ import { ActionType, LocType, LogType, } from "../../network/api.js";
38
38
  import { JobsBackend } from "../../network/backend.js";
39
- import { LazySeq } from "@seedtactics/immutable-collections";
39
+ import { HashMap, LazySeq } from "@seedtactics/immutable-collections";
40
40
  import { currentStatus } from "../../cell-status/current-status.js";
41
- import { barcodePotentialNewMaterial, inProcessMaterialInDialog, materialDialogOpen, materialInDialogInfo, materialInDialogLargestUsedProcess, } from "../../cell-status/material-details.js";
41
+ import { barcodePotentialNewMaterial, inProcessMaterialInDialog, materialDialogOpen, materialInDialogInfo, materialInDialogLargestUsedProcess, materialInDialogLocalEvents, } from "../../cell-status/material-details.js";
42
42
  import { currentOperator } from "../../data/operators.js";
43
43
  import { fmsInformation } from "../../network/server-settings.js";
44
44
  import { useAtomValue, useSetAtom } from "jotai";
45
45
  import { useEffect, useRef } from "react";
46
46
  import { last30Jobs } from "../../cell-status/scheduled-jobs.js";
47
47
  import { PartIdenticon } from "./Material.js";
48
+ import { isLogEntryInvalidated } from "../LogEntry.js";
49
+ import { ApiException } from "../../network/api.js";
50
+ const invalidatableEventTypes = LazySeq.of([
51
+ LogType.AddToQueue,
52
+ LogType.RemoveFromQueue,
53
+ LogType.LoadUnloadCycle,
54
+ LogType.MachineCycle,
55
+ LogType.BasketLoadUnload,
56
+ LogType.BasketCycle,
57
+ ]).toRSet((eventType) => eventType);
58
+ export function affectedMaterialForInvalidation(events, materialId, process) {
59
+ let affected = HashMap.empty();
60
+ for (const event of events) {
61
+ if (!invalidatableEventTypes.has(event.type) ||
62
+ isLogEntryInvalidated(event) ||
63
+ !event.material.some((material) => material.id === materialId && (process === null || material.proc >= process))) {
64
+ continue;
65
+ }
66
+ for (const material of event.material) {
67
+ affected = affected.set(material.id, material.serial ?? null);
68
+ }
69
+ }
70
+ return affected;
71
+ }
48
72
  function InvalidateSelect(props) {
49
73
  const lastMat = useAtomValue(materialInDialogLargestUsedProcess);
50
74
  const possible = useAtomValue(barcodePotentialNewMaterial);
@@ -63,12 +87,13 @@ function InvalidateSelect(props) {
63
87
  function change(e) {
64
88
  props.setState({
65
89
  updating: false,
90
+ error: null,
66
91
  process: e.startsWith("proc") ? parseInt(e.substring(4)) : null,
67
92
  changeRawMat: e.startsWith("rawMat") ? e.substring(6) : null,
68
93
  changeJobUnique: e.startsWith("job") ? e.substring(3) : null,
69
94
  });
70
95
  }
71
- return (_jsx(TextField, { value: props.st?.process
96
+ return (_jsx(TextField, { value: props.st?.process && hasProc
72
97
  ? "proc" + props.st.process.toString()
73
98
  : props.st?.changeRawMat
74
99
  ? "rawMat" + props.st.changeRawMat
@@ -104,6 +129,8 @@ function InvalidateSelect(props) {
104
129
  }
105
130
  export function InvalidateCycleDialogContent(props) {
106
131
  const curMat = useAtomValue(materialInDialogInfo);
132
+ const events = useAtomValue(materialInDialogLocalEvents);
133
+ const status = useAtomValue(currentStatus);
107
134
  const show = props.st !== null && curMat !== null;
108
135
  const boxRef = useRef(null);
109
136
  useEffect(() => {
@@ -114,7 +141,17 @@ export function InvalidateCycleDialogContent(props) {
114
141
  }, [boxRef, show]);
115
142
  if (!show)
116
143
  return _jsx("div", {});
117
- return (_jsx(Box, { ref: boxRef, sx: { display: "flex", flexDirection: "column", alignItems: "center", marginTop: "1em" }, children: _jsxs(Stack, { spacing: 2, children: [_jsx("p", { style: { maxWidth: "35em" }, children: "An invalidated cycle remains in the event log, but is not considered when determining the next process to be machined on a piece of material." }), _jsx(InvalidateSelect, { st: props.st, setState: props.setState })] }) }));
144
+ const process = props.st.process ?? (props.st.changeRawMat || props.st.changeJobUnique ? null : undefined);
145
+ const affected = process === undefined
146
+ ? HashMap.empty()
147
+ : affectedMaterialForInvalidation(events, curMat.materialID, process);
148
+ const affectedLabels = [...affected].map(([materialId, serial]) => ({
149
+ materialId,
150
+ serial,
151
+ queued: status.material.find((material) => material.materialID === materialId)?.location.type ===
152
+ LocType.InQueue,
153
+ }));
154
+ return (_jsx(Box, { ref: boxRef, sx: { display: "flex", flexDirection: "column", alignItems: "center", marginTop: "1em" }, children: _jsxs(Stack, { spacing: 2, children: [_jsx("p", { style: { maxWidth: "35em" }, children: "Invalidating removes whole recorded cycle events from active material reconstruction while retaining them in the event log." }), _jsx(InvalidateSelect, { st: props.st, setState: props.setState }), affectedLabels.length > 0 ? (_jsxs(_Fragment, { children: [_jsx("p", { children: "All material recorded on these events will be affected:" }), _jsx("ul", { children: affectedLabels.map((material) => (_jsxs("li", { children: [material.serial || `Material ID ${material.materialId}`, material.queued ? " (currently in a queue)" : ""] }, material.materialId))) })] })) : null, props.st.error ? _jsx("p", { role: "alert", children: props.st.error }) : null] }) }));
118
155
  }
119
156
  export function InvalidateCycleDialogButton(props) {
120
157
  const fmsInfo = useAtomValue(fmsInformation);
@@ -132,6 +169,8 @@ export function InvalidateCycleDialogButton(props) {
132
169
  return null;
133
170
  if (inProcMat && inProcMat.location.type === LocType.OnPallet)
134
171
  return null;
172
+ if (inProcMat && inProcMat.location.type === LocType.InQueue)
173
+ return null;
135
174
  const allowChange = possibleNew !== null &&
136
175
  (!LazySeq.ofObject(possibleNew.possibleCastingsByQueue ?? {}).isEmpty() ||
137
176
  !LazySeq.ofObject(possibleNew.possibleJobsByQueue ?? {}).isEmpty());
@@ -140,7 +179,13 @@ export function InvalidateCycleDialogButton(props) {
140
179
  if (props.ignoreOperator)
141
180
  operator = null;
142
181
  function showInvalidate() {
143
- props.setState({ process: null, updating: false, changeRawMat: null, changeJobUnique: null });
182
+ props.setState({
183
+ process: null,
184
+ updating: false,
185
+ error: null,
186
+ changeRawMat: null,
187
+ changeJobUnique: null,
188
+ });
144
189
  }
145
190
  function invalidateCycle() {
146
191
  if (curMat && (props.st?.process || props.st?.changeRawMat || props.st?.changeJobUnique)) {
@@ -152,7 +197,7 @@ export function InvalidateCycleDialogButton(props) {
152
197
  type: "MatDetails",
153
198
  details: mat,
154
199
  });
155
- props.onClose();
200
+ props.setState(null);
156
201
  }
157
202
  else {
158
203
  setMatToShow(null);
@@ -160,9 +205,15 @@ export function InvalidateCycleDialogButton(props) {
160
205
  }
161
206
  })
162
207
  .catch((e) => {
163
- console.log(e);
164
- setMatToShow(null);
165
- props.onClose();
208
+ props.setState({
209
+ ...props.st,
210
+ updating: false,
211
+ error: ApiException.isApiException(e)
212
+ ? e.response
213
+ : e instanceof Error
214
+ ? e.message
215
+ : String(e),
216
+ });
166
217
  });
167
218
  }
168
219
  }
@@ -51,6 +51,7 @@ import { fmsInformation } from "../../network/server-settings.js";
51
51
  import { currentStatus, secondsSinceEpochAtom } from "../../cell-status/current-status.js";
52
52
  import { useIsDemo, useSetTitle } from "../routes.js";
53
53
  import { QuarantineMatButton } from "./QuarantineButton.js";
54
+ import { CancelLoadButton } from "./CancelLoadButton.js";
54
55
  import { durationToSeconds } from "../../util/parseISODuration.js";
55
56
  import { formatSeconds } from "./SystemOverview.js";
56
57
  import { InvalidateCycleDialogButton, InvalidateCycleDialogContent, SwapMaterialButtons, SwapMaterialDialogContent, } from "./InvalidateCycle.js";
@@ -540,6 +541,7 @@ function AddMatButton({ queues, onClose, toQueue, showAddToQueue, setShowAddToQu
540
541
  const inProcMat = useAtomValue(matDetails.inProcessMaterialInDialog);
541
542
  const operator = useAtomValue(currentOperator);
542
543
  const [addExistingMat, addingExistingMat] = matDetails.useAddExistingMaterialToQueue();
544
+ const [addError, setAddError] = useState(null);
543
545
  if (!existingMat ||
544
546
  inProcMat?.location.type === api.LocType.OnPallet ||
545
547
  inProcMat?.action.type === api.ActionType.Loading ||
@@ -557,16 +559,26 @@ function AddMatButton({ queues, onClose, toQueue, showAddToQueue, setShowAddToQu
557
559
  return (_jsx(Button, { color: "primary", onClick: () => setShowAddToQueue(true), children: "Add To Queue" }));
558
560
  }
559
561
  else {
560
- return (_jsxs(Button, { color: "primary", disabled: toQueue === null || addingExistingMat, onClick: () => {
561
- addExistingMat({
562
- materialId: existingMat.materialID,
563
- queue: toQueue ?? "",
564
- queuePosition: -1,
565
- operator: operator,
566
- });
567
- setMatToShow(null);
568
- onClose();
569
- }, children: ["Add To ", toQueue ?? "Queue", " ", lastProcMat ? ` To Run Process ${lastProcMat.process + 1}` : ""] }));
562
+ return (_jsxs(_Fragment, { children: [_jsxs(Button, { color: "primary", disabled: toQueue === null || addingExistingMat, onClick: async () => {
563
+ setAddError(null);
564
+ try {
565
+ await addExistingMat({
566
+ materialId: existingMat.materialID,
567
+ queue: toQueue ?? "",
568
+ queuePosition: -1,
569
+ operator: operator,
570
+ });
571
+ setMatToShow(null);
572
+ onClose();
573
+ }
574
+ catch (e) {
575
+ setAddError(api.ApiException.isApiException(e)
576
+ ? e.response
577
+ : e instanceof Error
578
+ ? e.message
579
+ : String(e));
580
+ }
581
+ }, children: ["Add To ", toQueue ?? "Queue", " ", lastProcMat ? ` To Run Process ${lastProcMat.process + 1}` : ""] }), addError ? _jsx(Typography, { role: "alert", children: addError }) : null] }));
570
582
  }
571
583
  }
572
584
  function AssignWorkorderButton() {
@@ -593,7 +605,7 @@ const LoadMatDialog = memo(function LoadMatDialog(props) {
593
605
  setShowAddMaterial(false);
594
606
  setSelectedQueue(null);
595
607
  }, [setSwapSt, setInvalidateSt]);
596
- return (_jsx(MaterialDialog, { onClose: onClose, allowNote: true, highlightProcsGreaterOrEqualTo: invalidateSt?.process ?? undefined, extraDialogElements: _jsxs(_Fragment, { children: [_jsx(SwapMaterialDialogContent, { st: swapSt, setState: setSwapSt }), invalidateSt !== null ? (_jsx(InvalidateCycleDialogContent, { st: invalidateSt, setState: setInvalidateSt })) : null, showAddMaterial ? (_jsx(PromptForQueue, { selectedQueue: selectedQueue, setSelectedQueue: setSelectedQueue, queueNames: props.queues })) : undefined] }), buttons: _jsxs(_Fragment, { children: [_jsx(InstructionButton, { pallet: props.pallet }), _jsx(PrintLabelButton, {}), _jsx(QuarantineMatButton, {}), _jsx(SignalInspectionButton, {}), _jsx(AddMatButton, { toQueue: selectedQueue, queues: props.queues, onClose: onClose, showAddToQueue: showAddMaterial, setShowAddToQueue: setShowAddMaterial }), _jsx(SwapMaterialButtons, { st: swapSt, setState: setSwapSt, onClose: onClose }), _jsx(InvalidateCycleDialogButton, { st: invalidateSt, setState: setInvalidateSt, onClose: onClose, loadStation: true }), _jsx(AssignWorkorderButton, {})] }) }));
608
+ return (_jsx(MaterialDialog, { onClose: onClose, allowNote: true, highlightProcsGreaterOrEqualTo: invalidateSt?.process ?? undefined, extraDialogElements: _jsxs(_Fragment, { children: [_jsx(SwapMaterialDialogContent, { st: swapSt, setState: setSwapSt }), invalidateSt !== null ? (_jsx(InvalidateCycleDialogContent, { st: invalidateSt, setState: setInvalidateSt })) : null, showAddMaterial ? (_jsx(PromptForQueue, { selectedQueue: selectedQueue, setSelectedQueue: setSelectedQueue, queueNames: props.queues })) : undefined] }), buttons: _jsxs(_Fragment, { children: [_jsx(InstructionButton, { pallet: props.pallet }), _jsx(PrintLabelButton, {}), _jsx(QuarantineMatButton, {}), _jsx(CancelLoadButton, { onClose: onClose }), _jsx(SignalInspectionButton, {}), _jsx(AddMatButton, { toQueue: selectedQueue, queues: props.queues, onClose: onClose, showAddToQueue: showAddMaterial, setShowAddToQueue: setShowAddMaterial }), _jsx(SwapMaterialButtons, { st: swapSt, setState: setSwapSt, onClose: onClose }), _jsx(InvalidateCycleDialogButton, { st: invalidateSt, setState: setInvalidateSt, onClose: onClose, loadStation: true }), _jsx(AssignWorkorderButton, {})] }) }));
597
609
  });
598
610
  function useGridLayout({ numMatCols, maxNumFaces, horizontal, showMatInCompleted, }) {
599
611
  let rows = "";