@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.
@@ -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, } from "../network/api.js";
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, SwapMaterialButtons, SwapMaterialDialogContent, } from "../station-monitor/InvalidateCycle.js";
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: _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 })] }) }));
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 confirmableWork(material, basketId) {
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: "confirmable",
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(() => confirmableWork(material, basket.basketId), [basket.basketId, material]);
109
- const work = workState.type === "confirmable" ? workState.work : undefined;
110
- const submissionState = submission !== undefined && submission.workId === work?.workId ? submission.state : "idle";
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.basketId, material]);
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.basketId, material]);
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
- setSubmission({ workId: submittedWorkId, state: "submitting" });
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?.workId === submittedWorkId ? { workId: submittedWorkId, state } : current);
180
+ setSubmission((current) => (current === pending ? { ...pending, state } : current));
137
181
  }
138
182
  catch {
139
- setSubmission((current) => current?.workId === submittedWorkId ? { workId: submittedWorkId, state: "error" } : current);
183
+ setSubmission((current) => (current === pending ? { ...pending, state: "error" } : current));
140
184
  }
141
185
  }
142
- const submissionDisabled = submissionState === "submitting" ||
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 && 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] }));
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 { IInProcessMaterial, ILogEntry } from "../../network/api.js";
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, isActiveLoadStationOperation, } from "../../data/material-operation-policy.js";
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, SwapMaterialButtons, SwapMaterialDialogContent, } from "./InvalidateCycle.js";
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
- }, [setSwapSt, setInvalidateSt]);
639
- return (_jsx(MaterialDialog, { onClose: onClose, allowNote: true, highlightProcsGreaterOrEqualTo: invalidateSt?.process ?? undefined, extraDialogElements: _jsxs(_Fragment, { children: [_jsx(SwapMaterialDialogContent, { st: swapSt, setState: setSwapSt }), invalidateSt !== null ? (_jsx(InvalidateCycleDialogContent, { st: invalidateSt, setState: setInvalidateSt })) : null, showAddMaterial ? (_jsx(PromptForQueue, { selectedQueue: selectedQueue, setSelectedQueue: setSelectedQueue, queueNames: props.queues })) : undefined] }), buttons: _jsxs(_Fragment, { children: [_jsx(InstructionButton, { pallet: props.pallet }), _jsx(PrintLabelButton, {}), _jsx(QuarantineMatButton, {}), _jsx(CancelLoadButton, { onClose: onClose }), _jsx(SignalInspectionButton, {}), _jsx(AddMatButton, { toQueue: selectedQueue, queues: props.queues, onClose: onClose, showAddToQueue: showAddMaterial, setShowAddToQueue: setShowAddMaterial }), _jsx(SwapMaterialButtons, { st: swapSt, setState: setSwapSt, onClose: onClose }), _jsx(InvalidateCycleDialogButton, { st: invalidateSt, setState: setInvalidateSt, onClose: onClose, loadStation: true }), _jsx(AssignWorkorderButton, {})] }) }));
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 ? ` slot ${mat.action.loadToBasketSlot}` : ""] }));
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, SwapMaterialButtons, SwapMaterialDialogContent, } from "./InvalidateCycle.js";
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: _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 })] }) }));
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-C6EBFHuW.js"></script>
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>