@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.
- package/README.md +21 -0
- package/dist/assets/index-BkKYqUM0.js +388 -0
- package/dist/cell-status/material-details.d.ts +19 -5
- package/dist/cell-status/material-details.js +71 -37
- package/dist/components/LogEntry.js +2 -9
- package/dist/components/log-entry-container-name.d.ts +2 -0
- package/dist/components/log-entry-container-name.js +10 -0
- package/dist/components/operations/AllMaterial.js +4 -3
- package/dist/components/station-monitor/BasketLoadStationWork.js +12 -8
- package/dist/components/station-monitor/BulkRawMaterial.js +4 -4
- package/dist/components/station-monitor/CancelLoadButton.d.ts +4 -0
- package/dist/components/station-monitor/CancelLoadButton.js +60 -0
- package/dist/components/station-monitor/Closeout.js +2 -1
- package/dist/components/station-monitor/Inspection.js +2 -1
- package/dist/components/station-monitor/InvalidateCycle.d.ts +4 -1
- package/dist/components/station-monitor/InvalidateCycle.js +104 -38
- package/dist/components/station-monitor/LoadStation.js +25 -15
- package/dist/components/station-monitor/Material.js +14 -1
- package/dist/components/station-monitor/QuarantineButton.js +160 -90
- package/dist/components/station-monitor/Queues.js +2 -1
- package/dist/components/station-monitor/QueuesAddMaterial.js +65 -57
- package/dist/components/station-monitor/SystemOverview.js +2 -1
- package/dist/components/station-monitor/Whiteboard.js +2 -2
- package/dist/data/material-operation-policy.d.ts +21 -0
- package/dist/data/material-operation-policy.js +89 -0
- package/dist/extension.d.ts +11 -0
- package/dist/extension.js +21 -0
- package/dist/index.html +1 -2
- package/dist/index.js +1 -1
- package/dist/network/api.d.ts +20 -4
- package/dist/network/api.js +163 -2
- package/dist/network/backend-mock.js +6 -0
- package/dist/network/backend.d.ts +2 -0
- package/dist/network/backend.js +2 -1
- package/package.json +12 -11
- package/dist/assets/index-DHusyIyb.js +0 -388
- package/dist/assets/manifest-BzJli0lx.webmanifest +0 -15
- package/dist/bootstrap.d.ts +0 -5
- package/dist/bootstrap.js +0 -56
- package/dist/components/station-monitor/Material.test.d.ts +0 -1
- 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 {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
|
120
|
-
inProcMat.action.type === ActionType.LoadingToBasket) &&
|
|
121
|
-
!fmsInfo.allowQuarantineToCancelLoad) {
|
|
103
|
+
if (!canDirectlyQuarantine(inProcMat))
|
|
122
104
|
return null;
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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 (
|
|
169
|
-
case "
|
|
202
|
+
switch (displayedSnapshot.operation) {
|
|
203
|
+
case "RemoveFromSystem":
|
|
170
204
|
title = "Remove from system";
|
|
171
205
|
btnTxt = "Remove";
|
|
172
206
|
break;
|
|
173
|
-
case "
|
|
174
|
-
title =
|
|
175
|
-
? `Move to ${
|
|
176
|
-
: "Remove from queue";
|
|
177
|
-
btnTxt =
|
|
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 "
|
|
180
|
-
title =
|
|
181
|
-
? `
|
|
182
|
-
: "
|
|
183
|
-
btnTxt =
|
|
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
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
56
|
-
|
|
57
|
-
|
|
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 (
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
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";
|