@seedtactics/insight-client 16.8.4-beta.9 → 17.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +21 -0
  2. package/dist/assets/index-gLnatT5a.js +388 -0
  3. package/dist/cell-status/material-details.d.ts +19 -5
  4. package/dist/cell-status/material-details.js +71 -37
  5. package/dist/cell-status/rebookings.d.ts +1 -0
  6. package/dist/cell-status/rebookings.js +4 -1
  7. package/dist/components/LogEntry.js +2 -9
  8. package/dist/components/log-entry-container-name.d.ts +2 -0
  9. package/dist/components/log-entry-container-name.js +10 -0
  10. package/dist/components/operations/AllMaterial.js +4 -3
  11. package/dist/components/station-monitor/BasketLoadStationWork.js +18 -10
  12. package/dist/components/station-monitor/BulkRawMaterial.js +4 -4
  13. package/dist/components/station-monitor/CancelLoadButton.d.ts +4 -0
  14. package/dist/components/station-monitor/CancelLoadButton.js +60 -0
  15. package/dist/components/station-monitor/Closeout.js +2 -1
  16. package/dist/components/station-monitor/Inspection.js +2 -1
  17. package/dist/components/station-monitor/InvalidateCycle.d.ts +4 -1
  18. package/dist/components/station-monitor/InvalidateCycle.js +104 -38
  19. package/dist/components/station-monitor/LoadStation.js +41 -16
  20. package/dist/components/station-monitor/Material.js +16 -1
  21. package/dist/components/station-monitor/QuarantineButton.js +160 -90
  22. package/dist/components/station-monitor/Queues.js +2 -1
  23. package/dist/components/station-monitor/QueuesAddMaterial.js +65 -57
  24. package/dist/components/station-monitor/SystemOverview.js +2 -1
  25. package/dist/components/station-monitor/Whiteboard.js +2 -2
  26. package/dist/data/material-operation-policy.d.ts +21 -0
  27. package/dist/data/material-operation-policy.js +89 -0
  28. package/dist/data/move-arrows.d.ts +6 -1
  29. package/dist/data/move-arrows.js +14 -2
  30. package/dist/extension.d.ts +4 -4
  31. package/dist/extension.js +15 -3
  32. package/dist/index.html +1 -2
  33. package/dist/index.js +1 -1
  34. package/dist/network/api.d.ts +20 -4
  35. package/dist/network/api.js +163 -2
  36. package/dist/network/backend-mock.js +6 -0
  37. package/dist/network/backend.d.ts +3 -1
  38. package/dist/network/backend.js +2 -1
  39. package/package.json +11 -11
  40. package/dist/assets/index-DHusyIyb.js +0 -388
  41. package/dist/assets/manifest-BzJli0lx.webmanifest +0 -15
  42. package/dist/bootstrap.d.ts +0 -5
  43. package/dist/bootstrap.js +0 -56
  44. package/dist/components/station-monitor/Material.test.d.ts +0 -1
  45. package/dist/components/station-monitor/Material.test.js +0 -199
@@ -34,17 +34,42 @@ 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 { 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
+ import { canInvalidateMaterial, isActiveLoadStationOperation, } from "../../data/material-operation-policy.js";
51
+ const invalidatableEventTypes = LazySeq.of([
52
+ LogType.AddToQueue,
53
+ LogType.RemoveFromQueue,
54
+ LogType.LoadUnloadCycle,
55
+ LogType.MachineCycle,
56
+ LogType.BasketLoadUnload,
57
+ LogType.BasketCycle,
58
+ ]).toRSet((eventType) => eventType);
59
+ export function affectedMaterialForInvalidation(events, materialId, process) {
60
+ let affected = HashMap.empty();
61
+ for (const event of events) {
62
+ if (!invalidatableEventTypes.has(event.type) ||
63
+ isLogEntryInvalidated(event) ||
64
+ !event.material.some((material) => material.id === materialId && (process === null || material.proc >= process))) {
65
+ continue;
66
+ }
67
+ for (const material of event.material) {
68
+ affected = affected.set(material.id, material.serial ?? null);
69
+ }
70
+ }
71
+ return affected;
72
+ }
48
73
  function InvalidateSelect(props) {
49
74
  const lastMat = useAtomValue(materialInDialogLargestUsedProcess);
50
75
  const possible = useAtomValue(barcodePotentialNewMaterial);
@@ -60,50 +85,64 @@ function InvalidateSelect(props) {
60
85
  }));
61
86
  const hasProc = lastMat !== null && lastMat.process >= 1;
62
87
  const hasChangeMat = castings.length > 0 || jobs.length > 0;
88
+ const selectedCasting = props.st?.changeRawMat !== null &&
89
+ props.st?.changeRawMat !== undefined &&
90
+ castings.includes(props.st.changeRawMat);
91
+ const selectedJob = props.st?.changeJobUnique !== null &&
92
+ props.st?.changeJobUnique !== undefined &&
93
+ jobs.some((job) => job.jobUnique === props.st?.changeJobUnique);
63
94
  function change(e) {
64
95
  props.setState({
65
96
  updating: false,
66
- process: e.startsWith("proc") ? parseInt(e.substring(4)) : null,
97
+ error: null,
98
+ process: e.startsWith("proc") ? parseInt(e.substring(4)) : 1,
67
99
  changeRawMat: e.startsWith("rawMat") ? e.substring(6) : null,
68
100
  changeJobUnique: e.startsWith("job") ? e.substring(3) : null,
69
101
  });
70
102
  }
71
- return (_jsx(TextField, { value: props.st?.process
72
- ? "proc" + props.st.process.toString()
73
- : props.st?.changeRawMat
74
- ? "rawMat" + props.st.changeRawMat
75
- : props.st?.changeJobUnique
76
- ? "job" + props.st.changeJobUnique
77
- : "", select: true, onChange: (e) => change(e.target.value), variant: "outlined", label: props.st?.process || !hasChangeMat
78
- ? "Invalidate Process"
79
- : props.st?.changeRawMat
80
- ? "Change Raw Material"
81
- : props.st?.changeJobUnique
82
- ? "Change Assigned Job"
103
+ return (_jsx(TextField, { value: selectedCasting
104
+ ? "rawMat" + props.st.changeRawMat
105
+ : selectedJob
106
+ ? "job" + props.st.changeJobUnique
107
+ : props.st?.process && hasProc
108
+ ? "proc" + props.st.process.toString()
109
+ : "", select: true, onChange: (e) => change(e.target.value), variant: "outlined", label: props.st?.changeRawMat
110
+ ? "Change Raw Material"
111
+ : props.st?.changeJobUnique
112
+ ? "Change Assigned Job"
113
+ : props.st?.process || !hasChangeMat
114
+ ? "Invalidate Process"
83
115
  : !hasProc
84
116
  ? "Change Assignment"
85
117
  : "", children: [
86
118
  hasProc && hasChangeMat
87
119
  ? [_jsx(ListSubheader, { children: "Invalidate Selected Process" }, "procheader")]
88
120
  : [],
89
- lastMat
90
- ? LazySeq.ofRange(1, lastMat.process + 1)
91
- .map((p) => (_jsxs(MenuItem, { value: "proc" + p.toString(), children: ["Invalidate Process ", p] }, p)))
92
- .toRArray()
121
+ hasProc
122
+ ? [
123
+ _jsxs(MenuItem, { value: "proc" + lastMat.process.toString(), children: ["Invalidate Process ", lastMat.process] }, lastMat.process),
124
+ ...(lastMat.process > 1
125
+ ? [
126
+ _jsx(MenuItem, { value: "proc1", children: "Invalidate All Processes" }, "all"),
127
+ ]
128
+ : []),
129
+ ]
93
130
  : [],
94
131
  hasProc && hasChangeMat
95
132
  ? [_jsx(ListSubheader, { children: "Change Material Type" }, "matheader")]
96
133
  : [],
97
134
  hasChangeMat
98
135
  ? [
99
- ...castings.map((c) => (_jsxs(MenuItem, { value: "rawMat" + c, children: ["Invalidate Everything And Change to ", c] }, "rawMat" + c))),
100
- ...jobs.map(({ jobUnique, job }) => (_jsxs(MenuItem, { value: "job" + jobUnique, children: [job ? (_jsx(ListItemIcon, { children: _jsx(PartIdenticon, { part: job.partName }) })) : undefined, _jsx(ListItemText, { primary: "Invalidate Everything And Change To Job " + jobUnique, secondary: job?.partName })] }, "job" + jobUnique))),
136
+ ...castings.map((c) => (_jsxs(MenuItem, { value: "rawMat" + c, children: ["Invalidate Everything and Change to ", c] }, "rawMat" + c))),
137
+ ...jobs.map(({ jobUnique, job }) => (_jsxs(MenuItem, { value: "job" + jobUnique, children: [job ? (_jsx(ListItemIcon, { children: _jsx(PartIdenticon, { part: job.partName }) })) : undefined, _jsx(ListItemText, { primary: "Invalidate Everything and Change to Job " + jobUnique, secondary: job?.partName })] }, "job" + jobUnique))),
101
138
  ]
102
139
  : [],
103
140
  ] }));
104
141
  }
105
142
  export function InvalidateCycleDialogContent(props) {
106
143
  const curMat = useAtomValue(materialInDialogInfo);
144
+ const events = useAtomValue(materialInDialogLocalEvents);
145
+ const status = useAtomValue(currentStatus);
107
146
  const show = props.st !== null && curMat !== null;
108
147
  const boxRef = useRef(null);
109
148
  useEffect(() => {
@@ -114,7 +153,20 @@ export function InvalidateCycleDialogContent(props) {
114
153
  }, [boxRef, show]);
115
154
  if (!show)
116
155
  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 })] }) }));
156
+ const hasSelection = props.st.process !== null ||
157
+ props.st.changeRawMat !== null ||
158
+ props.st.changeJobUnique !== null;
159
+ const previewProcess = props.st.changeRawMat !== null || props.st.changeJobUnique !== null ? null : props.st.process;
160
+ const affected = hasSelection
161
+ ? affectedMaterialForInvalidation(events, curMat.materialID, previewProcess)
162
+ : HashMap.empty();
163
+ const affectedLabels = [...affected].map(([materialId, serial]) => ({
164
+ materialId,
165
+ serial,
166
+ queued: status.material.find((material) => material.materialID === materialId)?.location.type ===
167
+ LocType.InQueue,
168
+ }));
169
+ 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
170
  }
119
171
  export function InvalidateCycleDialogButton(props) {
120
172
  const fmsInfo = useAtomValue(fmsInformation);
@@ -130,7 +182,7 @@ export function InvalidateCycleDialogButton(props) {
130
182
  return null;
131
183
  if (curMat === null)
132
184
  return null;
133
- if (inProcMat && inProcMat.location.type === LocType.OnPallet)
185
+ if (!canInvalidateMaterial(inProcMat))
134
186
  return null;
135
187
  const allowChange = possibleNew !== null &&
136
188
  (!LazySeq.ofObject(possibleNew.possibleCastingsByQueue ?? {}).isEmpty() ||
@@ -140,7 +192,13 @@ export function InvalidateCycleDialogButton(props) {
140
192
  if (props.ignoreOperator)
141
193
  operator = null;
142
194
  function showInvalidate() {
143
- props.setState({ process: null, updating: false, changeRawMat: null, changeJobUnique: null });
195
+ props.setState({
196
+ process: null,
197
+ updating: false,
198
+ error: null,
199
+ changeRawMat: null,
200
+ changeJobUnique: null,
201
+ });
144
202
  }
145
203
  function invalidateCycle() {
146
204
  if (curMat && (props.st?.process || props.st?.changeRawMat || props.st?.changeJobUnique)) {
@@ -152,7 +210,7 @@ export function InvalidateCycleDialogButton(props) {
152
210
  type: "MatDetails",
153
211
  details: mat,
154
212
  });
155
- props.onClose();
213
+ props.setState(null);
156
214
  }
157
215
  else {
158
216
  setMatToShow(null);
@@ -160,21 +218,31 @@ export function InvalidateCycleDialogButton(props) {
160
218
  }
161
219
  })
162
220
  .catch((e) => {
163
- console.log(e);
164
- setMatToShow(null);
165
- props.onClose();
221
+ props.setState({
222
+ ...props.st,
223
+ updating: false,
224
+ error: ApiException.isApiException(e)
225
+ ? e.response
226
+ : e instanceof Error
227
+ ? e.message
228
+ : String(e),
229
+ });
166
230
  });
167
231
  }
168
232
  }
169
233
  return (_jsxs(_Fragment, { children: [props.st === null ? (_jsx(Button, { color: "primary", onClick: showInvalidate, children: "Invalidate Cycle" })) : undefined, props.st !== null ? (_jsx(Button, { color: "primary", onClick: invalidateCycle, disabled: props.st.updating ||
170
234
  (props.st.process === null &&
171
235
  props.st.changeRawMat === null &&
172
- props.st.changeJobUnique === null), children: props.st.process !== null
173
- ? "Invalidate Process " + props.st.process.toString()
174
- : props.st.changeJobUnique !== null
175
- ? "Invalidate And Change To Job " + props.st.changeJobUnique
176
- : props.st.changeRawMat !== null
177
- ? "Invalidate And Change To " + props.st.changeRawMat
236
+ props.st.changeJobUnique === null), children: props.st.changeJobUnique !== null
237
+ ? "Invalidate Everything and Change to Job " + props.st.changeJobUnique
238
+ : props.st.changeRawMat !== null
239
+ ? "Invalidate Everything and Change to " + props.st.changeRawMat
240
+ : props.st.process !== null
241
+ ? props.st.process === 1
242
+ ? lastMat && lastMat.process > 1
243
+ ? "Invalidate All Processes"
244
+ : "Invalidate Process 1"
245
+ : "Invalidate Process " + props.st.process.toString()
178
246
  : "Invalidate Cycle" })) : undefined] }));
179
247
  }
180
248
  function isNullOrEmpty(s) {
@@ -241,9 +309,7 @@ export function SwapMaterialButtons(props) {
241
309
  operator = null;
242
310
  if (!curMat ||
243
311
  curMat.location.type !== LocType.OnPallet ||
244
- curMat.action.type === ActionType.Loading ||
245
- curMat.action.type === ActionType.UnloadToCompletedMaterial ||
246
- curMat.action.type === ActionType.UnloadToInProcess) {
312
+ isActiveLoadStationOperation(curMat)) {
247
313
  return null;
248
314
  }
249
315
  function swapMats() {
@@ -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";
@@ -61,6 +62,7 @@ import { useAtomValue, useSetAtom } from "jotai";
61
62
  import { PrintLabelButton } from "./PrintedLabel.js";
62
63
  import { hideNonLoadingMaterialOnLoadStation } from "../../data/queue-material.js";
63
64
  import { basketDisplayName, loadStationDisplayName } from "../../cell-status/station-cycles.js";
65
+ import { canAddOrMoveMaterialToQueue } from "../../data/material-operation-policy.js";
64
66
  import { BasketLoadStationWorkflow, } from "./BasketLoadStationWork.js";
65
67
  function encodeLoadStationRegionIdPart(value) {
66
68
  return encodeURIComponent(value.toString());
@@ -282,6 +284,21 @@ function selectLoadStationAndQueueProps(loadNum, queues, curSt, hideNonLoading)
282
284
  queueMat.forEach((queue) => queue.mats.sort(mkCompareByProperties((mat) => mat.location.queuePosition ?? 0)));
283
285
  basketMat.forEach((basket) => basket.mats.sort(mkCompareByProperties((mat) => mat.location.basketSlot ?? 0)));
284
286
  const matCount = freeLoading.length + palFaces.valuesToAscLazySeq().sumBy((x) => x.length);
287
+ const activeBasketSlotCount = activeBasket
288
+ ? LazySeq.of(activeBasket.emptySlots ?? [])
289
+ .concat(activeBasket.unknownSlots ?? [])
290
+ .concat(LazySeq.of(curSt.material).collect((mat) => mat.location.type === api.LocType.InBasket &&
291
+ mat.location.basketId === activeBasket.basketId
292
+ ? mat.location.basketSlot
293
+ : undefined))
294
+ .concat(LazySeq.of(curSt.material).collect((mat) => mat.action.type === api.ActionType.LoadingToBasket &&
295
+ mat.action.loadToBasketId === activeBasket.basketId
296
+ ? mat.action.loadToBasketSlot
297
+ : undefined))
298
+ .distinctBy((slot) => slot)
299
+ .toRArray().length
300
+ : 0;
301
+ const layoutCount = Math.max(matCount, activeBasketSlotCount);
285
302
  return {
286
303
  pallet: pal,
287
304
  activeBasket,
@@ -291,7 +308,7 @@ function selectLoadStationAndQueueProps(loadNum, queues, curSt, hideNonLoading)
291
308
  queues: queueMat,
292
309
  baskets: basketMat,
293
310
  elapsedLoadingTime,
294
- fsize: matCount <= 2 ? "x-large" : matCount <= 6 ? "large" : "normal",
311
+ fsize: layoutCount <= 2 ? "x-large" : layoutCount <= 6 ? "large" : "normal",
295
312
  };
296
313
  }
297
314
  function MultiInstructionButton({ loadData }) {
@@ -540,10 +557,8 @@ function AddMatButton({ queues, onClose, toQueue, showAddToQueue, setShowAddToQu
540
557
  const inProcMat = useAtomValue(matDetails.inProcessMaterialInDialog);
541
558
  const operator = useAtomValue(currentOperator);
542
559
  const [addExistingMat, addingExistingMat] = matDetails.useAddExistingMaterialToQueue();
543
- if (!existingMat ||
544
- inProcMat?.location.type === api.LocType.OnPallet ||
545
- inProcMat?.action.type === api.ActionType.Loading ||
546
- inProcMat?.action.type === api.ActionType.LoadingToBasket) {
560
+ const [addError, setAddError] = useState(null);
561
+ if (!existingMat || !canAddOrMoveMaterialToQueue(inProcMat)) {
547
562
  return null;
548
563
  }
549
564
  if (queues.length === 0)
@@ -557,16 +572,26 @@ function AddMatButton({ queues, onClose, toQueue, showAddToQueue, setShowAddToQu
557
572
  return (_jsx(Button, { color: "primary", onClick: () => setShowAddToQueue(true), children: "Add To Queue" }));
558
573
  }
559
574
  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}` : ""] }));
575
+ return (_jsxs(_Fragment, { children: [_jsxs(Button, { color: "primary", disabled: toQueue === null || addingExistingMat, onClick: async () => {
576
+ setAddError(null);
577
+ try {
578
+ await addExistingMat({
579
+ materialId: existingMat.materialID,
580
+ queue: toQueue ?? "",
581
+ queuePosition: -1,
582
+ operator: operator,
583
+ });
584
+ setMatToShow(null);
585
+ onClose();
586
+ }
587
+ catch (e) {
588
+ setAddError(api.ApiException.isApiException(e)
589
+ ? e.response
590
+ : e instanceof Error
591
+ ? e.message
592
+ : String(e));
593
+ }
594
+ }, children: ["Add To ", toQueue ?? "Queue", " ", lastProcMat ? ` To Run Process ${lastProcMat.process + 1}` : ""] }), addError ? _jsx(Typography, { role: "alert", children: addError }) : null] }));
570
595
  }
571
596
  }
572
597
  function AssignWorkorderButton() {
@@ -593,7 +618,7 @@ const LoadMatDialog = memo(function LoadMatDialog(props) {
593
618
  setShowAddMaterial(false);
594
619
  setSelectedQueue(null);
595
620
  }, [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, {})] }) }));
621
+ 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
622
  });
598
623
  function useGridLayout({ numMatCols, maxNumFaces, horizontal, showMatInCompleted, }) {
599
624
  let rows = "";
@@ -49,6 +49,7 @@ import { useAtom, useAtomValue, useSetAtom } from "jotai";
49
49
  import { last30Rebookings } from "../../cell-status/rebookings.js";
50
50
  import { fmsInformation } from "../../network/server-settings.js";
51
51
  import { basketDisplayName } from "../../cell-status/station-cycles.js";
52
+ import { materialOperationState } from "../../data/material-operation-policy.js";
52
53
  export class PartIdenticon extends PureComponent {
53
54
  render() {
54
55
  const iconSize = this.props.size || 50;
@@ -376,6 +377,7 @@ const MatCard = forwardRef(function MatCard(props, ref) {
376
377
  }, children: [_jsx(PartIdenticon, { part: props.mat.partName }), _jsxs(Box, { sx: {
377
378
  marginLeft: "8px",
378
379
  flexGrow: 1,
380
+ minWidth: 0,
379
381
  }, children: [_jsx(MatCardHeader, { fsize: props.fsize, children: props.mat.partName }), props.displayJob ? (_jsx(MatCardDetail, { fsize: props.fsize, children: props.mat.jobUnique && props.mat.jobUnique !== ""
380
382
  ? "Assigned to " + props.mat.jobUnique
381
383
  : "Unassigned material" })) : undefined, !props.hideEmptySerial || props.mat.serial ? (_jsxs(MatCardDetail, { fsize: props.fsize, children: ["Serial: ", props.mat.serial ? props.mat.serial : "none"] })) : undefined, props.mat.workorderId === undefined ||
@@ -390,6 +392,7 @@ const MatCard = forwardRef(function MatCard(props, ref) {
390
392
  justifyContent: "space-between",
391
393
  alignItems: "flex-end",
392
394
  alignSelf: "start",
395
+ flexShrink: 0,
393
396
  }, children: [props.mat.serial && props.mat.serial.length >= 1 && !props.hideAvatar ? (_jsx("div", { children: _jsx(Avatar, { style: { width: "30px", height: "30px" }, children: props.mat.serial.slice(-1) }) })) : undefined, props.hideWarningIcon ? undefined : (_jsx("div", { children: _jsx(Warning, { mat: props.mat }) }))] })] }) })] }));
394
397
  });
395
398
  export const MatSummary = memo(MatCard);
@@ -537,6 +540,18 @@ function RebookingNote() {
537
540
  }
538
541
  return null;
539
542
  }
543
+ function MaterialOperationNotice() {
544
+ const material = useAtomValue(matDetails.inProcessMaterialInDialog);
545
+ if (material === null)
546
+ return null;
547
+ if (material.quarantineAfterUnload) {
548
+ return (_jsx(Typography, { variant: "caption", component: "p", children: "Quarantine requested" }));
549
+ }
550
+ const state = materialOperationState(material);
551
+ if (state.kind !== "ActiveLoadStationOperation" || state.cancellationId !== null)
552
+ return null;
553
+ return (_jsx(Typography, { variant: "caption", component: "p", children: "This material is part of the current load/unload operation. Complete the operation or cancel it at the machine control before making other changes." }));
554
+ }
540
555
  export const MaterialDetailContent = memo(function MaterialDetailContent({ highlightProcsGreaterOrEqualTo, }) {
541
556
  const toShow = useAtomValue(matDetails.materialDialogOpen);
542
557
  const mat = useAtomValue(matDetails.materialInDialogInfo);
@@ -559,7 +574,7 @@ export const MaterialDetailContent = memo(function MaterialDetailContent({ highl
559
574
  return _jsx("div", { style: { marginLeft: "1em" }, children: "Material not found." });
560
575
  }
561
576
  }
562
- return (_jsxs(_Fragment, { children: [_jsxs("div", { style: { marginLeft: "1em" }, children: [_jsx("div", { children: _jsxs("small", { children: ["Workorder: ", mat?.workorderId ?? "none"] }) }), _jsx("div", { children: _jsx(DisplayLoadingAndError, { fallback: _jsxs("small", { children: ["Inspections: ", _jsx(CircularProgress, { size: "10" })] }), children: _jsx(MaterialInspections, {}) }) }), _jsx("div", { children: _jsx(DisplayLoadingAndError, { fallback: _jsx("div", {}), children: _jsx(RebookingNote, {}) }) })] }), _jsx(DisplayLoadingAndError, { fallback: _jsx(CircularProgress, {}), children: _jsx(MaterialEvents, { highlightProcsGreaterOrEqualTo: highlightProcsGreaterOrEqualTo }) })] }));
577
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { style: { marginLeft: "1em" }, children: [_jsx(MaterialOperationNotice, {}), _jsx("div", { children: _jsxs("small", { children: ["Workorder: ", mat?.workorderId ?? "none"] }) }), _jsx("div", { children: _jsx(DisplayLoadingAndError, { fallback: _jsxs("small", { children: ["Inspections: ", _jsx(CircularProgress, { size: "10" })] }), children: _jsx(MaterialInspections, {}) }) }), _jsx("div", { children: _jsx(DisplayLoadingAndError, { fallback: _jsx("div", {}), children: _jsx(RebookingNote, {}) }) })] }), _jsx(DisplayLoadingAndError, { fallback: _jsx(CircularProgress, {}), children: _jsx(MaterialEvents, { highlightProcsGreaterOrEqualTo: highlightProcsGreaterOrEqualTo }) })] }));
563
578
  });
564
579
  function NotesDialogBody(props) {
565
580
  const [curNote, setCurNote] = useState("");