@seedtactics/insight-client 16.8.4-beta.1 → 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.
Files changed (40) hide show
  1. package/README.md +21 -0
  2. package/dist/assets/index-CQhVwNX7.js +388 -0
  3. package/dist/cell-status/material-details.d.ts +15 -5
  4. package/dist/cell-status/material-details.js +58 -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/operations/Programs.js +1 -1
  10. package/dist/components/station-monitor/BasketLoadStationWork.js +44 -36
  11. package/dist/components/station-monitor/BulkRawMaterial.js +4 -4
  12. package/dist/components/station-monitor/CancelLoadButton.d.ts +4 -0
  13. package/dist/components/station-monitor/CancelLoadButton.js +59 -0
  14. package/dist/components/station-monitor/Closeout.js +2 -1
  15. package/dist/components/station-monitor/Inspection.js +2 -1
  16. package/dist/components/station-monitor/InvalidateCycle.d.ts +4 -1
  17. package/dist/components/station-monitor/InvalidateCycle.js +61 -10
  18. package/dist/components/station-monitor/LoadStation.js +23 -11
  19. package/dist/components/station-monitor/Material.js +2 -4
  20. package/dist/components/station-monitor/QuarantineButton.js +80 -60
  21. package/dist/components/station-monitor/Queues.js +2 -1
  22. package/dist/components/station-monitor/QueuesAddMaterial.js +59 -48
  23. package/dist/components/station-monitor/SystemOverview.js +3 -2
  24. package/dist/components/station-monitor/Whiteboard.js +2 -2
  25. package/dist/extension.d.ts +11 -0
  26. package/dist/extension.js +21 -0
  27. package/dist/index.html +1 -2
  28. package/dist/index.js +1 -1
  29. package/dist/network/api.d.ts +18 -4
  30. package/dist/network/api.js +69 -2
  31. package/dist/network/backend-mock.js +3 -0
  32. package/dist/network/backend.d.ts +1 -0
  33. package/dist/network/backend.js +2 -1
  34. package/package.json +12 -11
  35. package/dist/assets/index-Y5yNHUvc.js +0 -388
  36. package/dist/assets/manifest-BzJli0lx.webmanifest +0 -15
  37. package/dist/bootstrap.d.ts +0 -5
  38. package/dist/bootstrap.js +0 -56
  39. package/dist/components/station-monitor/Material.test.d.ts +0 -1
  40. package/dist/components/station-monitor/Material.test.js +0 -199
@@ -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 = "";
@@ -235,14 +235,12 @@ export function MaterialAction({ mat, displayActionForSinglePallet, fsize, }) {
235
235
  }
236
236
  }
237
237
  case api.ActionType.LoadingToBasket:
238
- return (_jsxs(MatCardDetail, { fsize: fsize, children: ["Load into ", basketName, " ", mat.action.loadToBasketId ?? "", mat.action.loadToBasketSlot !== undefined
239
- ? ` slot ${mat.action.loadToBasketSlot + 1}`
240
- : ""] }));
238
+ return (_jsxs(MatCardDetail, { fsize: fsize, children: ["Load into ", basketName, " ", mat.action.loadToBasketId ?? "", mat.action.loadToBasketSlot !== undefined ? ` slot ${mat.action.loadToBasketSlot}` : ""] }));
241
239
  case api.ActionType.UnloadToInProcess:
242
240
  case api.ActionType.UnloadToCompletedMaterial:
243
241
  if (mat.action.unloadToBasketId) {
244
242
  return (_jsxs(MatCardDetail, { fsize: fsize, children: ["Unload to ", basketName, " ", mat.action.unloadToBasketId, mat.action.unloadToBasketSlot !== undefined
245
- ? ` slot ${mat.action.unloadToBasketSlot + 1}`
243
+ ? ` slot ${mat.action.unloadToBasketSlot}`
246
244
  : ""] }));
247
245
  }
248
246
  else if (mat.action.unloadIntoQueue) {
@@ -37,7 +37,7 @@ import { LazySeq } from "@seedtactics/immutable-collections";
37
37
  import { currentStatus } from "../../cell-status/current-status.js";
38
38
  import { inProcessMaterialInDialog, materialDialogOpen, 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 { ActionType, ApiException, LocType, QueueRole, } from "../../network/api.js";
41
41
  import { fmsInformation } from "../../network/server-settings.js";
42
42
  import { useAtomValue, useSetAtom } from "jotai";
43
43
  function useQuarantineMaterial(ignoreOperator) {
@@ -70,75 +70,48 @@ function useQuarantineMaterial(ignoreOperator) {
70
70
  const quarantineQueues = LazySeq.ofObject(curSt.queues)
71
71
  .filter(([qname, _]) => !activeQueues.has(qname))
72
72
  .toRSet(([qname, _]) => qname);
73
- const palLoc = LazySeq.ofObject(curSt.pallets).toOrderedMap(([_, pal]) => [
74
- pal.palletNum,
75
- pal.currentPalletLocation,
76
- ]);
77
73
  // If in a quarantine queue, allow removal from system
78
74
  if (inProcMat.location.type === LocType.InQueue &&
79
75
  inProcMat.location.currentQueue &&
80
76
  quarantineQueues.has(inProcMat.location.currentQueue)) {
81
77
  return {
82
78
  type: "Remove",
79
+ material: inProcMat,
83
80
  quarantine: () => removeFromQueue(inProcMat.materialID, operator),
84
81
  removing: removingFromQueue,
85
82
  quarantineQueueDestination: null,
83
+ removeFromQueues: null,
84
+ removingFromQueues: removingFromQueue,
86
85
  };
87
86
  }
88
87
  let type = null;
89
88
  switch (inProcMat.location.type) {
90
89
  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
- }
90
+ if (inProcMat.action.type === ActionType.Loading) {
91
+ return null;
101
92
  }
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";
93
+ // Check that the job outputs to a queue, only then can signaling for quarantine work
94
+ const job = curSt.jobs[inProcMat.jobUnique];
95
+ if (!job)
96
+ return null;
97
+ const path = job.procsAndPaths?.[inProcMat.process - 1]?.paths?.[inProcMat.path - 1];
98
+ if (inProcMat.process != job.procsAndPaths.length && (!path || !path.outputQueue)) {
99
+ return null;
116
100
  }
101
+ type = "SignalForScrap";
117
102
  break;
118
103
  case LocType.InQueue:
119
- if ((inProcMat.action.type === ActionType.Loading ||
120
- inProcMat.action.type === ActionType.LoadingToBasket) &&
121
- !fmsInfo.allowQuarantineToCancelLoad) {
122
- return null;
123
- }
124
- else if (inProcMat.action.type === ActionType.Loading ||
104
+ if (inProcMat.action.type === ActionType.Loading ||
125
105
  inProcMat.action.type === ActionType.LoadingToBasket) {
126
- type = "CancelLoad";
127
- }
128
- else {
129
- type = "Scrap";
106
+ return null;
130
107
  }
108
+ type = "Scrap";
131
109
  break;
132
110
  case LocType.InBasket:
133
- if (inProcMat.action.type === ActionType.Loading && !fmsInfo.allowQuarantineToCancelLoad) {
111
+ if (inProcMat.action.type === ActionType.Loading) {
134
112
  return null;
135
113
  }
136
- else if (inProcMat.action.type === ActionType.Loading) {
137
- type = "CancelLoad";
138
- }
139
- else {
140
- type = "Scrap";
141
- }
114
+ type = "Scrap";
142
115
  break;
143
116
  case LocType.Free:
144
117
  type = "Scrap";
@@ -147,18 +120,51 @@ function useQuarantineMaterial(ignoreOperator) {
147
120
  if (type) {
148
121
  return {
149
122
  type,
150
- quarantine: (reason) => signalQuarantine(inProcMat.materialID, operator, reason),
123
+ material: inProcMat,
124
+ quarantine: async (reason) => signalQuarantine(inProcMat.materialID, operator, reason),
151
125
  removing: signalingQuarantine,
152
126
  quarantineQueueDestination: quarantineQueue,
127
+ removeFromQueues: inProcMat.location.type === LocType.InQueue &&
128
+ inProcMat.location.currentQueue !== undefined &&
129
+ activeQueues.has(inProcMat.location.currentQueue) &&
130
+ inProcMat.action.type === ActionType.Waiting
131
+ ? () => removeFromQueue(inProcMat.materialID, operator)
132
+ : null,
133
+ removingFromQueues: removingFromQueue,
153
134
  };
154
135
  }
155
136
  else {
156
137
  return null;
157
138
  }
158
139
  }
140
+ function RemoveFromQueuesButton({ material, removeFromQueues, removing, }) {
141
+ const [open, setOpen] = useState(false);
142
+ const [error, setError] = useState(null);
143
+ const setMatToShow = useSetAtom(materialDialogOpen);
144
+ async function remove() {
145
+ setError(null);
146
+ try {
147
+ await removeFromQueues();
148
+ setOpen(false);
149
+ setMatToShow({
150
+ type: "MatSummary",
151
+ summary: {
152
+ materialID: material.materialID,
153
+ partName: material.partName,
154
+ serial: material.serial,
155
+ },
156
+ });
157
+ }
158
+ catch (e) {
159
+ setError(ApiException.isApiException(e) ? e.response : e instanceof Error ? e.message : String(e));
160
+ }
161
+ }
162
+ return (_jsxs(_Fragment, { children: [_jsx(Tooltip, { title: "Remove from all queues", children: _jsx(Button, { color: "primary", disabled: removing, onClick: () => setOpen(true), children: "Remove from Queues" }) }), _jsxs(Dialog, { open: open, onClose: () => setOpen(false), children: [_jsx(DialogTitle, { children: "Remove Material from Queues" }), _jsxs(DialogContent, { children: [_jsx("p", { children: "The material will be removed from all queues." }), error ? _jsx("p", { role: "alert", children: error }) : null] }), _jsxs(DialogActions, { children: [_jsx(Button, { color: "primary", disabled: removing, onClick: remove, children: "Remove from Queues" }), _jsx(Button, { color: "secondary", disabled: removing, onClick: () => setOpen(false), children: "Cancel" })] })] })] }));
163
+ }
159
164
  export function QuarantineMatButton({ onClose, ignoreOperator, }) {
160
165
  const [open, setOpen] = useState(false);
161
166
  const [reason, setReason] = useState("");
167
+ const [error, setError] = useState(null);
162
168
  const q = useQuarantineMaterial(!!ignoreOperator);
163
169
  const setMatToShow = useSetAtom(materialDialogOpen);
164
170
  if (q === null)
@@ -182,19 +188,33 @@ export function QuarantineMatButton({ onClose, ignoreOperator, }) {
182
188
  : "After unload, remove the part as scrap";
183
189
  btnTxt = q.quarantineQueueDestination ? "Quarantine" : "Scrap";
184
190
  break;
185
- case "CancelLoad":
186
- title = q.quarantineQueueDestination
187
- ? `Cancel load and move to ${q.quarantineQueueDestination}`
188
- : "Cancel load";
189
- btnTxt = "Cancel Load";
190
- break;
191
191
  }
192
- function quarantine() {
193
- q?.quarantine(reason);
194
- setMatToShow(null);
195
- setOpen(false);
196
- setReason("");
197
- onClose?.();
192
+ async function quarantine() {
193
+ if (!q)
194
+ return;
195
+ setError(null);
196
+ try {
197
+ await q.quarantine(reason);
198
+ setOpen(false);
199
+ setReason("");
200
+ if (q.type === "Remove") {
201
+ setMatToShow({
202
+ type: "MatSummary",
203
+ summary: {
204
+ materialID: q.material.materialID,
205
+ partName: q.material.partName,
206
+ serial: q.material.serial,
207
+ },
208
+ });
209
+ }
210
+ else {
211
+ setMatToShow(null);
212
+ onClose?.();
213
+ }
214
+ }
215
+ catch (e) {
216
+ setError(ApiException.isApiException(e) ? e.response : e instanceof Error ? e.message : String(e));
217
+ }
198
218
  }
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" })] })] })] }));
219
+ return (_jsxs(_Fragment, { children: [_jsx(Tooltip, { title: title, children: _jsx(Button, { color: "primary", disabled: q.removing, onClick: () => setOpen(true), children: btnTxt }) }), q.removeFromQueues ? (_jsx(RemoveFromQueuesButton, { material: q.material, removeFromQueues: q.removeFromQueues, removing: q.removingFromQueues })) : null, _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 }), error ? _jsx("p", { role: "alert", children: error }) : null] }), _jsxs(DialogActions, { children: [_jsx(Button, { color: "primary", disabled: q.removing, onClick: quarantine, children: btnTxt }), _jsx(Button, { color: "secondary", onClick: () => setOpen(false), children: "Cancel" })] })] })] }));
200
220
  }
@@ -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);
@@ -183,6 +183,7 @@ export function AddToQueueButton({ st: { enteredOperator, newMaterialTy, toQueue
183
183
  const [addExistingMat, addingExistingMat] = matDetails.useAddExistingMaterialToQueue();
184
184
  const [addNewMat, addingNewMat] = matDetails.useAddNewMaterialToQueue();
185
185
  const [addNewCasting, addingNewCasting] = useAddNewCastingToQueue();
186
+ const [addError, setAddError] = useState(null);
186
187
  const allowAdd = useAllowAddToQueue(queueNames);
187
188
  if (!allowAdd) {
188
189
  return null;
@@ -213,52 +214,62 @@ export function AddToQueueButton({ st: { enteredOperator, newMaterialTy, toQueue
213
214
  withSerialMsg = " With Serial " + newSerial;
214
215
  }
215
216
  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] })) }) }) }) }));
217
+ return (_jsxs(_Fragment, { children: [_jsx(Tooltip, { title: toQueue === null
218
+ ? "Select a queue to add the material to"
219
+ : fmsInfo.requireOperatorNamePromptWhenAddingMaterial &&
220
+ (enteredOperator === null || enteredOperator === "")
221
+ ? "Please enter an operator name"
222
+ : "", children: _jsx("span", { children: _jsx(Button, { color: "primary", disabled: toQueue === null ||
223
+ addingExistingMat ||
224
+ addingNewMat ||
225
+ addingNewCasting ||
226
+ (existingMat === null && newMaterialTy === null) ||
227
+ (fmsInfo.requireOperatorNamePromptWhenAddingMaterial &&
228
+ (enteredOperator === null || enteredOperator === "")), onClick: async () => {
229
+ setAddError(null);
230
+ try {
231
+ if (existingMat) {
232
+ await addExistingMat({
233
+ materialId: existingMat.materialID,
234
+ queue: toQueue ?? "",
235
+ queuePosition: -1,
236
+ operator: enteredOperator ?? operator,
237
+ });
238
+ }
239
+ else if (newMaterialTy &&
240
+ newMaterialTy.kind === "JobAndProc" &&
241
+ newMaterialTy.jobUnique) {
242
+ await addNewMat({
243
+ jobUnique: newMaterialTy.jobUnique,
244
+ lastCompletedProcess: newMaterialTy.last_proc,
245
+ serial: newSerial ?? undefined,
246
+ workorder: newWorkorder ?? newMaterialTy?.workorder,
247
+ queue: toQueue ?? "",
248
+ queuePosition: -1,
249
+ operator: enteredOperator ?? operator,
250
+ });
251
+ }
252
+ else if (newMaterialTy &&
253
+ newMaterialTy.kind === "RawMat" &&
254
+ newMaterialTy.rawMatName) {
255
+ await addNewCasting({
256
+ casting: newMaterialTy.rawMatName,
257
+ quantity: 1,
258
+ queue: toQueue ?? "",
259
+ serials: newSerial ? [newSerial] : undefined,
260
+ workorder: newWorkorder,
261
+ operator: enteredOperator ?? operator,
262
+ });
263
+ }
264
+ setMatToShow(null);
265
+ onClose();
266
+ }
267
+ catch (e) {
268
+ setAddError(api.ApiException.isApiException(e)
269
+ ? e.response
270
+ : e instanceof Error
271
+ ? e.message
272
+ : String(e));
273
+ }
274
+ }, 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
275
  }
@@ -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";
@@ -446,7 +447,7 @@ function PalletFaces({ maxNumFaces, mats: allMats, loadingOntoPallet, noFilter,
446
447
  }
447
448
  }
448
449
  function BasketContents({ mats }) {
449
- const byPosition = LazySeq.of(mats).orderedGroupBy((m) => (m.location.basketSlot ?? 0) + 1);
450
+ const byPosition = LazySeq.of(mats).orderedGroupBy((m) => m.location.basketSlot ?? 0);
450
451
  return (_jsx(Box, { sx: {
451
452
  display: "flex",
452
453
  flexWrap: "wrap",
@@ -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,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";