@seedtactics/insight-client 17.0.1-beta.6 → 17.0.1-beta.8
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/dist/assets/index-MFZreX-b.js +388 -0
- package/dist/cell-status/inspections.js +0 -20
- package/dist/cell-status/material-summary.js +1 -87
- package/dist/cell-status/station-cycles.d.ts +1 -0
- package/dist/cell-status/station-cycles.js +11 -14
- package/dist/components/operations/AllMaterial.js +2 -4
- package/dist/components/station-monitor/BasketLoadStationWork.js +64 -16
- package/dist/components/station-monitor/InvalidateCycle.d.ts +1 -17
- package/dist/components/station-monitor/InvalidateCycle.js +1 -91
- package/dist/components/station-monitor/LoadStation.js +3 -5
- package/dist/components/station-monitor/Material.js +5 -3
- package/dist/components/station-monitor/SystemOverview.js +2 -4
- package/dist/index.html +1 -1
- package/dist/network/api.d.ts +24 -32
- package/dist/network/api.js +53 -116
- package/dist/network/backend-mock.js +0 -3
- package/dist/network/backend.d.ts +0 -1
- package/docs/operator-procedures.md +6 -6
- package/package.json +1 -1
- package/dist/assets/index-C6EBFHuW.js +0 -388
|
@@ -185,26 +185,6 @@ export const updateLast30Inspections = atom(null, (_, set, { evt, now, expire })
|
|
|
185
185
|
return parts.union(LazySeq.of(log).toLookupMap((e) => e.key, (e) => e.entry.cntr, (e) => e.entry), (e1, e2) => e1.union(e2));
|
|
186
186
|
});
|
|
187
187
|
}
|
|
188
|
-
else if (evt.editMaterialInLog) {
|
|
189
|
-
const changedByCntr = evt.editMaterialInLog.editedEvents;
|
|
190
|
-
set(last30InspectionsRW, (parts) => parts.collectValues((entries) => {
|
|
191
|
-
for (const changed of changedByCntr) {
|
|
192
|
-
// inspection logs have only a single material
|
|
193
|
-
const mat = changed?.material[0];
|
|
194
|
-
const old = entries.get(changed.counter);
|
|
195
|
-
if (old !== undefined && mat) {
|
|
196
|
-
const newEntry = {
|
|
197
|
-
...old,
|
|
198
|
-
materialID: mat.id,
|
|
199
|
-
serial: mat.serial,
|
|
200
|
-
workorder: mat.workorder,
|
|
201
|
-
};
|
|
202
|
-
entries = entries.set(changed.counter, newEntry);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
return entries;
|
|
206
|
-
}));
|
|
207
|
-
}
|
|
208
188
|
});
|
|
209
189
|
export const setSpecificMonthInspections = atom(null, (_, set, log) => {
|
|
210
190
|
set(specificMonthInspectionsRW, LazySeq.of(log)
|
|
@@ -30,7 +30,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
30
30
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
31
31
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
32
32
|
*/
|
|
33
|
-
import { LogType
|
|
33
|
+
import { LogType } from "../network/api.js";
|
|
34
34
|
import { addDays } from "date-fns";
|
|
35
35
|
import { HashMap, LazySeq, HashSet } from "@seedtactics/immutable-collections";
|
|
36
36
|
import { atom } from "jotai";
|
|
@@ -203,88 +203,6 @@ function filter_old(expire, { matIdsForJob, matsById }) {
|
|
|
203
203
|
matsById = matsById.filter((e) => e.last_event >= expire);
|
|
204
204
|
return { matIdsForJob, matsById };
|
|
205
205
|
}
|
|
206
|
-
function process_swap(swap, st) {
|
|
207
|
-
let jobs = st.matIdsForJob;
|
|
208
|
-
const oldMatFromState = st.matsById.get(swap.oldMaterialID) ?? null;
|
|
209
|
-
const newMatFromState = st.matsById.get(swap.newMaterialID) ?? null;
|
|
210
|
-
if (oldMatFromState === null)
|
|
211
|
-
return st;
|
|
212
|
-
let oldMat = oldMatFromState;
|
|
213
|
-
let newMat;
|
|
214
|
-
if (newMatFromState === null) {
|
|
215
|
-
newMat = {
|
|
216
|
-
materialID: swap.newMaterialID,
|
|
217
|
-
jobUnique: oldMat.jobUnique,
|
|
218
|
-
partName: oldMat.partName,
|
|
219
|
-
last_event: oldMat.last_event,
|
|
220
|
-
numProcesses: oldMat.numProcesses,
|
|
221
|
-
startedProcess1: true,
|
|
222
|
-
unloaded_processes: {},
|
|
223
|
-
signaledInspections: [],
|
|
224
|
-
quarantineAfterUnload: null,
|
|
225
|
-
completedInspections: {},
|
|
226
|
-
};
|
|
227
|
-
}
|
|
228
|
-
else {
|
|
229
|
-
newMat = newMatFromState;
|
|
230
|
-
}
|
|
231
|
-
if (oldMat.jobUnique &&
|
|
232
|
-
oldMat.jobUnique !== "" &&
|
|
233
|
-
(!newMat.jobUnique || newMat.jobUnique === "")) {
|
|
234
|
-
// Swap newMat from raw material
|
|
235
|
-
const forJob = jobs.get(oldMat.jobUnique);
|
|
236
|
-
if (forJob !== undefined) {
|
|
237
|
-
jobs = jobs.set(oldMat.jobUnique, forJob.delete(oldMat.materialID).add(newMat.materialID));
|
|
238
|
-
}
|
|
239
|
-
newMat = { ...newMat, jobUnique: oldMat.jobUnique };
|
|
240
|
-
oldMat = { ...oldMat, jobUnique: "" };
|
|
241
|
-
}
|
|
242
|
-
const oldMatUnloads = oldMat.unloaded_processes;
|
|
243
|
-
oldMat = { ...oldMat, unloaded_processes: newMat.unloaded_processes };
|
|
244
|
-
newMat = { ...newMat, unloaded_processes: oldMatUnloads };
|
|
245
|
-
for (const evt of swap.editedEvents) {
|
|
246
|
-
const newMatFromEvt = evt.material.find((m) => m.id === swap.newMaterialID);
|
|
247
|
-
if (newMatFromEvt) {
|
|
248
|
-
newMat = {
|
|
249
|
-
...newMat,
|
|
250
|
-
serial: newMatFromEvt.serial ?? newMat.serial,
|
|
251
|
-
workorderId: newMatFromEvt.workorder ?? newMat.workorderId,
|
|
252
|
-
};
|
|
253
|
-
}
|
|
254
|
-
switch (evt.type) {
|
|
255
|
-
case LogType.Inspection:
|
|
256
|
-
case LogType.InspectionForce: {
|
|
257
|
-
const inspType = evt.program;
|
|
258
|
-
let inspect;
|
|
259
|
-
if (evt.result.toLowerCase() === "true" || evt.result === "1") {
|
|
260
|
-
inspect = true;
|
|
261
|
-
}
|
|
262
|
-
else {
|
|
263
|
-
inspect = false;
|
|
264
|
-
}
|
|
265
|
-
if (inspect) {
|
|
266
|
-
// remove from oldMat, add to newMat
|
|
267
|
-
oldMat = {
|
|
268
|
-
...oldMat,
|
|
269
|
-
signaledInspections: LazySeq.of(oldMat.signaledInspections)
|
|
270
|
-
.filter((i) => i !== inspType)
|
|
271
|
-
.toRArray(),
|
|
272
|
-
};
|
|
273
|
-
newMat = {
|
|
274
|
-
...newMat,
|
|
275
|
-
signaledInspections: LazySeq.of([...newMat.signaledInspections, inspType])
|
|
276
|
-
.distinct()
|
|
277
|
-
.toSortedArray((x) => x),
|
|
278
|
-
};
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
return {
|
|
284
|
-
matsById: st.matsById.set(oldMat.materialID, oldMat).set(newMat.materialID, newMat),
|
|
285
|
-
matIdsForJob: jobs,
|
|
286
|
-
};
|
|
287
|
-
}
|
|
288
206
|
export const setLast30MatSummary = atom(null, (_, set, log) => {
|
|
289
207
|
set(last30MaterialSummaryRW, (st) => log.reduce(process_event, st));
|
|
290
208
|
});
|
|
@@ -301,10 +219,6 @@ export const updateLast30MatSummary = atom(null, (_, set, { evt, now, expire })
|
|
|
301
219
|
}
|
|
302
220
|
});
|
|
303
221
|
}
|
|
304
|
-
else if (evt.editMaterialInLog) {
|
|
305
|
-
const edit = evt.editMaterialInLog;
|
|
306
|
-
set(last30MaterialSummaryRW, (st) => process_swap(edit, st));
|
|
307
|
-
}
|
|
308
222
|
});
|
|
309
223
|
export const setSpecificMonthMatSummary = atom(null, (_, set, log) => {
|
|
310
224
|
set(specificMonthMaterialSummaryRW, log.reduce(process_event, {
|
|
@@ -43,6 +43,7 @@ export declare function loadStationDisplayName(stationNumber: number, loadStatio
|
|
|
43
43
|
export declare function displayStationName(stationGroup: string, stationNumber: number, loadStationNames: {
|
|
44
44
|
[key: string]: string;
|
|
45
45
|
} | undefined): string;
|
|
46
|
+
export declare function basketSlotLabel(slot: number): string;
|
|
46
47
|
export declare function basketDisplayName(basketName: string | null | undefined): string;
|
|
47
48
|
export declare function carrierLabel(cycle: Readonly<PartCycleData>, basketName?: string): string;
|
|
48
49
|
export declare function carrierSortKey(cycle: Readonly<PartCycleData>): number;
|
|
@@ -72,6 +72,17 @@ export function displayStationName(stationGroup, stationNumber, loadStationNames
|
|
|
72
72
|
? loadStationDisplayName(stationNumber, loadStationNames)
|
|
73
73
|
: stat_name_and_num(stationGroup, stationNumber);
|
|
74
74
|
}
|
|
75
|
+
// Basket slots retain one-based numeric identities in the API and persistence. The operator UI
|
|
76
|
+
// displays them alphabetically: 1=A, 2=B, ..., 26=Z, 27=AA.
|
|
77
|
+
export function basketSlotLabel(slot) {
|
|
78
|
+
if (!Number.isSafeInteger(slot) || slot <= 0)
|
|
79
|
+
return String(slot);
|
|
80
|
+
let label = "";
|
|
81
|
+
for (let remaining = slot; remaining > 0; remaining = Math.floor((remaining - 1) / 26)) {
|
|
82
|
+
label = String.fromCharCode(65 + ((remaining - 1) % 26)) + label;
|
|
83
|
+
}
|
|
84
|
+
return label;
|
|
85
|
+
}
|
|
75
86
|
export function basketDisplayName(basketName) {
|
|
76
87
|
return basketName && basketName.trim().length > 0 ? basketName : "Basket";
|
|
77
88
|
}
|
|
@@ -151,16 +162,6 @@ function convertOldLogsToCycles(estimateCycleTimes, log, loadStationNames) {
|
|
|
151
162
|
.collect((c) => convertLogToCycle(estimateCycleTimes, c.cycle, c.elapsedForSingleMaterialMinutes, loadStationNames))
|
|
152
163
|
.buildHashMap((c) => c.cntr);
|
|
153
164
|
}
|
|
154
|
-
function process_swap(swap, partCycles) {
|
|
155
|
-
for (const changed of swap.editedEvents) {
|
|
156
|
-
const c = partCycles.get(changed.counter);
|
|
157
|
-
if (c !== undefined) {
|
|
158
|
-
const newC = { ...c, material: changed.material };
|
|
159
|
-
partCycles = partCycles.set(changed.counter, newC);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
return partCycles;
|
|
163
|
-
}
|
|
164
165
|
export const setLast30StationCycles = atom(null, (get, set, log) => {
|
|
165
166
|
const estimatedCycleTimes = get(last30EstimatedCycleTimes);
|
|
166
167
|
const loadStationNames = get(fmsInformation)?.loadStationNames;
|
|
@@ -204,10 +205,6 @@ export const updateLast30StationCycles = atom(null, (get, set, { evt, now, expir
|
|
|
204
205
|
return cycles;
|
|
205
206
|
});
|
|
206
207
|
}
|
|
207
|
-
else if (evt.editMaterialInLog) {
|
|
208
|
-
const edit = evt.editMaterialInLog;
|
|
209
|
-
set(last30StationCyclesRW, (oldCycles) => process_swap(edit, oldCycles));
|
|
210
|
-
}
|
|
211
208
|
});
|
|
212
209
|
export const setSpecificMonthStationCycles = atom(null, (get, set, log) => {
|
|
213
210
|
const estimatedCycleTimes = get(specificMonthEstimatedCycleTimes);
|
|
@@ -39,7 +39,7 @@ import { Box } from "@mui/material";
|
|
|
39
39
|
import { Typography } from "@mui/material";
|
|
40
40
|
import { LazySeq } from "@seedtactics/immutable-collections";
|
|
41
41
|
import { DragOverlayInProcMaterial, InProcMaterial, MaterialDialog, SortableInProcMaterial, } from "../station-monitor/Material.js";
|
|
42
|
-
import { InvalidateCycleDialogButton, InvalidateCycleDialogContent,
|
|
42
|
+
import { InvalidateCycleDialogButton, InvalidateCycleDialogContent, } from "../station-monitor/InvalidateCycle.js";
|
|
43
43
|
import { horizontalListSortingStrategy, SortableContext, useSortable, verticalListSortingStrategy, } from "@dnd-kit/sortable";
|
|
44
44
|
import { closestCenter, DndContext, DragOverlay, getFirstCollision, MeasuringStrategy, pointerWithin, rectIntersection, } from "@dnd-kit/core";
|
|
45
45
|
import { QuarantineMatButton } from "../station-monitor/QuarantineButton.js";
|
|
@@ -147,13 +147,11 @@ function MaterialBinColumn({ matBin, isDragOverlay, }) {
|
|
|
147
147
|
}
|
|
148
148
|
}
|
|
149
149
|
const AllMatDialog = memo(function AllMatDialog() {
|
|
150
|
-
const [swapSt, setSwapSt] = useState(null);
|
|
151
150
|
const [invalidateSt, setInvalidateSt] = useState(null);
|
|
152
151
|
function onClose() {
|
|
153
|
-
setSwapSt(null);
|
|
154
152
|
setInvalidateSt(null);
|
|
155
153
|
}
|
|
156
|
-
return (_jsx(MaterialDialog, { onClose: onClose, allowNote: true, highlightProcsGreaterOrEqualTo: invalidateSt?.process ?? undefined, extraDialogElements:
|
|
154
|
+
return (_jsx(MaterialDialog, { onClose: onClose, allowNote: true, highlightProcsGreaterOrEqualTo: invalidateSt?.process ?? undefined, extraDialogElements: _jsx(_Fragment, { children: invalidateSt !== null ? (_jsx(InvalidateCycleDialogContent, { st: invalidateSt, setState: setInvalidateSt })) : null }), buttons: _jsxs(_Fragment, { children: [_jsx(QuarantineMatButton, { onClose: onClose, ignoreOperator: true }), _jsx(CancelLoadButton, { onClose: onClose, ignoreOperator: true }), _jsx(InvalidateCycleDialogButton, { st: invalidateSt, setState: setInvalidateSt, onClose: onClose, ignoreOperator: true })] }) }));
|
|
157
155
|
});
|
|
158
156
|
function useCollisionDetection(allBins) {
|
|
159
157
|
return useCallback((args) => {
|
|
@@ -31,6 +31,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
31
31
|
import { useMemo, useState } from "react";
|
|
32
32
|
import { Alert, Box, Button, Stack, Typography } from "@mui/material";
|
|
33
33
|
import { LazySeq } from "@seedtactics/immutable-collections";
|
|
34
|
+
import { basketSlotLabel } from "../../cell-status/station-cycles.js";
|
|
34
35
|
import * as api from "../../network/api.js";
|
|
35
36
|
import { InProcMaterial } from "./Material.js";
|
|
36
37
|
import { MoveMaterialArrowNode } from "./MoveMaterialArrows.js";
|
|
@@ -69,14 +70,48 @@ function basketAction(mat, basketId) {
|
|
|
69
70
|
}
|
|
70
71
|
return undefined;
|
|
71
72
|
}
|
|
72
|
-
function
|
|
73
|
+
function stationWork(material, basket) {
|
|
73
74
|
const classifiedActions = LazySeq.of(material)
|
|
74
|
-
.collect((mat) => basketAction(mat, basketId))
|
|
75
|
+
.collect((mat) => basketAction(mat, basket.basketId))
|
|
75
76
|
.toRArray();
|
|
76
77
|
if (classifiedActions.some((action) => action.type === "invalid")) {
|
|
77
78
|
return { type: "inconsistent" };
|
|
78
79
|
}
|
|
79
80
|
const validActions = classifiedActions.filter((action) => action.type === "valid");
|
|
81
|
+
const descriptor = basket.loadStationWork;
|
|
82
|
+
if (descriptor !== undefined) {
|
|
83
|
+
if (!descriptor ||
|
|
84
|
+
!isNonBlank(descriptor.workId) ||
|
|
85
|
+
typeof descriptor.readyToConfirm !== "boolean")
|
|
86
|
+
return { type: "inconsistent" };
|
|
87
|
+
const awaiting = descriptor.awaitingMaterialSlots ?? [];
|
|
88
|
+
if (!Array.isArray(awaiting) ||
|
|
89
|
+
!awaiting.every(isPositiveInteger) ||
|
|
90
|
+
new Set(awaiting).size !== awaiting.length ||
|
|
91
|
+
(descriptor.readyToConfirm && awaiting.length > 0) ||
|
|
92
|
+
validActions.some((action) => action.workId !== descriptor.workId))
|
|
93
|
+
return { type: "inconsistent" };
|
|
94
|
+
const empty = descriptor.type === api.BasketLoadStationWorkType.ConfirmEmptyBasket;
|
|
95
|
+
if (empty) {
|
|
96
|
+
if (validActions.length > 0 ||
|
|
97
|
+
awaiting.length > 0 ||
|
|
98
|
+
material.some((mat) => mat.location.type === api.LocType.InBasket && mat.location.basketId === basket.basketId))
|
|
99
|
+
return { type: "inconsistent" };
|
|
100
|
+
}
|
|
101
|
+
else if (descriptor.type !== api.BasketLoadStationWorkType.Material ||
|
|
102
|
+
(descriptor.readyToConfirm && validActions.length === 0)) {
|
|
103
|
+
return { type: "inconsistent" };
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
type: "active",
|
|
107
|
+
work: {
|
|
108
|
+
workId: descriptor.workId,
|
|
109
|
+
ready: descriptor.readyToConfirm,
|
|
110
|
+
confirmEmpty: empty,
|
|
111
|
+
awaitingSlots: awaiting,
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
80
115
|
if (validActions.length === 0)
|
|
81
116
|
return { type: "none" };
|
|
82
117
|
const tagged = validActions.filter((action) => isNonBlank(action.workId));
|
|
@@ -85,8 +120,8 @@ function confirmableWork(material, basketId) {
|
|
|
85
120
|
return { type: "inconsistent" };
|
|
86
121
|
}
|
|
87
122
|
return {
|
|
88
|
-
type: "
|
|
89
|
-
work: { workId: tagged[0].workId },
|
|
123
|
+
type: "active",
|
|
124
|
+
work: { workId: tagged[0].workId, ready: true, confirmEmpty: false, awaitingSlots: [] },
|
|
90
125
|
};
|
|
91
126
|
}
|
|
92
127
|
function loadingInstruction(mat) {
|
|
@@ -105,21 +140,29 @@ function SlotMaterial({ material, fsize, }) {
|
|
|
105
140
|
}
|
|
106
141
|
export function BasketLoadStationWorkflow({ stationNumber, basket, material, fsize, submitCommand, }) {
|
|
107
142
|
const [submission, setSubmission] = useState();
|
|
108
|
-
const workState = useMemo(() =>
|
|
109
|
-
const
|
|
110
|
-
const
|
|
143
|
+
const workState = useMemo(() => stationWork(material, basket), [basket, material]);
|
|
144
|
+
const blockedReason = basket.loadStationWork?.confirmationBlockedReason?.trim();
|
|
145
|
+
const work = workState.type === "active" ? workState.work : undefined;
|
|
146
|
+
const submissionState = submission !== undefined &&
|
|
147
|
+
submission.workId === work?.workId &&
|
|
148
|
+
// A conflict rejects the submitted status snapshot, not the preserved occurrence.
|
|
149
|
+
// Compare the captured snapshot even when refresh precedes the response.
|
|
150
|
+
(submission.state !== "conflict" ||
|
|
151
|
+
(submission.basket === basket && submission.material === material))
|
|
152
|
+
? submission.state
|
|
153
|
+
: "idle";
|
|
111
154
|
const materialBySlot = useMemo(() => LazySeq.of(material)
|
|
112
155
|
.filter((mat) => mat.location.type === api.LocType.InBasket &&
|
|
113
156
|
mat.location.basketId === basket.basketId &&
|
|
114
157
|
mat.location.basketSlot !== undefined)
|
|
115
158
|
.groupBy((mat) => mat.location.basketSlot ?? 0)
|
|
116
|
-
.toHashMap((entry) => entry), [basket
|
|
159
|
+
.toHashMap((entry) => entry), [basket, material]);
|
|
117
160
|
const loadsBySlot = useMemo(() => LazySeq.of(material)
|
|
118
161
|
.filter((mat) => mat.action.type === api.ActionType.LoadingToBasket &&
|
|
119
162
|
mat.action.loadToBasketId === basket.basketId &&
|
|
120
163
|
mat.action.loadToBasketSlot !== undefined)
|
|
121
164
|
.groupBy((mat) => mat.action.loadToBasketSlot ?? 0)
|
|
122
|
-
.toHashMap((entry) => entry), [basket
|
|
165
|
+
.toHashMap((entry) => entry), [basket, material]);
|
|
123
166
|
const slots = useMemo(() => LazySeq.of(basket.emptySlots ?? [])
|
|
124
167
|
.concat(basket.unknownSlots ?? [])
|
|
125
168
|
.concat(materialBySlot.keysToLazySeq())
|
|
@@ -127,19 +170,21 @@ export function BasketLoadStationWorkflow({ stationNumber, basket, material, fsi
|
|
|
127
170
|
.distinctBy((slot) => slot)
|
|
128
171
|
.toSortedArray((slot) => slot), [basket.emptySlots, basket.unknownSlots, materialBySlot, loadsBySlot]);
|
|
129
172
|
async function submit() {
|
|
130
|
-
if (work === undefined || submitCommand === undefined)
|
|
173
|
+
if (work === undefined || !work.ready || submitCommand === undefined)
|
|
131
174
|
return;
|
|
132
175
|
const submittedWorkId = work.workId;
|
|
133
|
-
|
|
176
|
+
const pending = { workId: submittedWorkId, basket, material, state: "submitting" };
|
|
177
|
+
setSubmission(pending);
|
|
134
178
|
try {
|
|
135
179
|
const state = await submitCommand(stationNumber, { workId: submittedWorkId });
|
|
136
|
-
setSubmission((current) => current
|
|
180
|
+
setSubmission((current) => (current === pending ? { ...pending, state } : current));
|
|
137
181
|
}
|
|
138
182
|
catch {
|
|
139
|
-
setSubmission((current) => current
|
|
183
|
+
setSubmission((current) => (current === pending ? { ...pending, state: "error" } : current));
|
|
140
184
|
}
|
|
141
185
|
}
|
|
142
|
-
const submissionDisabled =
|
|
186
|
+
const submissionDisabled = !work?.ready ||
|
|
187
|
+
submissionState === "submitting" ||
|
|
143
188
|
submissionState === "accepted" ||
|
|
144
189
|
submissionState === "conflict";
|
|
145
190
|
return (_jsxs(Stack, { spacing: 2, sx: { mt: 2 }, children: [_jsx(Typography, { variant: "h6", children: "Basket slots" }), _jsx(Box, { sx: {
|
|
@@ -154,6 +199,9 @@ export function BasketLoadStationWorkflow({ stationNumber, basket, material, fsi
|
|
|
154
199
|
type: MoveMaterialNodeKindType.BasketSlotZone,
|
|
155
200
|
basketId: basket.basketId,
|
|
156
201
|
slot,
|
|
157
|
-
}, children: _jsxs(Box, { component: "section", "data-testid": `basket-load-station-slot-${slot}`, sx: { border: "1px solid", borderColor: "text.primary", p: 2 }, children: [_jsxs(Typography, { variant: "h6", children: ["Slot ", slot] }), slotMaterial.length > 0 ? (_jsx(SlotMaterial, { material: slotMaterial, fsize: fsize })) : (_jsx(Typography, { color: isUnknown ? "warning.main" : "text.secondary", children: isUnknown ? "Unknown" : "Empty" })), loads.map((mat, index) => (_jsxs(Box, { sx: { mt: 1 }, children: [_jsx(Typography, { children: loadingInstruction(mat) }), _jsxs(Typography, { variant: "body2", color: "text.secondary", children: [mat.partName, " \u00B7 Process ", mat.action.processAfterLoad ?? mat.process] })] }, `${mat.jobUnique}-${mat.process}-${index}`)))] }) }, slot));
|
|
158
|
-
}) }), work &&
|
|
202
|
+
}, children: _jsxs(Box, { component: "section", "data-testid": `basket-load-station-slot-${slot}`, sx: { border: "1px solid", borderColor: "text.primary", p: 2 }, children: [_jsxs(Typography, { variant: "h6", children: ["Slot ", basketSlotLabel(slot)] }), slotMaterial.length > 0 ? (_jsx(SlotMaterial, { material: slotMaterial, fsize: fsize })) : (_jsx(Typography, { color: isUnknown ? "warning.main" : "text.secondary", children: isUnknown ? "Unknown" : "Empty" })), loads.map((mat, index) => (_jsxs(Box, { sx: { mt: 1 }, children: [_jsx(Typography, { children: loadingInstruction(mat) }), _jsxs(Typography, { variant: "body2", color: "text.secondary", children: [mat.partName, " \u00B7 Process ", mat.action.processAfterLoad ?? mat.process] })] }, `${mat.jobUnique}-${mat.process}-${index}`)))] }) }, slot));
|
|
203
|
+
}) }), work?.confirmEmpty && _jsxs(Typography, { children: ["Confirm basket ", basket.basketId, " is empty."] }), work && !work.ready && (_jsx(Alert, { severity: "info", children: blockedReason ||
|
|
204
|
+
(work.awaitingSlots.length > 0
|
|
205
|
+
? `Waiting for material for slots ${work.awaitingSlots.map(basketSlotLabel).join(", ")}.`
|
|
206
|
+
: "Basket work is not ready for confirmation.") })), work && submitCommand ? (_jsx(Box, { sx: { display: "flex", justifyContent: "flex-end" }, children: _jsx(Button, { variant: "contained", disabled: submissionDisabled, onClick: () => void submit(), children: "Confirm" }) })) : work ? (_jsx(Alert, { severity: "warning", children: "Basket work confirmation is not configured." })) : workState.type === "inconsistent" ? (_jsx(Alert, { severity: "warning", children: "Basket work is inconsistent. Wait for refreshed material actions before confirming." })) : null, submissionState === "accepted" ? (_jsx(Alert, { severity: "success", children: "Confirmation accepted. Waiting for refreshed work." })) : submissionState === "conflict" ? (_jsx(Alert, { severity: "warning", children: "Basket work changed before confirmation. Review the refreshed slots and try again." })) : submissionState === "error" ? (_jsx(Alert, { severity: "error", children: "Unable to confirm basket work. No work was assumed complete." })) : null] }));
|
|
159
207
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ILogEntry } from "../../network/api.js";
|
|
2
2
|
import { HashMap } from "@seedtactics/immutable-collections";
|
|
3
|
-
import { ReactNode } from "react";
|
|
4
3
|
export type InvalidateCycleState = {
|
|
5
4
|
readonly process: number | null;
|
|
6
5
|
readonly changeRawMat: string | null;
|
|
@@ -19,18 +18,3 @@ export declare function InvalidateCycleDialogButton(props: InvalidateCycleProps
|
|
|
19
18
|
readonly ignoreOperator?: boolean;
|
|
20
19
|
readonly loadStation?: boolean;
|
|
21
20
|
}): import("react").JSX.Element | null;
|
|
22
|
-
interface SwapMaterial {
|
|
23
|
-
readonly selectedMatToSwap: Readonly<IInProcessMaterial> | null;
|
|
24
|
-
readonly updating: boolean;
|
|
25
|
-
}
|
|
26
|
-
export type SwapMaterialState = SwapMaterial | null;
|
|
27
|
-
export interface SwapMaterialProps {
|
|
28
|
-
readonly st: SwapMaterialState;
|
|
29
|
-
readonly setState: (s: SwapMaterialState) => void;
|
|
30
|
-
}
|
|
31
|
-
export declare function SwapMaterialDialogContent(props: SwapMaterialProps): ReactNode;
|
|
32
|
-
export declare function SwapMaterialButtons(props: SwapMaterialProps & {
|
|
33
|
-
readonly onClose: () => void;
|
|
34
|
-
readonly ignoreOperator?: boolean;
|
|
35
|
-
}): import("react").JSX.Element | null;
|
|
36
|
-
export {};
|
|
@@ -47,14 +47,12 @@ import { last30Jobs } from "../../cell-status/scheduled-jobs.js";
|
|
|
47
47
|
import { PartIdenticon } from "./Material.js";
|
|
48
48
|
import { isLogEntryInvalidated } from "../LogEntry.js";
|
|
49
49
|
import { ApiException } from "../../network/api.js";
|
|
50
|
-
import { canInvalidateMaterial
|
|
50
|
+
import { canInvalidateMaterial } from "../../data/material-operation-policy.js";
|
|
51
51
|
const invalidatableEventTypes = LazySeq.of([
|
|
52
52
|
LogType.AddToQueue,
|
|
53
53
|
LogType.RemoveFromQueue,
|
|
54
54
|
LogType.LoadUnloadCycle,
|
|
55
55
|
LogType.MachineCycle,
|
|
56
|
-
LogType.BasketLoadUnload,
|
|
57
|
-
LogType.BasketCycle,
|
|
58
56
|
]).toRSet((eventType) => eventType);
|
|
59
57
|
export function affectedMaterialForInvalidation(events, materialId, process) {
|
|
60
58
|
let affected = HashMap.empty();
|
|
@@ -245,91 +243,3 @@ export function InvalidateCycleDialogButton(props) {
|
|
|
245
243
|
: "Invalidate Process " + props.st.process.toString()
|
|
246
244
|
: "Invalidate Cycle" })) : undefined] }));
|
|
247
245
|
}
|
|
248
|
-
function isNullOrEmpty(s) {
|
|
249
|
-
return s === undefined || s === null || s == "";
|
|
250
|
-
}
|
|
251
|
-
function matCanSwap(curMat, job) {
|
|
252
|
-
return (newMat) => {
|
|
253
|
-
if (isNullOrEmpty(newMat.serial))
|
|
254
|
-
return false;
|
|
255
|
-
if (newMat.location.type === LocType.OnPallet)
|
|
256
|
-
return false;
|
|
257
|
-
if (newMat.process !== curMat.process - 1)
|
|
258
|
-
return false;
|
|
259
|
-
if (isNullOrEmpty(newMat.jobUnique)) {
|
|
260
|
-
// if part name is wrong, check casting
|
|
261
|
-
if (isNullOrEmpty(newMat.partName))
|
|
262
|
-
return false;
|
|
263
|
-
if (newMat.partName !== curMat.partName) {
|
|
264
|
-
if (!job)
|
|
265
|
-
return false;
|
|
266
|
-
if (!LazySeq.of(job.procsAndPaths)
|
|
267
|
-
.flatMap((p) => p.paths)
|
|
268
|
-
.some((p) => p.casting === newMat.partName)) {
|
|
269
|
-
return false;
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
else {
|
|
274
|
-
// check path
|
|
275
|
-
if (newMat.jobUnique !== curMat.jobUnique)
|
|
276
|
-
return false;
|
|
277
|
-
if (newMat.path !== curMat.path)
|
|
278
|
-
return false;
|
|
279
|
-
}
|
|
280
|
-
return true;
|
|
281
|
-
};
|
|
282
|
-
}
|
|
283
|
-
export function SwapMaterialDialogContent(props) {
|
|
284
|
-
const status = useAtomValue(currentStatus);
|
|
285
|
-
const curMat = useAtomValue(inProcessMaterialInDialog);
|
|
286
|
-
if (curMat === null || props.st === null)
|
|
287
|
-
return _jsx("div", {});
|
|
288
|
-
const curMatJob = status.jobs[curMat.jobUnique];
|
|
289
|
-
const availMats = status.material.filter(matCanSwap(curMat, curMatJob));
|
|
290
|
-
if (availMats.length === 0) {
|
|
291
|
-
return (_jsx("p", { style: { margin: "2em" }, children: "No material with the same job is available for swapping. You must edit the pallet using the cell controller software to remove the material from the pallet. Insight will automatically refresh once the cell controller software is updated." }));
|
|
292
|
-
}
|
|
293
|
-
else {
|
|
294
|
-
return (_jsxs("div", { style: { margin: "2em" }, children: [_jsx("p", { children: "Swap serial on pallet with material from the same job." }), _jsx("p", { children: "If material on the pallet is from a different job, you cannot use this screen. Instead, the material must first be removed from the pallet using the cell controller software. Insight will automatically refresh when this occurs." }), _jsx(TextField, { value: props.st?.selectedMatToSwap?.serial ?? "", select: true, onChange: (e) => props.st &&
|
|
295
|
-
props.setState({
|
|
296
|
-
...props.st,
|
|
297
|
-
selectedMatToSwap: availMats.find((m) => m.serial === e.target.value) ?? null,
|
|
298
|
-
}), style: { width: "20em" }, variant: "outlined", label: "Select serial to swap with " + (curMat.serial ?? ""), children: availMats.map((m) => (_jsx(MenuItem, { value: m.serial, children: m.serial }, m.materialID))) })] }));
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
export function SwapMaterialButtons(props) {
|
|
302
|
-
const fmsInfo = useAtomValue(fmsInformation);
|
|
303
|
-
const curMat = useAtomValue(inProcessMaterialInDialog);
|
|
304
|
-
const closeMatDialog = useSetAtom(materialDialogOpen);
|
|
305
|
-
let operator = useAtomValue(currentOperator);
|
|
306
|
-
if (!fmsInfo.allowSwapSerialAtLoadStation)
|
|
307
|
-
return null;
|
|
308
|
-
if (props.ignoreOperator)
|
|
309
|
-
operator = null;
|
|
310
|
-
if (!curMat ||
|
|
311
|
-
curMat.location.type !== LocType.OnPallet ||
|
|
312
|
-
isActiveLoadStationOperation(curMat)) {
|
|
313
|
-
return null;
|
|
314
|
-
}
|
|
315
|
-
function swapMats() {
|
|
316
|
-
if (curMat &&
|
|
317
|
-
props.st &&
|
|
318
|
-
props.st.selectedMatToSwap &&
|
|
319
|
-
curMat.location.type === LocType.OnPallet) {
|
|
320
|
-
props.setState({ selectedMatToSwap: props.st.selectedMatToSwap, updating: true });
|
|
321
|
-
JobsBackend.swapMaterialOnPallet(curMat.materialID, operator, {
|
|
322
|
-
pallet: curMat.location.palletNum ?? 0,
|
|
323
|
-
materialIDToSetOnPallet: props.st.selectedMatToSwap.materialID,
|
|
324
|
-
})
|
|
325
|
-
.catch(console.log)
|
|
326
|
-
.finally(() => {
|
|
327
|
-
closeMatDialog(null);
|
|
328
|
-
props.onClose();
|
|
329
|
-
});
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
return (_jsxs(_Fragment, { children: [props.st === null ? (_jsx(Button, { color: "primary", onClick: () => props.setState({ selectedMatToSwap: null, updating: false }), children: "Swap Serial" })) : undefined, props.st !== null ? (_jsx(Button, { color: "primary", onClick: swapMats, disabled: props.st.selectedMatToSwap === null || props.st.updating, children: props.st.selectedMatToSwap === null
|
|
333
|
-
? "Swap Serial"
|
|
334
|
-
: "Swap with " + (props.st.selectedMatToSwap.serial ?? "") })) : undefined] }));
|
|
335
|
-
}
|
|
@@ -54,7 +54,7 @@ import { QuarantineMatButton } from "./QuarantineButton.js";
|
|
|
54
54
|
import { CancelLoadButton } from "./CancelLoadButton.js";
|
|
55
55
|
import { durationToSeconds } from "../../util/parseISODuration.js";
|
|
56
56
|
import { formatSeconds } from "./SystemOverview.js";
|
|
57
|
-
import { InvalidateCycleDialogButton, InvalidateCycleDialogContent,
|
|
57
|
+
import { InvalidateCycleDialogButton, InvalidateCycleDialogContent, } from "./InvalidateCycle.js";
|
|
58
58
|
import { last30MaterialSummary } from "../../cell-status/material-summary.js";
|
|
59
59
|
import { addHours } from "date-fns";
|
|
60
60
|
import { PromptForQueue } from "./QueuesAddMaterial.js";
|
|
@@ -625,18 +625,16 @@ function AssignWorkorderButton() {
|
|
|
625
625
|
return (_jsx(Button, { color: "primary", onClick: () => setWorkorderDialogOpen(true), children: "Assign Workorder" }));
|
|
626
626
|
}
|
|
627
627
|
const LoadMatDialog = memo(function LoadMatDialog(props) {
|
|
628
|
-
const [swapSt, setSwapSt] = useState(null);
|
|
629
628
|
const [invalidateSt, setInvalidateSt] = useState(null);
|
|
630
629
|
// add material state
|
|
631
630
|
const [showAddMaterial, setShowAddMaterial] = useState(false);
|
|
632
631
|
const [selectedQueue, setSelectedQueue] = useState(null);
|
|
633
632
|
const onClose = useCallback(function onClose() {
|
|
634
|
-
setSwapSt(null);
|
|
635
633
|
setInvalidateSt(null);
|
|
636
634
|
setShowAddMaterial(false);
|
|
637
635
|
setSelectedQueue(null);
|
|
638
|
-
}, [
|
|
639
|
-
return (_jsx(MaterialDialog, { onClose: onClose, allowNote: true, highlightProcsGreaterOrEqualTo: invalidateSt?.process ?? undefined, extraDialogElements: _jsxs(_Fragment, { children: [
|
|
636
|
+
}, [setInvalidateSt]);
|
|
637
|
+
return (_jsx(MaterialDialog, { onClose: onClose, allowNote: true, highlightProcsGreaterOrEqualTo: invalidateSt?.process ?? undefined, extraDialogElements: _jsxs(_Fragment, { children: [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(InvalidateCycleDialogButton, { st: invalidateSt, setState: setInvalidateSt, onClose: onClose, loadStation: true }), _jsx(AssignWorkorderButton, {})] }) }));
|
|
640
638
|
});
|
|
641
639
|
function useGridLayout({ numMatCols, maxNumFaces, horizontal, showMatInCompleted, }) {
|
|
642
640
|
let rows = "";
|
|
@@ -48,7 +48,7 @@ import { currentStatus } from "../../cell-status/current-status.js";
|
|
|
48
48
|
import { useAtom, useAtomValue, useSetAtom } from "jotai";
|
|
49
49
|
import { last30Rebookings } from "../../cell-status/rebookings.js";
|
|
50
50
|
import { fmsInformation } from "../../network/server-settings.js";
|
|
51
|
-
import { basketDisplayName } from "../../cell-status/station-cycles.js";
|
|
51
|
+
import { basketDisplayName, basketSlotLabel } from "../../cell-status/station-cycles.js";
|
|
52
52
|
import { materialOperationState } from "../../data/material-operation-policy.js";
|
|
53
53
|
export class PartIdenticon extends PureComponent {
|
|
54
54
|
render() {
|
|
@@ -236,12 +236,14 @@ export function MaterialAction({ mat, displayActionForSinglePallet, fsize, }) {
|
|
|
236
236
|
}
|
|
237
237
|
}
|
|
238
238
|
case api.ActionType.LoadingToBasket:
|
|
239
|
-
return (_jsxs(MatCardDetail, { fsize: fsize, children: ["Load into ", basketName, " ", mat.action.loadToBasketId ?? "", mat.action.loadToBasketSlot !== undefined
|
|
239
|
+
return (_jsxs(MatCardDetail, { fsize: fsize, children: ["Load into ", basketName, " ", mat.action.loadToBasketId ?? "", mat.action.loadToBasketSlot !== undefined
|
|
240
|
+
? ` slot ${basketSlotLabel(mat.action.loadToBasketSlot)}`
|
|
241
|
+
: ""] }));
|
|
240
242
|
case api.ActionType.UnloadToInProcess:
|
|
241
243
|
case api.ActionType.UnloadToCompletedMaterial:
|
|
242
244
|
if (mat.action.unloadToBasketId) {
|
|
243
245
|
return (_jsxs(MatCardDetail, { fsize: fsize, children: ["Unload to ", basketName, " ", mat.action.unloadToBasketId, mat.action.unloadToBasketSlot !== undefined
|
|
244
|
-
? ` slot ${mat.action.unloadToBasketSlot}`
|
|
246
|
+
? ` slot ${basketSlotLabel(mat.action.unloadToBasketSlot)}`
|
|
245
247
|
: ""] }));
|
|
246
248
|
}
|
|
247
249
|
else if (mat.action.unloadIntoQueue) {
|
|
@@ -42,7 +42,7 @@ import { materialDialogOpen } from "../../cell-status/material-details.js";
|
|
|
42
42
|
import { last30Jobs } from "../../cell-status/scheduled-jobs.js";
|
|
43
43
|
import { addDays } from "date-fns";
|
|
44
44
|
import { durationToSeconds } from "../../util/parseISODuration.js";
|
|
45
|
-
import { InvalidateCycleDialogButton, InvalidateCycleDialogContent,
|
|
45
|
+
import { InvalidateCycleDialogButton, InvalidateCycleDialogContent, } from "./InvalidateCycle.js";
|
|
46
46
|
import { CancelLoadButton } from "./CancelLoadButton.js";
|
|
47
47
|
import { QuarantineMatButton } from "./QuarantineButton.js";
|
|
48
48
|
import { SelectInspTypeDialog, SignalInspectionButton } from "./SelectInspType.js";
|
|
@@ -824,13 +824,11 @@ export const SystemOverview = memo(function SystemOverview({ overview, }) {
|
|
|
824
824
|
}, children: [overview.stockerPals.map((pal) => (_jsx(StockerPallet, { pallet: pal, maxNumFaces: overview.maxNumFacesOnPallet }, pal.pallet.palletNum))), overview.floatingBaskets.map((basket) => (_jsx(FloatingBasket, { basket: basket }, basket.basket.basketId))), overview.storageBaskets ? (_jsx(BasketStorageSummary, { empty: overview.storageBaskets.empty, filled: overview.storageBaskets.filled })) : undefined] })) : undefined] }));
|
|
825
825
|
});
|
|
826
826
|
const SystemOverviewMaterialDialog = memo(function SystemOverviewMaterialDialog({ ignoreOperator, }) {
|
|
827
|
-
const [swapSt, setSwapSt] = useState(null);
|
|
828
827
|
const [invalidateSt, setInvalidateSt] = useState(null);
|
|
829
828
|
function onClose() {
|
|
830
|
-
setSwapSt(null);
|
|
831
829
|
setInvalidateSt(null);
|
|
832
830
|
}
|
|
833
|
-
return (_jsx(MaterialDialog, { onClose: onClose, allowNote: true, highlightProcsGreaterOrEqualTo: invalidateSt?.process ?? undefined, extraDialogElements:
|
|
831
|
+
return (_jsx(MaterialDialog, { onClose: onClose, allowNote: true, highlightProcsGreaterOrEqualTo: invalidateSt?.process ?? undefined, extraDialogElements: _jsx(_Fragment, { children: 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(InvalidateCycleDialogButton, { st: invalidateSt, setState: setInvalidateSt, onClose: onClose, ignoreOperator: ignoreOperator })] }) }));
|
|
834
832
|
});
|
|
835
833
|
export function SystemOverviewPage({ ignoreOperator, whiteBackground, }) {
|
|
836
834
|
useSetTitle("System Overview");
|
package/dist/index.html
CHANGED
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
</style>
|
|
46
|
-
<script type="module" crossorigin src="/assets/index-
|
|
46
|
+
<script type="module" crossorigin src="/assets/index-MFZreX-b.js"></script>
|
|
47
47
|
<link rel="stylesheet" crossorigin href="/assets/index-DXfAwuj-.css">
|
|
48
48
|
</head>
|
|
49
49
|
<body>
|