@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.
- package/README.md +21 -0
- package/dist/assets/index-CQhVwNX7.js +388 -0
- package/dist/cell-status/material-details.d.ts +15 -5
- package/dist/cell-status/material-details.js +58 -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/operations/Programs.js +1 -1
- package/dist/components/station-monitor/BasketLoadStationWork.js +44 -36
- 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 +59 -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 +61 -10
- package/dist/components/station-monitor/LoadStation.js +23 -11
- package/dist/components/station-monitor/Material.js +2 -4
- package/dist/components/station-monitor/QuarantineButton.js +80 -60
- package/dist/components/station-monitor/Queues.js +2 -1
- package/dist/components/station-monitor/QueuesAddMaterial.js +59 -48
- package/dist/components/station-monitor/SystemOverview.js +3 -2
- package/dist/components/station-monitor/Whiteboard.js +2 -2
- 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 +18 -4
- package/dist/network/api.js +69 -2
- package/dist/network/backend-mock.js +3 -0
- package/dist/network/backend.d.ts +1 -0
- package/dist/network/backend.js +2 -1
- package/package.json +12 -11
- package/dist/assets/index-Y5yNHUvc.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
|
@@ -49,6 +49,7 @@ export declare const serialInMaterialDialog: Atom<Promise<string | null>>;
|
|
|
49
49
|
export declare const serialInMaterialDialogUnwrapped: Atom<string | null>;
|
|
50
50
|
export declare const serialInMaterialDialogState: Atom<AsyncAtomState<string | null>>;
|
|
51
51
|
export declare const workorderInMaterialDialog: Atom<Promise<string | null>>;
|
|
52
|
+
export declare const materialInDialogLocalEvents: Atom<readonly Readonly<ILogEntry>[]>;
|
|
52
53
|
export declare const materialInDialogEvents: Atom<readonly Readonly<ILogEntry>[]>;
|
|
53
54
|
export type LargestUsedProces = {
|
|
54
55
|
readonly process: number;
|
|
@@ -96,9 +97,12 @@ export interface PrintLabelData {
|
|
|
96
97
|
readonly proc: number;
|
|
97
98
|
}
|
|
98
99
|
export declare function usePrintLabel(): [(data: PrintLabelData) => void, boolean, boolean];
|
|
99
|
-
export declare function useRemoveFromQueue(): [
|
|
100
|
+
export declare function useRemoveFromQueue(): [
|
|
101
|
+
(matId: number, operator: string | null) => Promise<void>,
|
|
102
|
+
boolean
|
|
103
|
+
];
|
|
100
104
|
export declare function useSignalForQuarantine(): [
|
|
101
|
-
(matId: number, operator: string | null, reason: string) => void
|
|
105
|
+
(matId: number, operator: string | null, reason: string) => Promise<void>,
|
|
102
106
|
boolean
|
|
103
107
|
];
|
|
104
108
|
export interface AddExistingMaterialToQueueData {
|
|
@@ -108,7 +112,7 @@ export interface AddExistingMaterialToQueueData {
|
|
|
108
112
|
readonly operator: string | null;
|
|
109
113
|
}
|
|
110
114
|
export declare function useAddExistingMaterialToQueue(): [
|
|
111
|
-
(d: AddExistingMaterialToQueueData) => void
|
|
115
|
+
(d: AddExistingMaterialToQueueData) => Promise<void>,
|
|
112
116
|
boolean
|
|
113
117
|
];
|
|
114
118
|
export interface AddNewMaterialToQueueData {
|
|
@@ -122,7 +126,10 @@ export interface AddNewMaterialToQueueData {
|
|
|
122
126
|
readonly onNewMaterial?: (mat: Readonly<IInProcessMaterial>) => void;
|
|
123
127
|
readonly onError?: (reason: unknown) => void;
|
|
124
128
|
}
|
|
125
|
-
export declare function useAddNewMaterialToQueue(): [
|
|
129
|
+
export declare function useAddNewMaterialToQueue(): [
|
|
130
|
+
(d: AddNewMaterialToQueueData) => Promise<Readonly<IInProcessMaterial>>,
|
|
131
|
+
boolean
|
|
132
|
+
];
|
|
126
133
|
export interface AddNewCastingToQueueData {
|
|
127
134
|
readonly casting: string;
|
|
128
135
|
readonly quantity: number;
|
|
@@ -133,4 +140,7 @@ export interface AddNewCastingToQueueData {
|
|
|
133
140
|
readonly onNewMaterial?: (mats: ReadonlyArray<Readonly<IInProcessMaterial>>) => void;
|
|
134
141
|
readonly onError?: (reason: unknown) => void;
|
|
135
142
|
}
|
|
136
|
-
export declare function useAddNewCastingToQueue(): [
|
|
143
|
+
export declare function useAddNewCastingToQueue(): [
|
|
144
|
+
(d: AddNewCastingToQueueData) => Promise<ReadonlyArray<Readonly<IInProcessMaterial>>>,
|
|
145
|
+
boolean
|
|
146
|
+
];
|
|
@@ -121,8 +121,9 @@ export const inProcessMaterialInDialog = atom(async (get) => {
|
|
|
121
121
|
const toShow = get(matToShow);
|
|
122
122
|
if (toShow === null)
|
|
123
123
|
return null;
|
|
124
|
-
if (toShow.type === "InProcMat")
|
|
125
|
-
return toShow.inproc;
|
|
124
|
+
if (toShow.type === "InProcMat") {
|
|
125
|
+
return status.material.find((m) => m.materialID === toShow.inproc.materialID) ?? null;
|
|
126
|
+
}
|
|
126
127
|
const matId = (await get(materialInDialogInfo))?.materialID ?? null;
|
|
127
128
|
return matId !== null && matId >= 0
|
|
128
129
|
? (status.material.find((m) => m.materialID === matId) ?? null)
|
|
@@ -210,6 +211,9 @@ const otherMatEvents = atom(async (get, { signal }) => {
|
|
|
210
211
|
return evts;
|
|
211
212
|
});
|
|
212
213
|
const otherMatEventsUnwrapped = unwrap(otherMatEvents, (prev) => prev ?? []);
|
|
214
|
+
export const materialInDialogLocalEvents = atom((get) => {
|
|
215
|
+
return LazySeq.of(get(localMatEventsUnwrapped)).concat(get(extraLogEventsFromUpdates)).toRArray();
|
|
216
|
+
});
|
|
213
217
|
export const materialInDialogEvents = atom((get) => {
|
|
214
218
|
const evtsFromUpdate = get(extraLogEventsFromUpdates);
|
|
215
219
|
return LazySeq.of(get(localMatEventsUnwrapped))
|
|
@@ -373,66 +377,83 @@ export function usePrintLabel() {
|
|
|
373
377
|
}
|
|
374
378
|
export function useRemoveFromQueue() {
|
|
375
379
|
const [updating, setUpdating] = useState(false);
|
|
376
|
-
const callback = useCallback((matId, operator) => {
|
|
380
|
+
const callback = useCallback(async (matId, operator) => {
|
|
377
381
|
setUpdating(true);
|
|
378
|
-
|
|
379
|
-
.
|
|
380
|
-
|
|
382
|
+
try {
|
|
383
|
+
await JobsBackend.removeMaterialFromAllQueues(matId, operator ?? undefined);
|
|
384
|
+
}
|
|
385
|
+
finally {
|
|
386
|
+
setUpdating(false);
|
|
387
|
+
}
|
|
381
388
|
}, []);
|
|
382
389
|
return [callback, updating];
|
|
383
390
|
}
|
|
384
391
|
export function useSignalForQuarantine() {
|
|
385
392
|
const [updating, setUpdating] = useState(false);
|
|
386
|
-
const callback = useCallback((matId, operator, reason) => {
|
|
393
|
+
const callback = useCallback(async (matId, operator, reason) => {
|
|
387
394
|
setUpdating(true);
|
|
388
|
-
|
|
389
|
-
.
|
|
390
|
-
|
|
395
|
+
try {
|
|
396
|
+
await JobsBackend.signalMaterialForQuarantine(matId, operator, reason === "" ? undefined : reason);
|
|
397
|
+
}
|
|
398
|
+
finally {
|
|
399
|
+
setUpdating(false);
|
|
400
|
+
}
|
|
391
401
|
}, []);
|
|
392
402
|
return [callback, updating];
|
|
393
403
|
}
|
|
394
404
|
export function useAddExistingMaterialToQueue() {
|
|
395
405
|
const [updating, setUpdating] = useState(false);
|
|
396
|
-
const callback = useCallback((d) => {
|
|
406
|
+
const callback = useCallback(async (d) => {
|
|
397
407
|
setUpdating(true);
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
408
|
+
try {
|
|
409
|
+
await JobsBackend.setMaterialInQueue(d.materialId, d.operator, new QueuePosition({
|
|
410
|
+
queue: d.queue,
|
|
411
|
+
position: d.queuePosition,
|
|
412
|
+
}));
|
|
413
|
+
}
|
|
414
|
+
finally {
|
|
415
|
+
setUpdating(false);
|
|
416
|
+
}
|
|
404
417
|
}, []);
|
|
405
418
|
return [callback, updating];
|
|
406
419
|
}
|
|
407
420
|
export function useAddNewMaterialToQueue() {
|
|
408
421
|
const [updating, setUpdating] = useState(false);
|
|
409
|
-
const callback = useCallback((d) => {
|
|
422
|
+
const callback = useCallback(async (d) => {
|
|
410
423
|
setUpdating(true);
|
|
411
|
-
|
|
412
|
-
.
|
|
413
|
-
if (
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
424
|
+
try {
|
|
425
|
+
const material = await JobsBackend.addUnprocessedMaterialToQueue(d.jobUnique, d.lastCompletedProcess, d.queue, d.queuePosition, d.operator, d.workorder, d.serial || "");
|
|
426
|
+
if (material === undefined)
|
|
427
|
+
throw new Error("No material returned");
|
|
428
|
+
d.onNewMaterial?.(material);
|
|
429
|
+
return material;
|
|
430
|
+
}
|
|
431
|
+
catch (e) {
|
|
432
|
+
d.onError?.(e);
|
|
433
|
+
throw e;
|
|
434
|
+
}
|
|
435
|
+
finally {
|
|
436
|
+
setUpdating(false);
|
|
437
|
+
}
|
|
422
438
|
}, []);
|
|
423
439
|
return [callback, updating];
|
|
424
440
|
}
|
|
425
441
|
export function useAddNewCastingToQueue() {
|
|
426
442
|
const [updating, setUpdating] = useState(false);
|
|
427
|
-
const callback = useCallback((d) => {
|
|
443
|
+
const callback = useCallback(async (d) => {
|
|
428
444
|
setUpdating(true);
|
|
429
|
-
|
|
430
|
-
.
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
.
|
|
445
|
+
try {
|
|
446
|
+
const material = await JobsBackend.addUnallocatedCastingToQueue(d.casting, d.queue, d.quantity, d.operator, d.workorder, [...(d.serials || [])]);
|
|
447
|
+
d.onNewMaterial?.(material);
|
|
448
|
+
return material;
|
|
449
|
+
}
|
|
450
|
+
catch (e) {
|
|
451
|
+
d.onError?.(e);
|
|
452
|
+
throw e;
|
|
453
|
+
}
|
|
454
|
+
finally {
|
|
455
|
+
setUpdating(false);
|
|
456
|
+
}
|
|
436
457
|
}, []);
|
|
437
458
|
return [callback, updating];
|
|
438
459
|
}
|
|
@@ -48,7 +48,7 @@ import { useAtomValue } from "jotai";
|
|
|
48
48
|
import { fmsInformation } from "../network/server-settings.js";
|
|
49
49
|
import { basketDisplayName, loadStationDisplayName } from "../cell-status/station-cycles.js";
|
|
50
50
|
import { filterRemoveAddQueue } from "./log-entry-queue-filter.js";
|
|
51
|
-
import { basketContainerName } from "./log-entry-container-name.js";
|
|
51
|
+
import { basketContainerName, basketCycleDescription } from "./log-entry-container-name.js";
|
|
52
52
|
const ColoredSpan = styled("span", { shouldForwardProp: (prop) => prop.toString()[0] !== "$" })(({ $type }) => {
|
|
53
53
|
switch ($type) {
|
|
54
54
|
case "machine":
|
|
@@ -191,14 +191,7 @@ function display(props, fmsInfo) {
|
|
|
191
191
|
}
|
|
192
192
|
case api.LogType.BasketCycle: {
|
|
193
193
|
const basketName = basketDisplayName(fmsInfo.basketName);
|
|
194
|
-
|
|
195
|
-
return (_jsxs("span", { children: [basketName, " ", entry.pal, " started cycle"] }));
|
|
196
|
-
}
|
|
197
|
-
else {
|
|
198
|
-
return (_jsxs("span", { children: [basketName, " ", entry.pal, " completed cycle", entry.containerIds && entry.containerIds.length > 0
|
|
199
|
-
? ` from ${entry.containerIds.length} UUID fragment${entry.containerIds.length === 1 ? "" : "s"}`
|
|
200
|
-
: ""] }));
|
|
201
|
-
}
|
|
194
|
+
return _jsx("span", { children: basketCycleDescription(entry, basketName) });
|
|
202
195
|
}
|
|
203
196
|
case api.LogType.BasketInLocation: {
|
|
204
197
|
const basketName = basketDisplayName(fmsInfo.basketName);
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import * as api from "../network/api.js";
|
|
2
2
|
type ContainerNameEntry = Readonly<Pick<api.ILogEntry, "pal" | "containerId">>;
|
|
3
|
+
type BasketCycleEntry = ContainerNameEntry & Readonly<Pick<api.ILogEntry, "startofcycle" | "containerIds">>;
|
|
3
4
|
export declare function basketContainerName(entry: ContainerNameEntry, basketName: string): string;
|
|
5
|
+
export declare function basketCycleDescription(entry: BasketCycleEntry, basketName: string): string;
|
|
4
6
|
export {};
|
|
@@ -5,3 +5,13 @@ export function basketContainerName(entry, basketName) {
|
|
|
5
5
|
return `${basketName} ${entry.pal}`;
|
|
6
6
|
return basketName;
|
|
7
7
|
}
|
|
8
|
+
export function basketCycleDescription(entry, basketName) {
|
|
9
|
+
const container = basketContainerName(entry, basketName);
|
|
10
|
+
if (entry.startofcycle)
|
|
11
|
+
return `${container} started cycle`;
|
|
12
|
+
const fragmentCount = entry.containerIds?.length ?? 0;
|
|
13
|
+
const fragments = fragmentCount > 0
|
|
14
|
+
? ` from ${fragmentCount} UUID fragment${fragmentCount === 1 ? "" : "s"}`
|
|
15
|
+
: "";
|
|
16
|
+
return `${container} completed cycle${fragments}`;
|
|
17
|
+
}
|
|
@@ -43,6 +43,7 @@ import { InvalidateCycleDialogButton, InvalidateCycleDialogContent, SwapMaterial
|
|
|
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";
|
|
46
|
+
import { CancelLoadButton } from "../station-monitor/CancelLoadButton.js";
|
|
46
47
|
import { useSetTitle } from "../routes.js";
|
|
47
48
|
import { useAtom, useAtomValue, useSetAtom } from "jotai";
|
|
48
49
|
import { fmsInformation } from "../../network/server-settings.js";
|
|
@@ -152,7 +153,7 @@ const AllMatDialog = memo(function AllMatDialog() {
|
|
|
152
153
|
setSwapSt(null);
|
|
153
154
|
setInvalidateSt(null);
|
|
154
155
|
}
|
|
155
|
-
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, { onClose: onClose, ignoreOperator: true }), _jsx(SwapMaterialButtons, { st: swapSt, setState: setSwapSt, onClose: onClose, ignoreOperator: true }), _jsx(InvalidateCycleDialogButton, { st: invalidateSt, setState: setInvalidateSt, onClose: onClose, ignoreOperator: true })] }) }));
|
|
156
|
+
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, { onClose: onClose, ignoreOperator: true }), _jsx(CancelLoadButton, { onClose: onClose, ignoreOperator: true }), _jsx(SwapMaterialButtons, { st: swapSt, setState: setSwapSt, onClose: onClose, ignoreOperator: true }), _jsx(InvalidateCycleDialogButton, { st: invalidateSt, setState: setInvalidateSt, onClose: onClose, ignoreOperator: true })] }) }));
|
|
156
157
|
});
|
|
157
158
|
function useCollisionDetection(allBins) {
|
|
158
159
|
return useCallback((args) => {
|
|
@@ -260,12 +261,12 @@ export function AllMaterial(props) {
|
|
|
260
261
|
? findQueueInQuarantineQueues(over.id, allBins)
|
|
261
262
|
: findMaterialInQuarantineQueues(over.id, allBins);
|
|
262
263
|
if (overCol) {
|
|
263
|
-
addExistingMatToQueue({
|
|
264
|
+
void addExistingMatToQueue({
|
|
264
265
|
materialId: activeDrag.mat.materialID,
|
|
265
266
|
queue: overCol.bin.queueName,
|
|
266
267
|
queuePosition: overCol.idx,
|
|
267
268
|
operator: null,
|
|
268
|
-
});
|
|
269
|
+
}).catch(console.error);
|
|
269
270
|
reorderQueuedMat({
|
|
270
271
|
queue: overCol.bin.queueName,
|
|
271
272
|
matId: activeDrag.mat.materialID,
|
|
@@ -249,7 +249,7 @@ export function ProgramSummaryTable() {
|
|
|
249
249
|
function ProgramContentCode() {
|
|
250
250
|
const ct = useAtomValue(programContent);
|
|
251
251
|
const [highlighted, setHighlighted] = useState(null);
|
|
252
|
-
const worker = useMemo(() => new Worker(new URL("./ProgramHighlight.
|
|
252
|
+
const worker = useMemo(() => new Worker(new URL("./ProgramHighlight.js", import.meta.url), { type: "module" }), []);
|
|
253
253
|
useEffect(() => {
|
|
254
254
|
let set = (h) => setHighlighted(h);
|
|
255
255
|
const handleMessage = (event) => set(event.data);
|
|
@@ -28,81 +28,86 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
28
28
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
29
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
30
30
|
*/
|
|
31
|
-
import {
|
|
31
|
+
import { useMemo, useState } from "react";
|
|
32
32
|
import { Alert, Box, Button, Card, CardContent, Stack, Typography } from "@mui/material";
|
|
33
33
|
import { LazySeq } from "@seedtactics/immutable-collections";
|
|
34
34
|
import * as api from "../../network/api.js";
|
|
35
35
|
import { InProcMaterial } from "./Material.js";
|
|
36
36
|
import { MoveMaterialArrowNode } from "./MoveMaterialArrows.js";
|
|
37
37
|
import { MoveMaterialNodeKindType } from "../../data/move-arrows.js";
|
|
38
|
-
function
|
|
39
|
-
return Number.isInteger(value) && (value ??
|
|
38
|
+
function isPositiveInteger(value) {
|
|
39
|
+
return Number.isInteger(value) && (value ?? 0) > 0;
|
|
40
40
|
}
|
|
41
41
|
function isNonBlank(value) {
|
|
42
42
|
return typeof value === "string" && value.trim() !== "";
|
|
43
43
|
}
|
|
44
|
-
function
|
|
44
|
+
function basketAction(mat, basketId) {
|
|
45
45
|
if (mat.location.type === api.LocType.InBasket &&
|
|
46
46
|
mat.location.basketId === basketId &&
|
|
47
47
|
(mat.action.type === api.ActionType.UnloadToInProcess ||
|
|
48
48
|
mat.action.type === api.ActionType.UnloadToCompletedMaterial)) {
|
|
49
|
-
if (!
|
|
50
|
-
return "invalid";
|
|
49
|
+
if (!isPositiveInteger(mat.location.basketSlot)) {
|
|
50
|
+
return { type: "invalid" };
|
|
51
51
|
}
|
|
52
|
-
return "
|
|
52
|
+
return { type: "valid", workId: mat.action.workId };
|
|
53
53
|
}
|
|
54
54
|
if (mat.action.type === api.ActionType.LoadingToBasket) {
|
|
55
|
-
if (!
|
|
56
|
-
return "invalid";
|
|
55
|
+
if (!isPositiveInteger(mat.action.loadToBasketId))
|
|
56
|
+
return { type: "invalid" };
|
|
57
57
|
if (mat.action.loadToBasketId !== basketId)
|
|
58
58
|
return undefined;
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
const reloadsSameSlot = mat.location.type === api.LocType.InBasket &&
|
|
60
|
+
mat.location.basketId === basketId &&
|
|
61
|
+
mat.location.basketSlot === mat.action.loadToBasketSlot;
|
|
62
|
+
if (!isPositiveInteger(mat.action.loadToBasketSlot) ||
|
|
63
|
+
(!reloadsSameSlot &&
|
|
64
|
+
mat.location.type !== api.LocType.Free &&
|
|
61
65
|
(mat.location.type !== api.LocType.InQueue || !isNonBlank(mat.location.currentQueue)))) {
|
|
62
|
-
return "invalid";
|
|
66
|
+
return { type: "invalid" };
|
|
63
67
|
}
|
|
64
|
-
return "
|
|
68
|
+
return { type: "valid", workId: mat.action.workId };
|
|
65
69
|
}
|
|
66
70
|
return undefined;
|
|
67
71
|
}
|
|
68
72
|
function confirmableWork(material, basketId) {
|
|
69
73
|
const classifiedActions = LazySeq.of(material)
|
|
70
|
-
.collect((mat) =>
|
|
71
|
-
const phase = actionPhase(mat, basketId);
|
|
72
|
-
return phase === undefined ? undefined : { phase, workId: mat.action.workId };
|
|
73
|
-
})
|
|
74
|
+
.collect((mat) => basketAction(mat, basketId))
|
|
74
75
|
.toRArray();
|
|
75
|
-
if (classifiedActions.some((
|
|
76
|
+
if (classifiedActions.some((action) => action.type === "invalid")) {
|
|
76
77
|
return { type: "inconsistent" };
|
|
77
78
|
}
|
|
78
|
-
const
|
|
79
|
-
if (
|
|
79
|
+
const validActions = classifiedActions.filter((action) => action.type === "valid");
|
|
80
|
+
if (validActions.length === 0)
|
|
80
81
|
return { type: "none" };
|
|
81
|
-
const tagged =
|
|
82
|
+
const tagged = validActions.filter((action) => isNonBlank(action.workId));
|
|
82
83
|
const workIds = LazySeq.of(tagged).toHashSet(({ workId }) => workId);
|
|
83
|
-
|
|
84
|
-
if (tagged.length !== phaseActions.length || workIds.size !== 1 || phases.size !== 1) {
|
|
84
|
+
if (tagged.length !== validActions.length || workIds.size !== 1) {
|
|
85
85
|
return { type: "inconsistent" };
|
|
86
86
|
}
|
|
87
87
|
return {
|
|
88
88
|
type: "confirmable",
|
|
89
|
-
work: { workId: tagged[0].workId
|
|
89
|
+
work: { workId: tagged[0].workId },
|
|
90
90
|
};
|
|
91
91
|
}
|
|
92
|
-
function
|
|
92
|
+
function loadingInstruction(mat) {
|
|
93
|
+
if (mat.location.type === api.LocType.InBasket &&
|
|
94
|
+
mat.location.basketId === mat.action.loadToBasketId &&
|
|
95
|
+
mat.location.basketSlot === mat.action.loadToBasketSlot) {
|
|
96
|
+
return "Unload, re-plate, and reload this slot";
|
|
97
|
+
}
|
|
93
98
|
if (mat.location.type === api.LocType.InQueue && mat.location.currentQueue) {
|
|
94
|
-
return mat.location.currentQueue
|
|
99
|
+
return `Load from ${mat.location.currentQueue}`;
|
|
95
100
|
}
|
|
96
|
-
return "raw material";
|
|
101
|
+
return "Load from raw material";
|
|
97
102
|
}
|
|
98
103
|
function SlotMaterial({ material, fsize, }) {
|
|
99
104
|
return material.map((mat) => (_jsx(MoveMaterialArrowNode, { kind: { type: MoveMaterialNodeKindType.Material, material: mat }, children: _jsx(InProcMaterial, { mat: mat, fsize: fsize }) }, mat.materialID)));
|
|
100
105
|
}
|
|
101
106
|
export function BasketLoadStationWorkflow({ stationNumber, basket, material, fsize, submitCommand, }) {
|
|
102
|
-
const [submission, setSubmission] = useState(
|
|
107
|
+
const [submission, setSubmission] = useState();
|
|
103
108
|
const workState = useMemo(() => confirmableWork(material, basket.basketId), [basket.basketId, material]);
|
|
104
109
|
const work = workState.type === "confirmable" ? workState.work : undefined;
|
|
105
|
-
|
|
110
|
+
const submissionState = submission !== undefined && submission.workId === work?.workId ? submission.state : "idle";
|
|
106
111
|
const materialBySlot = useMemo(() => LazySeq.of(material)
|
|
107
112
|
.filter((mat) => mat.location.type === api.LocType.InBasket &&
|
|
108
113
|
mat.location.basketId === basket.basketId &&
|
|
@@ -124,16 +129,19 @@ export function BasketLoadStationWorkflow({ stationNumber, basket, material, fsi
|
|
|
124
129
|
async function submit() {
|
|
125
130
|
if (work === undefined || submitCommand === undefined)
|
|
126
131
|
return;
|
|
127
|
-
|
|
132
|
+
const submittedWorkId = work.workId;
|
|
133
|
+
setSubmission({ workId: submittedWorkId, state: "submitting" });
|
|
128
134
|
try {
|
|
129
|
-
|
|
135
|
+
const state = await submitCommand(stationNumber, { workId: submittedWorkId });
|
|
136
|
+
setSubmission((current) => current?.workId === submittedWorkId ? { workId: submittedWorkId, state } : current);
|
|
130
137
|
}
|
|
131
138
|
catch {
|
|
132
|
-
setSubmission("error");
|
|
139
|
+
setSubmission((current) => current?.workId === submittedWorkId ? { workId: submittedWorkId, state: "error" } : current);
|
|
133
140
|
}
|
|
134
141
|
}
|
|
135
|
-
const submissionDisabled =
|
|
136
|
-
|
|
142
|
+
const submissionDisabled = submissionState === "submitting" ||
|
|
143
|
+
submissionState === "accepted" ||
|
|
144
|
+
submissionState === "conflict";
|
|
137
145
|
return (_jsxs(Stack, { spacing: 2, sx: { mt: 2 }, children: [_jsx(Typography, { variant: "h6", children: "Basket slots" }), _jsx(Box, { sx: {
|
|
138
146
|
display: "grid",
|
|
139
147
|
gridTemplateColumns: "repeat(auto-fit, minmax(15em, 1fr))",
|
|
@@ -142,6 +150,6 @@ export function BasketLoadStationWorkflow({ stationNumber, basket, material, fsi
|
|
|
142
150
|
const slotMaterial = materialBySlot.get(slot) ?? [];
|
|
143
151
|
const loads = loadsBySlot.get(slot) ?? [];
|
|
144
152
|
const isUnknown = basket.unknownSlots?.includes(slot) === true;
|
|
145
|
-
return (_jsx(Card, { "data-testid": `basket-load-station-slot-${slot}`, variant: "outlined", children: _jsxs(CardContent, { children: [_jsxs(Typography, { variant: "h6", children: ["Slot ", slot
|
|
146
|
-
}) }), work && submitCommand ? (_jsx(Box, { sx: { display: "flex", justifyContent: "flex-end" }, children: _jsx(Button, { variant: "contained", disabled: submissionDisabled, onClick: () => void submit(), children:
|
|
153
|
+
return (_jsx(Card, { "data-testid": `basket-load-station-slot-${slot}`, variant: "outlined", children: _jsxs(CardContent, { 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));
|
|
154
|
+
}) }), 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] }));
|
|
147
155
|
}
|
|
@@ -122,20 +122,20 @@ export const BulkAddCastingWithoutSerialDialog = memo(function BulkAddCastingWit
|
|
|
122
122
|
enteredQty !== null &&
|
|
123
123
|
!isNaN(enteredQty) &&
|
|
124
124
|
enteredQty > 0) {
|
|
125
|
-
addNewCasting({
|
|
125
|
+
void addNewCasting({
|
|
126
126
|
casting: selectedCasting,
|
|
127
127
|
quantity: enteredQty,
|
|
128
128
|
queue: queue,
|
|
129
129
|
workorder: null,
|
|
130
130
|
operator: fmsInfo.requireOperatorNamePromptWhenAddingMaterial ? enteredOperator : operator,
|
|
131
|
-
});
|
|
131
|
+
}).catch(console.log);
|
|
132
132
|
}
|
|
133
133
|
close();
|
|
134
134
|
}
|
|
135
135
|
function addAndPrint() {
|
|
136
136
|
if (queue !== null && selectedCasting !== null && enteredQty !== null && !isNaN(enteredQty)) {
|
|
137
137
|
setAdding(true);
|
|
138
|
-
addNewCasting({
|
|
138
|
+
void addNewCasting({
|
|
139
139
|
casting: selectedCasting,
|
|
140
140
|
quantity: enteredQty,
|
|
141
141
|
queue: queue,
|
|
@@ -154,7 +154,7 @@ export const BulkAddCastingWithoutSerialDialog = memo(function BulkAddCastingWit
|
|
|
154
154
|
onError: () => {
|
|
155
155
|
close();
|
|
156
156
|
},
|
|
157
|
-
});
|
|
157
|
+
}).catch(() => { });
|
|
158
158
|
}
|
|
159
159
|
}
|
|
160
160
|
return (_jsxs(_Fragment, { children: [_jsxs(Dialog, { open: queue !== null, onClose: () => close(), maxWidth: "md", children: [_jsx(DialogTitle, { children: "Add Raw Material" }), _jsx(DialogContent, { children: _jsxs(Stack, { direction: "column", spacing: 2, sx: {
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Button, Dialog, DialogActions, DialogContent, DialogTitle, TextField, Tooltip, } from "@mui/material";
|
|
3
|
+
import { useAtomValue, useSetAtom } from "jotai";
|
|
4
|
+
import { useState } from "react";
|
|
5
|
+
import { LazySeq } from "@seedtactics/immutable-collections";
|
|
6
|
+
import { currentStatus } from "../../cell-status/current-status.js";
|
|
7
|
+
import { inProcessMaterialInDialog, materialDialogOpen, } from "../../cell-status/material-details.js";
|
|
8
|
+
import { currentOperator } from "../../data/operators.js";
|
|
9
|
+
import { JobsBackend } from "../../network/backend.js";
|
|
10
|
+
import { ApiException, CancelLoadRequest } from "../../network/api.js";
|
|
11
|
+
export function CancelLoadButton({ onClose, ignoreOperator = false, }) {
|
|
12
|
+
const material = useAtomValue(inProcessMaterialInDialog);
|
|
13
|
+
const status = useAtomValue(currentStatus);
|
|
14
|
+
const operator = useAtomValue(currentOperator);
|
|
15
|
+
const setMaterialDialogOpen = useSetAtom(materialDialogOpen);
|
|
16
|
+
const [open, setOpen] = useState(false);
|
|
17
|
+
const [reason, setReason] = useState("");
|
|
18
|
+
const [updating, setUpdating] = useState(false);
|
|
19
|
+
const [error, setError] = useState(null);
|
|
20
|
+
const [snapshot, setSnapshot] = useState(null);
|
|
21
|
+
const cancellationId = material?.action.loadCancellationId ?? null;
|
|
22
|
+
const currentSnapshot = material === null || cancellationId === null
|
|
23
|
+
? null
|
|
24
|
+
: {
|
|
25
|
+
material,
|
|
26
|
+
cancellationId,
|
|
27
|
+
group: LazySeq.of(status.material)
|
|
28
|
+
.filter((candidate) => candidate.action.loadCancellationId === cancellationId)
|
|
29
|
+
.toRArray(),
|
|
30
|
+
};
|
|
31
|
+
if (currentSnapshot === null && snapshot === null)
|
|
32
|
+
return null;
|
|
33
|
+
async function cancelLoad() {
|
|
34
|
+
if (snapshot === null)
|
|
35
|
+
return;
|
|
36
|
+
setUpdating(true);
|
|
37
|
+
setError(null);
|
|
38
|
+
try {
|
|
39
|
+
await JobsBackend.cancelLoad(snapshot.material.materialID, ignoreOperator ? null : operator, new CancelLoadRequest({
|
|
40
|
+
expectedLoadCancellationId: snapshot.cancellationId,
|
|
41
|
+
reason: reason || undefined,
|
|
42
|
+
}));
|
|
43
|
+
setMaterialDialogOpen(null);
|
|
44
|
+
onClose?.();
|
|
45
|
+
}
|
|
46
|
+
catch (e) {
|
|
47
|
+
setError(ApiException.isApiException(e) ? e.response : e instanceof Error ? e.message : String(e));
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
setUpdating(false);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return (_jsxs(_Fragment, { children: [currentSnapshot ? (_jsx(Tooltip, { title: "Cancel the displayed load instruction", children: _jsx(Button, { color: "primary", disabled: updating, onClick: () => {
|
|
54
|
+
setSnapshot(currentSnapshot);
|
|
55
|
+
setReason("");
|
|
56
|
+
setError(null);
|
|
57
|
+
setOpen(true);
|
|
58
|
+
}, children: "Cancel Load" }) })) : null, _jsxs(Dialog, { open: open && snapshot !== null, onClose: () => setOpen(false), children: [_jsx(DialogTitle, { children: "Cancel Load" }), _jsxs(DialogContent, { children: [_jsx("p", { children: "The displayed load instruction will be cancelled and recalculated for:" }), _jsx("ul", { children: snapshot?.group.map((candidate) => (_jsx("li", { children: candidate.serial || `Material ID ${candidate.materialID}` }, candidate.materialID))) }), _jsx(TextField, { label: "Reason (optional)", value: reason, onChange: (e) => setReason(e.target.value), fullWidth: true, multiline: true }), error ? _jsx("p", { role: "alert", children: error }) : null] }), _jsxs(DialogActions, { children: [_jsx(Button, { color: "primary", disabled: updating, onClick: cancelLoad, children: "Cancel Load" }), _jsx(Button, { color: "secondary", disabled: updating, onClick: () => setOpen(false), children: "Keep Load" })] })] })] }));
|
|
59
|
+
}
|
|
@@ -44,6 +44,7 @@ import { materialDialogOpen, materialInDialogInfo, materialInDialogLargestUsedPr
|
|
|
44
44
|
import { last30MaterialSummary, } from "../../cell-status/material-summary.js";
|
|
45
45
|
import { instructionUrl } from "../../network/backend.js";
|
|
46
46
|
import { QuarantineMatButton } from "./QuarantineButton.js";
|
|
47
|
+
import { CancelLoadButton } from "./CancelLoadButton.js";
|
|
47
48
|
import { useIsDemo, useSetTitle } from "../routes.js";
|
|
48
49
|
import { atom, useAtom, useAtomValue, useSetAtom } from "jotai";
|
|
49
50
|
function CompleteButton() {
|
|
@@ -108,7 +109,7 @@ function AssignWorkorderButton() {
|
|
|
108
109
|
return (_jsx(Button, { color: "primary", onClick: () => setWorkorderDialogOpen(true), children: "Assign Workorder" }));
|
|
109
110
|
}
|
|
110
111
|
const CloseoutMaterialDialog = memo(function CloseoutDialog() {
|
|
111
|
-
return (_jsx(MaterialDialog, { allowNote: true, buttons: _jsxs(_Fragment, { children: [_jsx(InstrButton, {}), _jsx(QuarantineMatButton, {}), _jsx(AssignWorkorderButton, {}), _jsx(CompleteButton, {})] }) }));
|
|
112
|
+
return (_jsx(MaterialDialog, { allowNote: true, buttons: _jsxs(_Fragment, { children: [_jsx(InstrButton, {}), _jsx(QuarantineMatButton, {}), _jsx(CancelLoadButton, {}), _jsx(AssignWorkorderButton, {}), _jsx(CompleteButton, {})] }) }));
|
|
112
113
|
});
|
|
113
114
|
const currentNearestMinutes = atom(new Date());
|
|
114
115
|
currentNearestMinutes.onMount = (set) => {
|
|
@@ -43,6 +43,7 @@ import { last30MaterialSummary } from "../../cell-status/material-summary.js";
|
|
|
43
43
|
import { LazySeq } from "@seedtactics/immutable-collections";
|
|
44
44
|
import { instructionUrl } from "../../network/backend.js";
|
|
45
45
|
import { QuarantineMatButton } from "./QuarantineButton.js";
|
|
46
|
+
import { CancelLoadButton } from "./CancelLoadButton.js";
|
|
46
47
|
import { useIsDemo, useSetTitle } from "../routes.js";
|
|
47
48
|
import { useAtomValue, useSetAtom } from "jotai";
|
|
48
49
|
function InspButtons(props) {
|
|
@@ -67,7 +68,7 @@ function InspButtons(props) {
|
|
|
67
68
|
setMatToShow(null);
|
|
68
69
|
}
|
|
69
70
|
const url = instructionUrl(material.partName, props.inspection_type, material.materialID, null, maxProc, operator);
|
|
70
|
-
return (_jsxs(_Fragment, { children: [material.partName !== "" ? (demo ? (_jsx(Button, { color: "primary", children: "Instructions" })) : (_jsx(Button, { href: url, target: "bms-instructions", color: "primary", children: "Instructions" }))) : undefined, _jsx(QuarantineMatButton, {}), _jsxs(Button, { color: "primary", disabled: completeInspUpdating, onClick: () => markInspComplete(true), children: ["Mark ", props.inspection_type, " Success"] }), _jsxs(Button, { color: "primary", disabled: completeInspUpdating, onClick: () => markInspComplete(false), children: ["Mark ", props.inspection_type, " Failed"] })] }));
|
|
71
|
+
return (_jsxs(_Fragment, { children: [material.partName !== "" ? (demo ? (_jsx(Button, { color: "primary", children: "Instructions" })) : (_jsx(Button, { href: url, target: "bms-instructions", color: "primary", children: "Instructions" }))) : undefined, _jsx(QuarantineMatButton, {}), _jsx(CancelLoadButton, {}), _jsxs(Button, { color: "primary", disabled: completeInspUpdating, onClick: () => markInspComplete(true), children: ["Mark ", props.inspection_type, " Success"] }), _jsxs(Button, { color: "primary", disabled: completeInspUpdating, onClick: () => markInspComplete(false), children: ["Mark ", props.inspection_type, " Failed"] })] }));
|
|
71
72
|
}
|
|
72
73
|
function DialogBodyInspButtons({ focusInspectionType }) {
|
|
73
74
|
const material = useAtomValue(matDetails.materialInDialogInspections);
|
|
@@ -1,15 +1,18 @@
|
|
|
1
|
-
import { IInProcessMaterial } from "../../network/api.js";
|
|
1
|
+
import { IInProcessMaterial, ILogEntry } from "../../network/api.js";
|
|
2
|
+
import { HashMap } from "@seedtactics/immutable-collections";
|
|
2
3
|
import { ReactNode } from "react";
|
|
3
4
|
export type InvalidateCycleState = {
|
|
4
5
|
readonly process: number | null;
|
|
5
6
|
readonly changeRawMat: string | null;
|
|
6
7
|
readonly changeJobUnique: string | null;
|
|
7
8
|
readonly updating: boolean;
|
|
9
|
+
readonly error: string | null;
|
|
8
10
|
};
|
|
9
11
|
export type InvalidateCycleProps = {
|
|
10
12
|
readonly st: InvalidateCycleState | null;
|
|
11
13
|
readonly setState: (s: InvalidateCycleState | null) => void;
|
|
12
14
|
};
|
|
15
|
+
export declare function affectedMaterialForInvalidation(events: ReadonlyArray<Readonly<ILogEntry>>, materialId: number, process: number | null): HashMap<number, string | null>;
|
|
13
16
|
export declare function InvalidateCycleDialogContent(props: InvalidateCycleProps): import("react").JSX.Element;
|
|
14
17
|
export declare function InvalidateCycleDialogButton(props: InvalidateCycleProps & {
|
|
15
18
|
readonly onClose: () => void;
|