@seedtactics/insight-client 16.8.4-beta.8 → 17.0.0-beta.0

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 (41) hide show
  1. package/README.md +21 -0
  2. package/dist/assets/index-BkKYqUM0.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/components/LogEntry.js +2 -9
  6. package/dist/components/log-entry-container-name.d.ts +2 -0
  7. package/dist/components/log-entry-container-name.js +10 -0
  8. package/dist/components/operations/AllMaterial.js +4 -3
  9. package/dist/components/station-monitor/BasketLoadStationWork.js +12 -8
  10. package/dist/components/station-monitor/BulkRawMaterial.js +4 -4
  11. package/dist/components/station-monitor/CancelLoadButton.d.ts +4 -0
  12. package/dist/components/station-monitor/CancelLoadButton.js +60 -0
  13. package/dist/components/station-monitor/Closeout.js +2 -1
  14. package/dist/components/station-monitor/Inspection.js +2 -1
  15. package/dist/components/station-monitor/InvalidateCycle.d.ts +4 -1
  16. package/dist/components/station-monitor/InvalidateCycle.js +104 -38
  17. package/dist/components/station-monitor/LoadStation.js +25 -15
  18. package/dist/components/station-monitor/Material.js +14 -1
  19. package/dist/components/station-monitor/QuarantineButton.js +160 -90
  20. package/dist/components/station-monitor/Queues.js +2 -1
  21. package/dist/components/station-monitor/QueuesAddMaterial.js +65 -57
  22. package/dist/components/station-monitor/SystemOverview.js +2 -1
  23. package/dist/components/station-monitor/Whiteboard.js +2 -2
  24. package/dist/data/material-operation-policy.d.ts +21 -0
  25. package/dist/data/material-operation-policy.js +89 -0
  26. package/dist/extension.d.ts +11 -0
  27. package/dist/extension.js +21 -0
  28. package/dist/index.html +1 -2
  29. package/dist/index.js +1 -1
  30. package/dist/network/api.d.ts +20 -4
  31. package/dist/network/api.js +163 -2
  32. package/dist/network/backend-mock.js +6 -0
  33. package/dist/network/backend.d.ts +2 -0
  34. package/dist/network/backend.js +2 -1
  35. package/package.json +12 -11
  36. package/dist/assets/index-DHusyIyb.js +0 -388
  37. package/dist/assets/manifest-BzJli0lx.webmanifest +0 -15
  38. package/dist/bootstrap.d.ts +0 -5
  39. package/dist/bootstrap.js +0 -56
  40. package/dist/components/station-monitor/Material.test.d.ts +0 -1
  41. package/dist/components/station-monitor/Material.test.js +0 -199
@@ -35,22 +35,31 @@ import { useState } from "react";
35
35
  import { Button, Dialog, DialogActions, DialogContent, DialogTitle, TextField, Tooltip, } from "@mui/material";
36
36
  import { LazySeq } from "@seedtactics/immutable-collections";
37
37
  import { currentStatus } from "../../cell-status/current-status.js";
38
- import { inProcessMaterialInDialog, materialDialogOpen, useRemoveFromQueue, useSignalForQuarantine, } from "../../cell-status/material-details.js";
38
+ import { inProcessMaterialInDialog, materialDialogOpen, useQuarantineQueuedMaterial, useRemoveFromQueue, useSignalForQuarantine, } from "../../cell-status/material-details.js";
39
39
  import { currentOperator } from "../../data/operators.js";
40
- import { ActionType, LocType, PalletLocationEnum, QueueRole } from "../../network/api.js";
40
+ import { ApiException, LocType, QueueRole, } from "../../network/api.js";
41
41
  import { fmsInformation } from "../../network/server-settings.js";
42
42
  import { useAtomValue, useSetAtom } from "jotai";
43
- function useQuarantineMaterial(ignoreOperator) {
43
+ import { canDirectlyQuarantine, canRemoveFromQueue, canSignalQuarantine, isActiveLoadStationOperation, } from "../../data/material-operation-policy.js";
44
+ function hasSupportedQuarantineExit(material, status) {
45
+ const job = status.jobs[material.jobUnique];
46
+ if (!job)
47
+ return false;
48
+ const path = job.procsAndPaths[material.process - 1]?.paths[material.path - 1];
49
+ if (!path)
50
+ return false;
51
+ return material.process === job.procsAndPaths.length || Boolean(path.outputQueue);
52
+ }
53
+ function useQuarantineMaterial() {
44
54
  const fmsInfo = useAtomValue(fmsInformation);
45
- const [removeFromQueue, removingFromQueue] = useRemoveFromQueue();
46
- const [signalQuarantine, signalingQuarantine] = useSignalForQuarantine();
47
55
  const inProcMat = useAtomValue(inProcessMaterialInDialog);
48
56
  const curSt = useAtomValue(currentStatus);
49
- let operator = useAtomValue(currentOperator);
50
- if (ignoreOperator)
51
- operator = null;
52
57
  if (inProcMat === null || inProcMat.materialID < 0)
53
58
  return null;
59
+ if (isActiveLoadStationOperation(inProcMat))
60
+ return null;
61
+ if (inProcMat.quarantineAfterUnload)
62
+ return null;
54
63
  const quarantineQueue = fmsInfo.quarantineQueue?.length ? fmsInfo.quarantineQueue : null;
55
64
  const activeQueues = LazySeq.ofObject(curSt.jobs)
56
65
  .flatMap(([_, job]) => job.procsAndPaths)
@@ -70,131 +79,192 @@ function useQuarantineMaterial(ignoreOperator) {
70
79
  const quarantineQueues = LazySeq.ofObject(curSt.queues)
71
80
  .filter(([qname, _]) => !activeQueues.has(qname))
72
81
  .toRSet(([qname, _]) => qname);
73
- const palLoc = LazySeq.ofObject(curSt.pallets).toOrderedMap(([_, pal]) => [
74
- pal.palletNum,
75
- pal.currentPalletLocation,
76
- ]);
77
82
  // If in a quarantine queue, allow removal from system
78
83
  if (inProcMat.location.type === LocType.InQueue &&
79
84
  inProcMat.location.currentQueue &&
80
85
  quarantineQueues.has(inProcMat.location.currentQueue)) {
81
86
  return {
82
87
  type: "Remove",
83
- quarantine: () => removeFromQueue(inProcMat.materialID, operator),
84
- removing: removingFromQueue,
88
+ material: inProcMat,
85
89
  quarantineQueueDestination: null,
90
+ canRemoveFromQueues: false,
86
91
  };
87
92
  }
88
93
  let type = null;
89
94
  switch (inProcMat.location.type) {
90
95
  case LocType.OnPallet:
91
- if (fmsInfo.allowQuarantineToCancelLoad) {
92
- // either cancel load or signal based on material action and pallet location
93
- const palAtLoad = inProcMat.location.palletNum &&
94
- palLoc.get(inProcMat.location.palletNum)?.loc === PalletLocationEnum.LoadUnload;
95
- if (inProcMat.action.type === ActionType.Loading || palAtLoad) {
96
- type = "CancelLoad";
97
- }
98
- else {
99
- type = "SignalForScrap";
100
- }
101
- }
102
- else {
103
- // must signal since it is currently on a pallet, but only if the material is not loading
104
- if (inProcMat.action.type === ActionType.Loading) {
105
- return null;
106
- }
107
- // Check that the job outputs to a queue, only then can signaling for quarantine work
108
- const job = curSt.jobs[inProcMat.jobUnique];
109
- if (!job)
110
- return null;
111
- const path = job.procsAndPaths?.[inProcMat.process - 1]?.paths?.[inProcMat.path - 1];
112
- if (inProcMat.process != job.procsAndPaths.length && (!path || !path.outputQueue)) {
113
- return null;
114
- }
115
- type = "SignalForScrap";
96
+ case LocType.InBasket:
97
+ if (!canSignalQuarantine(inProcMat) || !hasSupportedQuarantineExit(inProcMat, curSt)) {
98
+ return null;
116
99
  }
100
+ type = "SignalForScrap";
117
101
  break;
118
102
  case LocType.InQueue:
119
- if ((inProcMat.action.type === ActionType.Loading ||
120
- inProcMat.action.type === ActionType.LoadingToBasket) &&
121
- !fmsInfo.allowQuarantineToCancelLoad) {
103
+ if (!canDirectlyQuarantine(inProcMat))
122
104
  return null;
123
- }
124
- else if (inProcMat.action.type === ActionType.Loading ||
125
- inProcMat.action.type === ActionType.LoadingToBasket) {
126
- type = "CancelLoad";
127
- }
128
- else {
129
- type = "Scrap";
130
- }
131
- break;
132
- case LocType.InBasket:
133
- if (inProcMat.action.type === ActionType.Loading && !fmsInfo.allowQuarantineToCancelLoad) {
105
+ if (inProcMat.location.currentQueue === undefined ||
106
+ quarantineQueues.has(inProcMat.location.currentQueue)) {
134
107
  return null;
135
108
  }
136
- else if (inProcMat.action.type === ActionType.Loading) {
137
- type = "CancelLoad";
138
- }
139
- else {
140
- type = "Scrap";
141
- }
142
- break;
143
- case LocType.Free:
144
109
  type = "Scrap";
145
110
  break;
111
+ case LocType.Free:
112
+ return null;
146
113
  }
147
114
  if (type) {
148
115
  return {
149
116
  type,
150
- quarantine: (reason) => signalQuarantine(inProcMat.materialID, operator, reason),
151
- removing: signalingQuarantine,
117
+ material: inProcMat,
152
118
  quarantineQueueDestination: quarantineQueue,
119
+ canRemoveFromQueues: inProcMat.location.type === LocType.InQueue &&
120
+ inProcMat.location.currentQueue !== undefined &&
121
+ !quarantineQueues.has(inProcMat.location.currentQueue) &&
122
+ canRemoveFromQueue(inProcMat),
153
123
  };
154
124
  }
155
125
  else {
156
126
  return null;
157
127
  }
158
128
  }
129
+ function RemoveFromQueuesButton({ material, removeFromQueues, removing, }) {
130
+ const [open, setOpen] = useState(false);
131
+ const [error, setError] = useState(null);
132
+ const setMatToShow = useSetAtom(materialDialogOpen);
133
+ async function remove() {
134
+ setError(null);
135
+ try {
136
+ await removeFromQueues();
137
+ setOpen(false);
138
+ setMatToShow({
139
+ type: "MatSummary",
140
+ summary: {
141
+ materialID: material.materialID,
142
+ partName: material.partName,
143
+ serial: material.serial,
144
+ },
145
+ });
146
+ }
147
+ catch (e) {
148
+ setError(ApiException.isApiException(e) ? e.response : e instanceof Error ? e.message : String(e));
149
+ }
150
+ }
151
+ function openRemoval() {
152
+ setError(null);
153
+ setOpen(true);
154
+ }
155
+ return (_jsxs(_Fragment, { children: [_jsx(Tooltip, { title: "Remove from the current queue so it can be rescanned", children: _jsx(Button, { color: "primary", disabled: removing, onClick: openRemoval, children: "Remove from Queue" }) }), _jsxs(Dialog, { open: open, onClose: () => setOpen(false), children: [_jsx(DialogTitle, { children: "Remove Material from Queue" }), _jsxs(DialogContent, { children: [_jsx("p", { children: "Remove this material from its current queue. Its production history will remain available. You can scan it again to correct its history or assignment and then add it back to a queue." }), error ? _jsx("p", { role: "alert", children: error }) : null] }), _jsxs(DialogActions, { children: [_jsx(Button, { color: "primary", disabled: removing, onClick: remove, children: "Remove from Queue" }), _jsx(Button, { color: "secondary", disabled: removing, onClick: () => setOpen(false), children: "Cancel" })] })] })] }));
156
+ }
159
157
  export function QuarantineMatButton({ onClose, ignoreOperator, }) {
160
158
  const [open, setOpen] = useState(false);
161
159
  const [reason, setReason] = useState("");
162
- const q = useQuarantineMaterial(!!ignoreOperator);
160
+ const [error, setError] = useState(null);
161
+ const [updating, setUpdating] = useState(false);
162
+ const [snapshot, setSnapshot] = useState(null);
163
+ const q = useQuarantineMaterial();
164
+ const [removeFromQueue, removingFromQueue] = useRemoveFromQueue();
165
+ const [signalQuarantine, signalingQuarantine] = useSignalForQuarantine();
166
+ const [quarantineQueued, quarantiningQueued] = useQuarantineQueuedMaterial();
167
+ const currentOperatorValue = useAtomValue(currentOperator);
168
+ const operator = ignoreOperator ? null : currentOperatorValue;
163
169
  const setMatToShow = useSetAtom(materialDialogOpen);
164
- if (q === null)
170
+ const currentSnapshot = q === null
171
+ ? null
172
+ : {
173
+ material: q.material,
174
+ materialId: q.material.materialID,
175
+ operation: q.type === "SignalForScrap"
176
+ ? "DeferredQuarantine"
177
+ : q.type === "Scrap"
178
+ ? "DirectQuarantine"
179
+ : "RemoveFromSystem",
180
+ destination: q.quarantineQueueDestination,
181
+ operator,
182
+ };
183
+ const displayedSnapshot = snapshot ?? currentSnapshot;
184
+ function openQuarantine() {
185
+ if (currentSnapshot === null)
186
+ return;
187
+ setError(null);
188
+ setReason("");
189
+ setSnapshot(currentSnapshot);
190
+ setOpen(true);
191
+ }
192
+ function closeQuarantine() {
193
+ if (updating)
194
+ return;
195
+ setOpen(false);
196
+ setSnapshot(null);
197
+ }
198
+ if (displayedSnapshot === null)
165
199
  return null;
166
200
  let title;
167
201
  let btnTxt;
168
- switch (q.type) {
169
- case "Remove":
202
+ switch (displayedSnapshot.operation) {
203
+ case "RemoveFromSystem":
170
204
  title = "Remove from system";
171
205
  btnTxt = "Remove";
172
206
  break;
173
- case "Scrap":
174
- title = q.quarantineQueueDestination
175
- ? `Move to ${q.quarantineQueueDestination}`
176
- : "Remove from queue";
177
- btnTxt = q.quarantineQueueDestination ? "Quarantine" : "Remove";
207
+ case "DirectQuarantine":
208
+ title = displayedSnapshot.destination
209
+ ? `Move to ${displayedSnapshot.destination}`
210
+ : "Remove from queue and treat as scrap";
211
+ btnTxt = displayedSnapshot.destination ? "Quarantine" : "Scrap";
178
212
  break;
179
- case "SignalForScrap":
180
- title = q.quarantineQueueDestination
181
- ? `After unload, move to ${q.quarantineQueueDestination}`
182
- : "After unload, remove the part as scrap";
183
- btnTxt = q.quarantineQueueDestination ? "Quarantine" : "Scrap";
184
- break;
185
- case "CancelLoad":
186
- title = q.quarantineQueueDestination
187
- ? `Cancel load and move to ${q.quarantineQueueDestination}`
188
- : "Cancel load";
189
- btnTxt = "Cancel Load";
213
+ case "DeferredQuarantine":
214
+ title = displayedSnapshot.destination
215
+ ? `The current automated operation will continue. When the material leaves automation control, move it to ${displayedSnapshot.destination}`
216
+ : "The current automated operation will continue. When the material leaves automation control, remove it from normal production flow as scrap";
217
+ btnTxt = displayedSnapshot.destination ? "Quarantine" : "Scrap";
190
218
  break;
191
219
  }
192
- function quarantine() {
193
- q?.quarantine(reason);
194
- setMatToShow(null);
195
- setOpen(false);
196
- setReason("");
197
- onClose?.();
220
+ async function quarantine() {
221
+ if (snapshot === null)
222
+ return;
223
+ setUpdating(true);
224
+ setError(null);
225
+ try {
226
+ switch (snapshot.operation) {
227
+ case "DeferredQuarantine":
228
+ await signalQuarantine(snapshot.materialId, snapshot.operator, reason);
229
+ break;
230
+ case "DirectQuarantine":
231
+ await quarantineQueued(snapshot.materialId, snapshot.operator, reason);
232
+ break;
233
+ case "RemoveFromSystem":
234
+ await removeFromQueue(snapshot.materialId, snapshot.operator);
235
+ break;
236
+ }
237
+ setOpen(false);
238
+ setSnapshot(null);
239
+ setReason("");
240
+ if (snapshot.operation === "RemoveFromSystem") {
241
+ setMatToShow({
242
+ type: "MatSummary",
243
+ summary: {
244
+ materialID: snapshot.materialId,
245
+ partName: snapshot.material.partName,
246
+ serial: snapshot.material.serial,
247
+ },
248
+ });
249
+ }
250
+ else {
251
+ setMatToShow(null);
252
+ onClose?.();
253
+ }
254
+ }
255
+ catch (e) {
256
+ setError(ApiException.isApiException(e) ? e.response : e instanceof Error ? e.message : String(e));
257
+ }
258
+ finally {
259
+ setUpdating(false);
260
+ }
198
261
  }
199
- return (_jsxs(_Fragment, { children: [_jsx(Tooltip, { title: title, children: _jsx(Button, { color: "primary", disabled: q.removing, onClick: () => setOpen(true), children: btnTxt }) }), _jsxs(Dialog, { open: open, onClose: () => setOpen(false), children: [_jsx(DialogTitle, { children: "Quarantine Material" }), _jsxs(DialogContent, { children: [_jsx("p", { children: title }), _jsx(TextField, { label: "Reason", value: reason, onChange: (e) => setReason(e.target.value), fullWidth: true, autoFocus: true, multiline: true })] }), _jsxs(DialogActions, { children: [_jsx(Button, { color: "primary", onClick: quarantine, children: btnTxt }), _jsx(Button, { color: "secondary", onClick: () => setOpen(false), children: "Cancel" })] })] })] }));
262
+ const removing = updating ||
263
+ (displayedSnapshot.operation === "DeferredQuarantine"
264
+ ? signalingQuarantine
265
+ : displayedSnapshot.operation === "DirectQuarantine"
266
+ ? quarantiningQueued
267
+ : removingFromQueue);
268
+ const removeFromQueues = q?.canRemoveFromQueues === true ? () => removeFromQueue(q.material.materialID, operator) : null;
269
+ return (_jsxs(_Fragment, { children: [q ? (_jsx(Tooltip, { title: title, children: _jsx(Button, { color: "primary", disabled: removing, onClick: openQuarantine, children: btnTxt }) })) : null, removeFromQueues && q ? (_jsx(RemoveFromQueuesButton, { material: q.material, removeFromQueues: removeFromQueues, removing: removingFromQueue })) : null, _jsxs(Dialog, { open: open && snapshot !== null, onClose: closeQuarantine, children: [_jsx(DialogTitle, { children: "Quarantine Material" }), _jsxs(DialogContent, { children: [_jsx("p", { children: title }), _jsx(TextField, { label: "Reason", value: reason, onChange: (e) => setReason(e.target.value), fullWidth: true, autoFocus: true, multiline: true }), error ? _jsx("p", { role: "alert", children: error }) : null] }), _jsxs(DialogActions, { children: [_jsx(Button, { color: "primary", disabled: removing, onClick: quarantine, children: btnTxt }), _jsx(Button, { color: "secondary", disabled: removing, onClick: closeQuarantine, children: "Cancel" })] })] })] }));
200
270
  }
@@ -69,6 +69,7 @@ import { materialDialogOpen } from "../../cell-status/material-details.js";
69
69
  import { PrintLabelButton } from "./PrintedLabel.js";
70
70
  import { AddToQueueButton, AddToQueueMaterialDialogCt, } from "./QueuesAddMaterial.js";
71
71
  import { QuarantineMatButton } from "./QuarantineButton.js";
72
+ import { CancelLoadButton } from "./CancelLoadButton.js";
72
73
  import { AddByBarcodeDialog, scanBarcodeToAddToQueueDialog } from "../BarcodeScanning.js";
73
74
  import { InvalidateCycleDialogButton, InvalidateCycleDialogContent, } from "./InvalidateCycle.js";
74
75
  const JobTableRow = styled(TableRow, { shouldForwardProp: (prop) => prop.toString()[0] !== "$" })(({ $noBorderBottom, $highlightedRow, $noncompletedRow }) => ({
@@ -287,7 +288,7 @@ const QueuedMaterialDialog = memo(function QueuedMaterialDialog({ queueNames, })
287
288
  setInvalidateSt(null);
288
289
  }, []);
289
290
  queueNames = usePossibleQueuesForAdd(queueNames);
290
- return (_jsx(MaterialDialog, { allowNote: true, onClose: onClose, highlightProcsGreaterOrEqualTo: invalidateSt?.process ?? undefined, extraDialogElements: _jsxs(_Fragment, { children: [_jsx(InvalidateCycleDialogContent, { st: invalidateSt, setState: setInvalidateSt }), _jsx(AddToQueueMaterialDialogCt, { queueNames: queueNames, st: addMatSt, setState: setAddMatSt })] }), buttons: _jsxs(_Fragment, { children: [_jsx(PrintLabelButton, {}), _jsx(QuarantineMatButton, { onClose: onClose }), _jsx(InvalidateCycleDialogButton, { onClose: onClose, st: invalidateSt, setState: setInvalidateSt }), _jsx(AddToQueueButton, { st: addMatSt, queueNames: queueNames, onClose: onClose })] }) }));
291
+ return (_jsx(MaterialDialog, { allowNote: true, onClose: onClose, highlightProcsGreaterOrEqualTo: invalidateSt?.process ?? undefined, extraDialogElements: _jsxs(_Fragment, { children: [_jsx(InvalidateCycleDialogContent, { st: invalidateSt, setState: setInvalidateSt }), _jsx(AddToQueueMaterialDialogCt, { queueNames: queueNames, st: addMatSt, setState: setAddMatSt })] }), buttons: _jsxs(_Fragment, { children: [_jsx(PrintLabelButton, {}), _jsx(QuarantineMatButton, { onClose: onClose }), _jsx(CancelLoadButton, { onClose: onClose }), _jsx(InvalidateCycleDialogButton, { onClose: onClose, st: invalidateSt, setState: setInvalidateSt }), _jsx(AddToQueueButton, { st: addMatSt, queueNames: queueNames, onClose: onClose })] }) }));
291
292
  });
292
293
  export const Queues = (props) => {
293
294
  const operator = useAtomValue(currentOperator);
@@ -42,21 +42,18 @@ import { currentOperator } from "../../data/operators.js";
42
42
  import { fmsInformation } from "../../network/server-settings.js";
43
43
  import { useAddNewCastingToQueue } from "../../cell-status/material-details.js";
44
44
  import { useAtomValue, useSetAtom } from "jotai";
45
+ import { canAddOrMoveMaterialToQueue } from "../../data/material-operation-policy.js";
45
46
  function useAllowAddToQueue(queueNames) {
46
47
  const existingMat = useAtomValue(matDetails.materialInDialogInfo);
47
48
  const inProcMat = useAtomValue(matDetails.inProcessMaterialInDialog);
48
49
  const possibleNewMats = useAtomValue(matDetails.barcodePotentialNewMaterial);
49
- const curInQueueOnScreen = inProcMat !== null &&
50
- inProcMat.location.type === api.LocType.InQueue &&
51
- inProcMat.location.currentQueue &&
52
- queueNames.includes(inProcMat.location.currentQueue);
53
- if (curInQueueOnScreen)
50
+ if (!canAddOrMoveMaterialToQueue(inProcMat))
54
51
  return false;
55
- if (inProcMat?.location.type === api.LocType.OnPallet)
56
- return false;
57
- if (inProcMat?.action.type === api.ActionType.Loading ||
58
- inProcMat?.action.type === api.ActionType.LoadingToBasket)
52
+ if (inProcMat?.location.type === api.LocType.InQueue &&
53
+ inProcMat.location.currentQueue !== undefined &&
54
+ queueNames.includes(inProcMat.location.currentQueue)) {
59
55
  return false;
56
+ }
60
57
  if (existingMat === null && !possibleNewMats) {
61
58
  return false;
62
59
  }
@@ -183,6 +180,7 @@ export function AddToQueueButton({ st: { enteredOperator, newMaterialTy, toQueue
183
180
  const [addExistingMat, addingExistingMat] = matDetails.useAddExistingMaterialToQueue();
184
181
  const [addNewMat, addingNewMat] = matDetails.useAddNewMaterialToQueue();
185
182
  const [addNewCasting, addingNewCasting] = useAddNewCastingToQueue();
183
+ const [addError, setAddError] = useState(null);
186
184
  const allowAdd = useAllowAddToQueue(queueNames);
187
185
  if (!allowAdd) {
188
186
  return null;
@@ -213,52 +211,62 @@ export function AddToQueueButton({ st: { enteredOperator, newMaterialTy, toQueue
213
211
  withSerialMsg = " With Serial " + newSerial;
214
212
  }
215
213
  const curQueue = inProcMat?.location.currentQueue ?? null;
216
- return (_jsx(_Fragment, { children: _jsx(Tooltip, { title: toQueue === null
217
- ? "Select a queue to add the material to"
218
- : fmsInfo.requireOperatorNamePromptWhenAddingMaterial &&
219
- (enteredOperator === null || enteredOperator === "")
220
- ? "Please enter an operator name"
221
- : "", children: _jsx("span", { children: _jsx(Button, { color: "primary", disabled: toQueue === null ||
222
- addingExistingMat ||
223
- addingNewMat ||
224
- addingNewCasting ||
225
- (existingMat === null && newMaterialTy === null) ||
226
- (fmsInfo.requireOperatorNamePromptWhenAddingMaterial &&
227
- (enteredOperator === null || enteredOperator === "")), onClick: () => {
228
- if (existingMat) {
229
- addExistingMat({
230
- materialId: existingMat.materialID,
231
- queue: toQueue ?? "",
232
- queuePosition: -1,
233
- operator: enteredOperator ?? operator,
234
- });
235
- }
236
- else if (newMaterialTy &&
237
- newMaterialTy.kind === "JobAndProc" &&
238
- newMaterialTy.jobUnique) {
239
- addNewMat({
240
- jobUnique: newMaterialTy.jobUnique,
241
- lastCompletedProcess: newMaterialTy.last_proc,
242
- serial: newSerial ?? undefined,
243
- workorder: newWorkorder ?? newMaterialTy?.workorder,
244
- queue: toQueue ?? "",
245
- queuePosition: -1,
246
- operator: enteredOperator ?? operator,
247
- });
248
- }
249
- else if (newMaterialTy &&
250
- newMaterialTy.kind === "RawMat" &&
251
- newMaterialTy.rawMatName) {
252
- addNewCasting({
253
- casting: newMaterialTy.rawMatName,
254
- quantity: 1,
255
- queue: toQueue ?? "",
256
- serials: newSerial ? [newSerial] : undefined,
257
- workorder: newWorkorder,
258
- operator: enteredOperator ?? operator,
259
- });
260
- }
261
- setMatToShow(null);
262
- onClose();
263
- }, children: toQueue === null ? ("Add to Queue") : (_jsxs("span", { children: [curQueue !== null ? `Move From ${curQueue} To` : "Add To", " ", toQueue, withSerialMsg, addProcMsg] })) }) }) }) }));
214
+ return (_jsxs(_Fragment, { children: [_jsx(Tooltip, { title: toQueue === null
215
+ ? "Select a queue to add the material to"
216
+ : fmsInfo.requireOperatorNamePromptWhenAddingMaterial &&
217
+ (enteredOperator === null || enteredOperator === "")
218
+ ? "Please enter an operator name"
219
+ : "", children: _jsx("span", { children: _jsx(Button, { color: "primary", disabled: toQueue === null ||
220
+ addingExistingMat ||
221
+ addingNewMat ||
222
+ addingNewCasting ||
223
+ (existingMat === null && newMaterialTy === null) ||
224
+ (fmsInfo.requireOperatorNamePromptWhenAddingMaterial &&
225
+ (enteredOperator === null || enteredOperator === "")), onClick: async () => {
226
+ setAddError(null);
227
+ try {
228
+ if (existingMat) {
229
+ await addExistingMat({
230
+ materialId: existingMat.materialID,
231
+ queue: toQueue ?? "",
232
+ queuePosition: -1,
233
+ operator: enteredOperator ?? operator,
234
+ });
235
+ }
236
+ else if (newMaterialTy &&
237
+ newMaterialTy.kind === "JobAndProc" &&
238
+ newMaterialTy.jobUnique) {
239
+ await addNewMat({
240
+ jobUnique: newMaterialTy.jobUnique,
241
+ lastCompletedProcess: newMaterialTy.last_proc,
242
+ serial: newSerial ?? undefined,
243
+ workorder: newWorkorder ?? newMaterialTy?.workorder,
244
+ queue: toQueue ?? "",
245
+ queuePosition: -1,
246
+ operator: enteredOperator ?? operator,
247
+ });
248
+ }
249
+ else if (newMaterialTy &&
250
+ newMaterialTy.kind === "RawMat" &&
251
+ newMaterialTy.rawMatName) {
252
+ await addNewCasting({
253
+ casting: newMaterialTy.rawMatName,
254
+ quantity: 1,
255
+ queue: toQueue ?? "",
256
+ serials: newSerial ? [newSerial] : undefined,
257
+ workorder: newWorkorder,
258
+ operator: enteredOperator ?? operator,
259
+ });
260
+ }
261
+ setMatToShow(null);
262
+ onClose();
263
+ }
264
+ catch (e) {
265
+ setAddError(api.ApiException.isApiException(e)
266
+ ? e.response
267
+ : e instanceof Error
268
+ ? e.message
269
+ : String(e));
270
+ }
271
+ }, children: toQueue === null ? ("Add to Queue") : (_jsxs("span", { children: [curQueue !== null ? `Move From ${curQueue} To` : "Add To", " ", toQueue, withSerialMsg, addProcMsg] })) }) }) }), addError ? _jsx("p", { role: "alert", children: addError }) : null] }));
264
272
  }
@@ -43,6 +43,7 @@ import { last30Jobs } from "../../cell-status/scheduled-jobs.js";
43
43
  import { addDays } from "date-fns";
44
44
  import { durationToSeconds } from "../../util/parseISODuration.js";
45
45
  import { InvalidateCycleDialogButton, InvalidateCycleDialogContent, SwapMaterialButtons, SwapMaterialDialogContent, } from "./InvalidateCycle.js";
46
+ import { CancelLoadButton } from "./CancelLoadButton.js";
46
47
  import { QuarantineMatButton } from "./QuarantineButton.js";
47
48
  import { SelectInspTypeDialog, SignalInspectionButton } from "./SelectInspType.js";
48
49
  import { useSetTitle } from "../routes.js";
@@ -825,7 +826,7 @@ const SystemOverviewMaterialDialog = memo(function SystemOverviewMaterialDialog(
825
826
  setSwapSt(null);
826
827
  setInvalidateSt(null);
827
828
  }
828
- 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, { ignoreOperator: ignoreOperator }), _jsx(SignalInspectionButton, {}), _jsx(SwapMaterialButtons, { st: swapSt, setState: setSwapSt, onClose: onClose, ignoreOperator: ignoreOperator }), _jsx(InvalidateCycleDialogButton, { st: invalidateSt, setState: setInvalidateSt, onClose: onClose, ignoreOperator: ignoreOperator })] }) }));
829
+ 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, { ignoreOperator: ignoreOperator }), _jsx(CancelLoadButton, { onClose: onClose, ignoreOperator: ignoreOperator }), _jsx(SignalInspectionButton, {}), _jsx(SwapMaterialButtons, { st: swapSt, setState: setSwapSt, onClose: onClose, ignoreOperator: ignoreOperator }), _jsx(InvalidateCycleDialogButton, { st: invalidateSt, setState: setInvalidateSt, onClose: onClose, ignoreOperator: ignoreOperator })] }) }));
829
830
  });
830
831
  export function SystemOverviewPage({ ignoreOperator, whiteBackground, }) {
831
832
  useSetTitle("System Overview");
@@ -58,12 +58,12 @@ export const SortableRegion = memo(function SortableRegion(props) {
58
58
  const overMatId = over ? numericId(over.id) : null;
59
59
  if (over && active.id !== over.id && activeMatId !== null && overMatId !== null) {
60
60
  const overIdx = props.matIds.indexOf(overMatId);
61
- addExistingMatToQueue({
61
+ void addExistingMatToQueue({
62
62
  materialId: activeMatId,
63
63
  queue: props.queueName,
64
64
  queuePosition: overIdx,
65
65
  operator: operator,
66
- });
66
+ }).catch(console.error);
67
67
  reorderQueuedMat({
68
68
  queue: props.queueName,
69
69
  matId: activeMatId,
@@ -0,0 +1,21 @@
1
+ import { IInProcessMaterial } from "../network/api.js";
2
+ export type MaterialOperationState = {
3
+ readonly kind: "ActiveLoadStationOperation";
4
+ readonly cancellationId: string | null;
5
+ } | {
6
+ readonly kind: "AutomationControlled";
7
+ } | {
8
+ readonly kind: "HumanControlledQueue";
9
+ } | {
10
+ readonly kind: "AddToQueueProposal";
11
+ };
12
+ type MaterialForPolicy = Readonly<IInProcessMaterial> | null;
13
+ export declare function materialOperationState(material: Readonly<IInProcessMaterial>): MaterialOperationState;
14
+ export declare function isActiveLoadStationOperation(material: Readonly<IInProcessMaterial>): boolean;
15
+ export declare function canCancelLoad(material: MaterialForPolicy): boolean;
16
+ export declare function canSignalQuarantine(material: MaterialForPolicy): boolean;
17
+ export declare function canDirectlyQuarantine(material: MaterialForPolicy): boolean;
18
+ export declare function canRemoveFromQueue(material: MaterialForPolicy): boolean;
19
+ export declare function canInvalidateMaterial(material: MaterialForPolicy): boolean;
20
+ export declare function canAddOrMoveMaterialToQueue(material: MaterialForPolicy): boolean;
21
+ export {};
@@ -0,0 +1,89 @@
1
+ /* Copyright (c) 2026, John Lenz
2
+
3
+ All rights reserved.
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ * Redistributions of source code must retain the above copyright
9
+ notice, this list of conditions and the following disclaimer.
10
+
11
+ * Redistributions in binary form must reproduce the above copyright
12
+ notice, this list of conditions and the following disclaimer in the
13
+ documentation and/or other materials provided with the distribution.
14
+
15
+ * Neither the name of John Lenz, Black Maple Software, SeedTactics,
16
+ nor the names of other contributors may be used to endorse or promote
17
+ products derived from this software without specific prior written
18
+ permission.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
24
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30
+ POSSIBILITY OF SUCH DAMAGE.
31
+ */
32
+ import { ActionType, LocType } from "../network/api.js";
33
+ function nonBlankCancellationId(material) {
34
+ const cancellationId = material.action.loadCancellationId;
35
+ return cancellationId !== undefined && cancellationId.trim() !== "" ? cancellationId : null;
36
+ }
37
+ function hasLoadStationAction(material) {
38
+ switch (material.action.type) {
39
+ case ActionType.Loading:
40
+ case ActionType.UnloadToInProcess:
41
+ case ActionType.UnloadToCompletedMaterial:
42
+ case ActionType.LoadingToBasket:
43
+ return true;
44
+ default:
45
+ return false;
46
+ }
47
+ }
48
+ export function materialOperationState(material) {
49
+ const cancellationId = nonBlankCancellationId(material);
50
+ if (cancellationId !== null || hasLoadStationAction(material)) {
51
+ return { kind: "ActiveLoadStationOperation", cancellationId };
52
+ }
53
+ if (material.location.type === LocType.InQueue && material.action.type === ActionType.Waiting) {
54
+ return { kind: "HumanControlledQueue" };
55
+ }
56
+ if (material.location.type === LocType.OnPallet ||
57
+ material.location.type === LocType.InBasket ||
58
+ material.action.type === ActionType.Machining) {
59
+ return { kind: "AutomationControlled" };
60
+ }
61
+ return { kind: "AddToQueueProposal" };
62
+ }
63
+ export function isActiveLoadStationOperation(material) {
64
+ return materialOperationState(material).kind === "ActiveLoadStationOperation";
65
+ }
66
+ export function canCancelLoad(material) {
67
+ if (material === null)
68
+ return false;
69
+ const state = materialOperationState(material);
70
+ return state.kind === "ActiveLoadStationOperation" && state.cancellationId !== null;
71
+ }
72
+ export function canSignalQuarantine(material) {
73
+ return material !== null && materialOperationState(material).kind === "AutomationControlled";
74
+ }
75
+ export function canDirectlyQuarantine(material) {
76
+ return material !== null && materialOperationState(material).kind === "HumanControlledQueue";
77
+ }
78
+ export function canRemoveFromQueue(material) {
79
+ return canDirectlyQuarantine(material);
80
+ }
81
+ export function canInvalidateMaterial(material) {
82
+ return material === null || materialOperationState(material).kind === "AddToQueueProposal";
83
+ }
84
+ export function canAddOrMoveMaterialToQueue(material) {
85
+ if (material === null)
86
+ return true;
87
+ const state = materialOperationState(material);
88
+ return state.kind === "AddToQueueProposal" || state.kind === "HumanControlledQueue";
89
+ }
@@ -0,0 +1,11 @@
1
+ import type { AppProps } from "./components/App.js";
2
+ /**
3
+ * Starts one complete FMS Insight application with optional statically composed custom pages.
4
+ */
5
+ export declare function bootstrapInsight(appProps?: AppProps, rootElement?: HTMLElement): Promise<void>;
6
+ export { customState } from "./cell-status/custom-state.js";
7
+ export type { AppProps };
8
+ export { defaultChooseModes } from "./components/ChooseMode.js";
9
+ export type { ChooseModeItem } from "./components/ChooseMode.js";
10
+ export { RouteLocation } from "./components/routes.js";
11
+ export { authenticatedFetch } from "./network/backend.js";