@seedtactics/insight-client 17.0.0-beta.1 → 17.0.0-beta.2

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.
@@ -2,6 +2,7 @@ import { ReactNode, ComponentType } from "react";
2
2
  import { ChooseModeItem } from "./ChooseMode.js";
3
3
  import * as serverSettings from "../network/server-settings.js";
4
4
  import type { SubmitBasketLoadStationCommand } from "./station-monitor/BasketLoadStationWork.js";
5
+ import type { SubmitBasketLocationCorrection, SubmitBasketMovementCompletion } from "./station-monitor/BasketMovementArrival.js";
5
6
  export declare function NavTabs({ children }: {
6
7
  children?: ReactNode;
7
8
  }): import("react").JSX.Element;
@@ -12,6 +13,8 @@ export interface AppProps {
12
13
  };
13
14
  readonly chooseModes?: (i: serverSettings.FMSInfoAndUser) => ReadonlyArray<ChooseModeItem> | null;
14
15
  readonly submitBasketLoadStationCommand?: SubmitBasketLoadStationCommand;
16
+ readonly submitBasketMovementCompletion?: SubmitBasketMovementCompletion;
17
+ readonly submitBasketLocationCorrection?: SubmitBasketLocationCorrection;
15
18
  }
16
19
  declare const App: import("react").MemoExoticComponent<(props: AppProps) => import("react").JSX.Element>;
17
20
  export default App;
@@ -310,7 +310,7 @@ const App = memo(function App(props) {
310
310
  else {
311
311
  switch (route.route) {
312
312
  case routes.RouteLocation.Station_LoadMonitor:
313
- page = (_jsx(LoadStation, { loadNum: route.loadNum, queues: route.queues, completed: route.completed, submitBasketLoadStationCommand: props.submitBasketLoadStationCommand }));
313
+ page = (_jsx(LoadStation, { loadNum: route.loadNum, queues: route.queues, completed: route.completed, submitBasketLoadStationCommand: props.submitBasketLoadStationCommand, submitBasketMovementCompletion: props.submitBasketMovementCompletion, submitBasketLocationCorrection: props.submitBasketLocationCorrection }, route.loadNum));
314
314
  nav1 = StationToolbar;
315
315
  nav2 = StationToolbarOverviewButton;
316
316
  showOperator = true;
@@ -93,10 +93,22 @@ function logType(entry, fmsInfo) {
93
93
  else {
94
94
  return "Depart";
95
95
  }
96
- case api.LogType.BasketIdentityHint:
97
- return `${basketName} Identity Hint`;
96
+ case api.LogType.BasketIdentityAssociation:
97
+ return `${basketName} Identity Association`;
98
+ case api.LogType.BasketIdentityAssociationCorrection:
99
+ return `${basketName} Identity Correction`;
98
100
  case api.LogType.BasketContentSnapshot:
99
101
  return `${basketName} Content Snapshot`;
102
+ case api.LogType.BasketLocationObservation:
103
+ return `${basketName} Location Observation`;
104
+ case api.LogType.BasketLocationObservationCorrection:
105
+ return `${basketName} Location Correction`;
106
+ case api.LogType.BasketRegionSurvey:
107
+ return `${basketName} Region Survey`;
108
+ case api.LogType.BasketMisload:
109
+ return `${basketName} Inspection Hold`;
110
+ case api.LogType.BasketMisloadResolution:
111
+ return `${basketName} Inspection Resolution`;
100
112
  case api.LogType.MachineCycle:
101
113
  if (entry.startofcycle) {
102
114
  return "Start Cycle";
@@ -141,6 +153,12 @@ function logType(entry, fmsInfo) {
141
153
  return "Message";
142
154
  }
143
155
  }
156
+ function evidencePosition(entry) {
157
+ const title = entry.details?.locationTitle ?? entry.details?.location ?? entry.loc;
158
+ const locationNumber = entry.locnum > 0 ? ` ${entry.locnum}` : "";
159
+ const zone = entry.details?.zone === undefined ? "" : ` zone ${entry.details.zone}`;
160
+ return `${title}${locationNumber}${zone}`;
161
+ }
144
162
  export function isLogEntryInvalidated(e) {
145
163
  return e.details?.["PalletCycleInvalidated"] === "1";
146
164
  }
@@ -202,14 +220,57 @@ function display(props, fmsInfo) {
202
220
  return (_jsxs("span", { children: [basketContainerName(entry, basketName), " departed from ", entry.loc, entry.locnum > 0 ? ` position ${entry.locnum}` : ""] }));
203
221
  }
204
222
  }
205
- case api.LogType.BasketIdentityHint: {
223
+ case api.LogType.BasketIdentityAssociation: {
224
+ const basketName = basketDisplayName(fmsInfo.basketName);
225
+ const episodeCount = entry.details?.episodeCount;
226
+ const source = entry.details?.sourceName;
227
+ const note = entry.details?.note;
228
+ return (_jsxs("span", { children: [source === undefined ? "" : `${source} recorded `, episodeCount === undefined
229
+ ? `content episodes associated with ${basketName} ${entry.pal}`
230
+ : `${episodeCount} content episode${episodeCount === "1" ? "" : "s"} associated with ${basketName} ${entry.pal}`, entry.details?.location === undefined && entry.details?.locationTitle === undefined
231
+ ? ""
232
+ : ` at ${evidencePosition(entry)}`, note === undefined ? "" : `: ${note}`] }));
233
+ }
234
+ case api.LogType.BasketIdentityAssociationCorrection: {
206
235
  const basketName = basketDisplayName(fmsInfo.basketName);
207
- return (_jsxs("span", { children: [basketContainerName(entry, basketName), " presently associated with ", basketName, " ", entry.pal] }));
236
+ return (_jsxs("span", { children: ["Identity association for ", basketName, " ", entry.pal, " corrected"] }));
208
237
  }
209
238
  case api.LogType.BasketContentSnapshot: {
210
239
  const basketName = basketDisplayName(fmsInfo.basketName);
211
240
  return (_jsxs("span", { children: [basketContainerName(entry, basketName), " contained ", displayMat(entry.material)] }));
212
241
  }
242
+ case api.LogType.BasketLocationObservation: {
243
+ const basketName = basketDisplayName(fmsInfo.basketName);
244
+ const operator = entry.details?.sourceName ?? entry.details?.operator ?? "Operator";
245
+ return (_jsxs("span", { children: [operator, " observed ", basketName, " ", entry.pal, " at ", evidencePosition(entry)] }));
246
+ }
247
+ case api.LogType.BasketLocationObservationCorrection: {
248
+ const basketName = basketDisplayName(fmsInfo.basketName);
249
+ const operator = entry.details?.sourceName ?? entry.details?.operator ?? "Operator";
250
+ const location = evidencePosition(entry);
251
+ const replacementBasketId = entry.details?.replacementBasketId;
252
+ return replacementBasketId === undefined ? (_jsxs("span", { children: [operator, " retracted the observation of ", basketName, " ", entry.pal, " at ", location] })) : (_jsxs("span", { children: [operator, " changed the observation of ", basketName, " ", entry.pal, " at ", location, " to", " ", basketName, " ", replacementBasketId, " at ", location] }));
253
+ }
254
+ case api.LogType.BasketRegionSurvey: {
255
+ const basketName = basketDisplayName(fmsInfo.basketName);
256
+ const source = entry.details?.sourceName;
257
+ const observed = entry.details?.observedBasketCount;
258
+ const unidentified = entry.details?.unidentifiedBasketCount;
259
+ return (_jsxs("span", { children: [source === undefined ? "Operator" : source, " recorded a ", entry.result.toLowerCase(), " ", basketName.toLowerCase(), " survey at ", evidencePosition(entry), observed === undefined ? "" : ` (${observed} identified`, unidentified === undefined ? "" : `, ${unidentified} unidentified`, observed === undefined && unidentified === undefined ? "" : ")"] }));
260
+ }
261
+ case api.LogType.BasketMisload: {
262
+ const basketName = basketDisplayName(fmsInfo.basketName);
263
+ const source = entry.details?.sourceName;
264
+ const reason = entry.details?.reason;
265
+ const subject = entry.pal > 0 ? `${basketName} ${entry.pal}` : `Unnumbered ${basketName.toLowerCase()}`;
266
+ return (_jsxs("span", { children: [source === undefined ? subject : `${source} reported ${subject}`, " requires inspection at", " ", evidencePosition(entry), reason === undefined ? "" : `: ${reason}`] }));
267
+ }
268
+ case api.LogType.BasketMisloadResolution: {
269
+ const basketName = basketDisplayName(fmsInfo.basketName);
270
+ const source = entry.details?.sourceName;
271
+ const subject = entry.pal > 0 ? `${basketName} ${entry.pal}` : `Unnumbered ${basketName.toLowerCase()}`;
272
+ return (_jsxs("span", { children: [source === undefined ? "Inspection hold" : `${source} resolved inspection hold`, " for", " ", subject, " at ", evidencePosition(entry), ": ", entry.result] }));
273
+ }
213
274
  case api.LogType.MachineCycle:
214
275
  return (_jsxs("span", { children: [displayMat(entry.material), " on", " ", _jsxs(ColoredSpan, { $type: "pallet", children: ["pallet ", entry.pal] }), " at", " ", _jsxs(ColoredSpan, { $type: "machine", children: [entry.loc, " ", entry.locnum.toString()] }), entry.program && entry.program !== "" ? (_jsxs("span", { children: [" with program ", entry.program] })) : undefined] }));
215
276
  case api.LogType.PartMark:
@@ -0,0 +1,38 @@
1
+ import * as api from "../../network/api.js";
2
+ export interface BasketMovementCompletionCommand {
3
+ readonly commandId: string;
4
+ readonly instructionId: string;
5
+ readonly observedBasketId: number;
6
+ }
7
+ export interface BasketMovementCompletionReceipt {
8
+ readonly stationNumber: number;
9
+ readonly instructionId: string;
10
+ readonly observationId: string;
11
+ readonly observedBasketId: number;
12
+ }
13
+ export type SubmitBasketMovementCompletion = (stationNumber: number, command: BasketMovementCompletionCommand) => Promise<BasketMovementCompletionReceipt | "conflict">;
14
+ export interface BasketLocationCorrectionCommand {
15
+ readonly correctionId: string;
16
+ readonly targetObservationId: string;
17
+ readonly replacementBasketId: number | null;
18
+ readonly replacementObservationId: string | null;
19
+ }
20
+ export type SubmitBasketLocationCorrection = (stationNumber: number, command: BasketLocationCorrectionCommand) => Promise<"accepted" | "conflict">;
21
+ export interface BasketArrivalReceipt {
22
+ readonly stationNumber: number;
23
+ readonly instruction: Readonly<api.IBasketMoveInstruction>;
24
+ readonly command: BasketMovementCompletionCommand;
25
+ readonly receipt: BasketMovementCompletionReceipt;
26
+ readonly status: "recorded" | "corrected" | "retracted";
27
+ }
28
+ export declare function loadStationArrivalInstruction(instructions: ReadonlyArray<Readonly<api.IBasketMoveInstruction>>, stationNumber: number): Readonly<api.IBasketMoveInstruction> | undefined;
29
+ export declare function BasketMovementArrival({ stationNumber, basketName, instruction, submitCommand, submitCorrection, receipt, onAccepted, onCorrected, }: {
30
+ readonly stationNumber: number;
31
+ readonly basketName: string;
32
+ readonly instruction: Readonly<api.IBasketMoveInstruction>;
33
+ readonly submitCommand: SubmitBasketMovementCompletion | undefined;
34
+ readonly submitCorrection: SubmitBasketLocationCorrection | undefined;
35
+ readonly receipt?: BasketArrivalReceipt;
36
+ readonly onAccepted?: (receipt: BasketArrivalReceipt) => void;
37
+ readonly onCorrected?: (command: BasketLocationCorrectionCommand) => void;
38
+ }): import("react").JSX.Element;
@@ -0,0 +1,175 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /* Copyright (c) 2026, John Lenz
3
+
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ * Redistributions of source code must retain the above copyright
10
+ notice, this list of conditions and the following disclaimer.
11
+ * Redistributions in binary form must reproduce the above copyright
12
+ notice, this list of conditions and the following disclaimer in the
13
+ documentation and/or other materials provided with the distribution.
14
+ * Neither the name of John Lenz, Black Maple Software, SeedTactics,
15
+ nor the names of other contributors may be used to endorse or
16
+ promote products derived from this software without specific
17
+ prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
+ */
31
+ import { useEffect, useRef, useState } from "react";
32
+ import { Alert, Box, Button, Paper, Stack, TextField, Typography } from "@mui/material";
33
+ import * as api from "../../network/api.js";
34
+ export function loadStationArrivalInstruction(instructions, stationNumber) {
35
+ const activeIds = new Set(instructions.map((instruction) => instruction.instructionId));
36
+ return instructions.find((instruction) => instruction.basketId !== undefined &&
37
+ instruction.destination.location === api.BasketLocationEnum.LoadUnload &&
38
+ instruction.destination.locationNum === stationNumber &&
39
+ (instruction.prerequisiteInstructionId === undefined ||
40
+ !activeIds.has(instruction.prerequisiteInstructionId)));
41
+ }
42
+ export function BasketMovementArrival({ stationNumber, basketName, instruction, submitCommand, submitCorrection, receipt, onAccepted, onCorrected, }) {
43
+ const [chooseDifferent, setChooseDifferent] = useState(false);
44
+ const [differentBasket, setDifferentBasket] = useState("");
45
+ const [submission, setSubmission] = useState();
46
+ const [changingRecordedBasket, setChangingRecordedBasket] = useState(false);
47
+ const [correctionSubmission, setCorrectionSubmission] = useState();
48
+ const currentInstructionId = useRef(instruction.instructionId);
49
+ const currentStationNumber = useRef(stationNumber);
50
+ const currentCorrectionTargetId = useRef(undefined);
51
+ currentInstructionId.current = instruction.instructionId;
52
+ currentStationNumber.current = stationNumber;
53
+ useEffect(() => {
54
+ setChooseDifferent(false);
55
+ setDifferentBasket("");
56
+ setSubmission((current) => current?.state === "error" && current.command.instructionId === instruction.instructionId
57
+ ? current
58
+ : undefined);
59
+ setChangingRecordedBasket(false);
60
+ setCorrectionSubmission(undefined);
61
+ }, [instruction.instructionId, stationNumber]);
62
+ useEffect(() => {
63
+ setChangingRecordedBasket(false);
64
+ setDifferentBasket("");
65
+ setCorrectionSubmission((current) => current?.state === "error" &&
66
+ receipt?.status !== "retracted" &&
67
+ current.command.targetObservationId === receipt?.receipt.observationId
68
+ ? current
69
+ : undefined);
70
+ }, [receipt?.receipt.observationId, receipt?.status]);
71
+ const expectedBasketId = instruction.basketId;
72
+ if (expectedBasketId === undefined) {
73
+ return (_jsx(Alert, { severity: "warning", children: "This tracked basket must be handled through the recovery workflow." }));
74
+ }
75
+ async function record(command) {
76
+ if (submitCommand === undefined)
77
+ return;
78
+ setSubmission({ state: "submitting", command });
79
+ try {
80
+ const result = await submitCommand(stationNumber, command);
81
+ if (currentStationNumber.current !== stationNumber ||
82
+ currentInstructionId.current !== command.instructionId)
83
+ return;
84
+ if (result === "conflict") {
85
+ setSubmission({ state: result, command });
86
+ }
87
+ else {
88
+ setSubmission({ state: "accepted", command, receipt: result });
89
+ onAccepted?.({
90
+ stationNumber,
91
+ instruction,
92
+ command,
93
+ receipt: result,
94
+ status: "recorded",
95
+ });
96
+ }
97
+ }
98
+ catch (error) {
99
+ if (currentStationNumber.current !== stationNumber ||
100
+ currentInstructionId.current !== command.instructionId)
101
+ return;
102
+ setSubmission({
103
+ state: "error",
104
+ command,
105
+ message: error instanceof Error ? error.message : "Unable to record basket arrival",
106
+ });
107
+ }
108
+ }
109
+ async function correct(command) {
110
+ if (submitCorrection === undefined || activeCommand === undefined)
111
+ return;
112
+ setCorrectionSubmission({ state: "submitting", command });
113
+ try {
114
+ const result = await submitCorrection(stationNumber, command);
115
+ if (currentStationNumber.current !== stationNumber ||
116
+ currentCorrectionTargetId.current !== command.targetObservationId)
117
+ return;
118
+ setCorrectionSubmission({ state: result, command });
119
+ if (result === "accepted")
120
+ onCorrected?.(command);
121
+ }
122
+ catch {
123
+ if (currentStationNumber.current !== stationNumber ||
124
+ currentCorrectionTargetId.current !== command.targetObservationId)
125
+ return;
126
+ setCorrectionSubmission({ state: "error", command });
127
+ }
128
+ }
129
+ function createCommand(basketId) {
130
+ return {
131
+ commandId: crypto.randomUUID(),
132
+ instructionId: instruction.instructionId,
133
+ observedBasketId: basketId,
134
+ };
135
+ }
136
+ const parsedDifferentBasket = Number(differentBasket);
137
+ const activeCommand = receipt?.status === "retracted"
138
+ ? undefined
139
+ : (receipt?.command ??
140
+ (submission?.state === "accepted" &&
141
+ submission.command.instructionId === instruction.instructionId
142
+ ? submission.command
143
+ : undefined));
144
+ const activeObservationId = receipt?.status === "retracted"
145
+ ? undefined
146
+ : (receipt?.receipt.observationId ??
147
+ (submission?.state === "accepted" ? submission.receipt.observationId : undefined));
148
+ currentCorrectionTargetId.current = activeObservationId;
149
+ function createCorrection(replacementBasketId) {
150
+ return {
151
+ correctionId: crypto.randomUUID(),
152
+ targetObservationId: activeObservationId ?? "",
153
+ replacementBasketId,
154
+ replacementObservationId: replacementBasketId === null ? null : crypto.randomUUID(),
155
+ };
156
+ }
157
+ const validDifferentBasket = Number.isInteger(parsedDifferentBasket) &&
158
+ parsedDifferentBasket > 0 &&
159
+ parsedDifferentBasket !== (activeCommand?.observedBasketId ?? expectedBasketId);
160
+ const disabled = submitCommand === undefined ||
161
+ activeCommand !== undefined ||
162
+ (submission !== undefined && submission.state !== "error");
163
+ const receiptMessage = receipt?.status === "retracted"
164
+ ? "Arrival observation retracted."
165
+ : receipt?.status === "corrected"
166
+ ? `Arrival corrected to ${basketName.toLocaleLowerCase()} ${receipt.command.observedBasketId}.`
167
+ : "Arrival recorded. Waiting for updated status.";
168
+ return (_jsx(Paper, { component: "section", elevation: 2, sx: { margin: 1, padding: 2 }, children: _jsxs(Stack, { spacing: 2, children: [_jsxs(Box, { children: [_jsx(Typography, { variant: "overline", children: receipt === undefined ? "Basket arrival" : "Basket arrival receipt" }), _jsx(Typography, { variant: "h4", children: receipt?.status === "retracted"
169
+ ? "Retracted basket arrival"
170
+ : receipt === undefined
171
+ ? `Bring ${basketName.toLocaleLowerCase()} ${expectedBasketId} to this station`
172
+ : `${basketName} ${receipt.command.observedBasketId} at this station` }), receipt === undefined ? (_jsx(Typography, { children: "Record arrival after the basket is physically in place." })) : null] }), receipt === undefined ? (_jsxs(Stack, { direction: { xs: "column", sm: "row" }, spacing: 1, children: [_jsx(Button, { disabled: disabled, onClick: () => void record(createCommand(expectedBasketId)), size: "large", variant: "contained", children: submission?.state === "submitting"
173
+ ? "Recording arrival…"
174
+ : `${basketName} ${expectedBasketId} arrived` }), _jsxs(Button, { disabled: disabled, onClick: () => setChooseDifferent((visible) => !visible), size: "large", variant: "outlined", children: ["Different ", basketName.toLocaleLowerCase()] })] })) : null, receipt === undefined && chooseDifferent && !disabled ? (_jsxs(Stack, { direction: { xs: "column", sm: "row" }, spacing: 1, children: [_jsx(TextField, { label: `${basketName} number`, onChange: (event) => setDifferentBasket(event.target.value), type: "number", value: differentBasket, slotProps: { htmlInput: { inputMode: "numeric", min: 1 } } }), _jsxs(Button, { disabled: !validDifferentBasket, onClick: () => void record(createCommand(parsedDifferentBasket)), variant: "contained", children: ["Record ", basketName.toLocaleLowerCase(), " arrival"] })] })) : null, submitCommand === undefined ? (_jsx(Alert, { severity: "warning", children: "Basket arrival completion is not configured." })) : activeCommand !== undefined || receipt?.status === "retracted" ? (_jsxs(Stack, { spacing: 1, children: [_jsx(Alert, { severity: "success", children: receiptMessage }), submitCorrection === undefined || activeCommand === undefined ? null : (_jsxs(Stack, { direction: { xs: "column", sm: "row" }, spacing: 1, children: [_jsx(Button, { disabled: correctionSubmission !== undefined, onClick: () => setChangingRecordedBasket((visible) => !visible), variant: "outlined", children: "Change" }), _jsx(Button, { color: "error", disabled: correctionSubmission !== undefined, onClick: () => void correct(createCorrection(null)), variant: "text", children: "Undo" })] })), changingRecordedBasket && correctionSubmission === undefined ? (_jsxs(Stack, { direction: { xs: "column", sm: "row" }, spacing: 1, children: [_jsx(TextField, { label: `Correct ${basketName.toLocaleLowerCase()} number`, onChange: (event) => setDifferentBasket(event.target.value), type: "number", value: differentBasket, slotProps: { htmlInput: { inputMode: "numeric", min: 1 } } }), _jsx(Button, { disabled: !validDifferentBasket, onClick: () => void correct(createCorrection(parsedDifferentBasket)), variant: "contained", children: "Save correction" })] })) : null, correctionSubmission?.state === "accepted" ? (_jsx(Alert, { severity: "success", children: "Correction recorded." })) : correctionSubmission?.state === "conflict" ? (_jsx(Alert, { severity: "warning", children: "Basket evidence changed. Review the refreshed status." })) : correctionSubmission?.state === "error" ? (_jsx(Alert, { action: _jsx(Button, { color: "inherit", onClick: () => void correct(correctionSubmission.command), children: "Retry correction" }), severity: "error", children: "Unable to correct the recorded arrival." })) : null] })) : submission?.state === "conflict" ? (_jsx(Alert, { severity: "warning", children: "Basket movement changed. Review the refreshed instruction." })) : submission?.state === "error" ? (_jsx(Alert, { action: _jsx(Button, { onClick: () => void record(submission.command), color: "inherit", children: "Retry arrival" }), severity: "error", children: submission.message })) : null] }) }));
175
+ }
@@ -1,11 +1,14 @@
1
1
  import { ReactNode } from "react";
2
2
  import { SubmitBasketLoadStationCommand } from "./BasketLoadStationWork.js";
3
+ import { SubmitBasketLocationCorrection, SubmitBasketMovementCompletion } from "./BasketMovementArrival.js";
3
4
  interface LoadStationProps {
4
5
  readonly loadNum: number;
5
6
  readonly queues: ReadonlyArray<string>;
6
7
  readonly completed: boolean;
7
8
  readonly whiteBackground?: boolean;
8
9
  readonly submitBasketLoadStationCommand?: SubmitBasketLoadStationCommand;
10
+ readonly submitBasketMovementCompletion?: SubmitBasketMovementCompletion;
11
+ readonly submitBasketLocationCorrection?: SubmitBasketLocationCorrection;
9
12
  }
10
13
  export declare function LoadStation(props: LoadStationProps): import("react").JSX.Element;
11
14
  declare function LoadStationCheckWidth(props: LoadStationProps): ReactNode;
@@ -31,7 +31,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31
31
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32
32
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
33
  */
34
- import { useMemo, memo, useState, useCallback } from "react";
34
+ import { useMemo, memo, useState, useCallback, useEffect } from "react";
35
35
  import { Box, useMediaQuery, Button, Typography } from "@mui/material";
36
36
  import { LazySeq, mkCompareByProperties, OrderedMap } from "@seedtactics/immutable-collections";
37
37
  import { FolderOpen as FolderOpenIcon } from "@mui/icons-material";
@@ -64,6 +64,7 @@ import { hideNonLoadingMaterialOnLoadStation } from "../../data/queue-material.j
64
64
  import { basketDisplayName, loadStationDisplayName } from "../../cell-status/station-cycles.js";
65
65
  import { canAddOrMoveMaterialToQueue } from "../../data/material-operation-policy.js";
66
66
  import { BasketLoadStationWorkflow, } from "./BasketLoadStationWork.js";
67
+ import { BasketMovementArrival, loadStationArrivalInstruction, } from "./BasketMovementArrival.js";
67
68
  function encodeLoadStationRegionIdPart(value) {
68
69
  return encodeURIComponent(value.toString());
69
70
  }
@@ -663,6 +664,20 @@ export function LoadStation(props) {
663
664
  const currentSt = useAtomValue(currentStatus);
664
665
  const hideNonLoading = useAtomValue(hideNonLoadingMaterialOnLoadStation);
665
666
  const data = useMemo(() => selectLoadStationAndQueueProps(props.loadNum, props.queues, currentSt, hideNonLoading), [currentSt, props.loadNum, props.queues, hideNonLoading]);
667
+ const arrivalInstruction = loadStationArrivalInstruction(currentSt.basketMoveInstructions ?? [], props.loadNum);
668
+ const [recentArrivalReceipt, setRecentArrivalReceipt] = useState();
669
+ useEffect(() => {
670
+ if (recentArrivalReceipt !== undefined &&
671
+ (recentArrivalReceipt.stationNumber !== props.loadNum ||
672
+ (arrivalInstruction !== undefined &&
673
+ arrivalInstruction.instructionId !== recentArrivalReceipt.instruction.instructionId)))
674
+ setRecentArrivalReceipt(undefined);
675
+ }, [arrivalInstruction, props.loadNum, recentArrivalReceipt]);
676
+ const displayedArrivalInstruction = arrivalInstruction ?? recentArrivalReceipt?.instruction;
677
+ const displayedArrivalReceipt = recentArrivalReceipt?.stationNumber === props.loadNum &&
678
+ displayedArrivalInstruction?.instructionId === recentArrivalReceipt?.instruction.instructionId
679
+ ? recentArrivalReceipt
680
+ : undefined;
666
681
  const queueCols = LazySeq.of(data.queues)
667
682
  .sortBy(([q, _]) => q)
668
683
  .map(([q, mats]) => ({
@@ -709,31 +724,53 @@ export function LoadStation(props) {
709
724
  horizontal: fillViewPort,
710
725
  showMatInCompleted: props.completed,
711
726
  });
712
- return (_jsx(MoveMaterialArrowContainer, { hideArrows: !fillViewPort, whiteBackground: props.whiteBackground, children: _jsxs(Box, { component: "main", sx: {
713
- width: "100%",
714
- display: "grid",
715
- gridTemplate: grid,
716
- minHeight: {
717
- xs: "calc(100vh - 64px - 32px)",
718
- sm: "calc(100vh - 64px - 40px)",
719
- md: "calc(100vh - 64px)",
720
- },
721
- padding: fillViewPort ? undefined : "8px",
722
- }, children: [matCols.map((col, idx) => (_jsx(Box, { "data-testid": materialColumnTestId(col), sx: {
723
- gridArea: `mat${idx}`,
724
- padding: "8px",
725
- borderRight: fillViewPort ? "1px solid black" : undefined,
726
- borderBottom: !fillViewPort ? "1px solid black" : undefined,
727
- }, children: _jsx(MaterialColumn, { data: data, region: col }) }, idx))), data.face.keysToAscLazySeq().map((faceNum, idx) => (_jsx(Box, { sx: {
728
- marginLeft: "15px",
729
- gridArea: `palface${idx}`,
730
- padding: "8px",
731
- borderTop: data.pallet && idx !== 0 ? "1px solid black" : undefined,
732
- }, children: _jsx(PalletFace, { data: data, faceNum: faceNum, loadNum: props.loadNum, submitBasketLoadStationCommand: props.submitBasketLoadStationCommand }) }, faceNum))), _jsx(Box, { "data-testid": "load-station-completed", sx: {
733
- borderLeft: fillViewPort ? "1px solid black" : undefined,
734
- borderTop: !fillViewPort ? "1px solid black" : undefined,
735
- gridArea: "completed",
736
- }, children: _jsx(CompletedCol, { fillViewPort: fillViewPort, showMaterial: props.completed }) }), _jsx(MultiInstructionButton, { loadData: data }), _jsx(SelectWorkorderDialog, {}), _jsx(SelectInspTypeDialog, {}), _jsx(LoadMatDialog, { pallet: data.pallet?.palletNum ?? null, queues: props.queues })] }) }));
727
+ return (_jsxs(_Fragment, { children: [displayedArrivalInstruction === undefined ? null : (_jsx(BasketMovementArrival, { basketName: basketName, instruction: displayedArrivalInstruction, stationNumber: props.loadNum, submitCommand: props.submitBasketMovementCompletion, submitCorrection: props.submitBasketLocationCorrection, receipt: displayedArrivalReceipt, onAccepted: setRecentArrivalReceipt, onCorrected: (command) => setRecentArrivalReceipt((current) => {
728
+ if (current === undefined ||
729
+ current.stationNumber !== props.loadNum ||
730
+ current.receipt.observationId !== command.targetObservationId)
731
+ return current;
732
+ return command.replacementBasketId === null ||
733
+ command.replacementObservationId === null
734
+ ? { ...current, status: "retracted" }
735
+ : {
736
+ ...current,
737
+ receipt: {
738
+ ...current.receipt,
739
+ observationId: command.replacementObservationId,
740
+ observedBasketId: command.replacementBasketId,
741
+ },
742
+ command: {
743
+ ...current.command,
744
+ commandId: command.replacementObservationId,
745
+ observedBasketId: command.replacementBasketId,
746
+ },
747
+ status: "corrected",
748
+ };
749
+ }) })), _jsx(MoveMaterialArrowContainer, { hideArrows: !fillViewPort, whiteBackground: props.whiteBackground, children: _jsxs(Box, { component: "main", sx: {
750
+ width: "100%",
751
+ display: "grid",
752
+ gridTemplate: grid,
753
+ minHeight: {
754
+ xs: "calc(100vh - 64px - 32px)",
755
+ sm: "calc(100vh - 64px - 40px)",
756
+ md: "calc(100vh - 64px)",
757
+ },
758
+ padding: fillViewPort ? undefined : "8px",
759
+ }, children: [matCols.map((col, idx) => (_jsx(Box, { "data-testid": materialColumnTestId(col), sx: {
760
+ gridArea: `mat${idx}`,
761
+ padding: "8px",
762
+ borderRight: fillViewPort ? "1px solid black" : undefined,
763
+ borderBottom: !fillViewPort ? "1px solid black" : undefined,
764
+ }, children: _jsx(MaterialColumn, { data: data, region: col }) }, idx))), data.face.keysToAscLazySeq().map((faceNum, idx) => (_jsx(Box, { sx: {
765
+ marginLeft: "15px",
766
+ gridArea: `palface${idx}`,
767
+ padding: "8px",
768
+ borderTop: data.pallet && idx !== 0 ? "1px solid black" : undefined,
769
+ }, children: _jsx(PalletFace, { data: data, faceNum: faceNum, loadNum: props.loadNum, submitBasketLoadStationCommand: props.submitBasketLoadStationCommand }) }, faceNum))), _jsx(Box, { "data-testid": "load-station-completed", sx: {
770
+ borderLeft: fillViewPort ? "1px solid black" : undefined,
771
+ borderTop: !fillViewPort ? "1px solid black" : undefined,
772
+ gridArea: "completed",
773
+ }, children: _jsx(CompletedCol, { fillViewPort: fillViewPort, showMaterial: props.completed }) }), _jsx(MultiInstructionButton, { loadData: data }), _jsx(SelectWorkorderDialog, {}), _jsx(SelectInspTypeDialog, {}), _jsx(LoadMatDialog, { pallet: data.pallet?.palletNum ?? null, queues: props.queues })] }) })] }));
737
774
  }
738
775
  function LoadStationCheckWidth(props) {
739
776
  const fmsInfo = useAtomValue(fmsInformation);
@@ -9,3 +9,4 @@ export { defaultChooseModes } from "./components/ChooseMode.js";
9
9
  export type { ChooseModeItem } from "./components/ChooseMode.js";
10
10
  export { RouteLocation } from "./components/routes.js";
11
11
  export { authenticatedFetch } from "./network/backend.js";
12
+ export type { BasketMovementCompletionCommand, BasketMovementCompletionReceipt, BasketLocationCorrectionCommand, SubmitBasketLocationCorrection, SubmitBasketMovementCompletion, } from "./components/station-monitor/BasketMovementArrival.js";
package/dist/index.html CHANGED
@@ -43,7 +43,7 @@
43
43
  }
44
44
  }
45
45
  </style>
46
- <script type="module" crossorigin src="/assets/index-gLnatT5a.js"></script>
46
+ <script type="module" crossorigin src="/assets/index-BvhibzJZ.js"></script>
47
47
  <link rel="stylesheet" crossorigin href="/assets/index-DXfAwuj-.css">
48
48
  </head>
49
49
  <body>
@@ -233,6 +233,8 @@ export declare class LogEntry implements ILogEntry {
233
233
  [key: string]: string;
234
234
  } | undefined;
235
235
  tooluse?: ToolUse[] | undefined;
236
+ foreignId?: string | undefined;
237
+ correlationId?: string | undefined;
236
238
  constructor(data?: ILogEntry);
237
239
  init(_data?: any): void;
238
240
  static fromJS(data: any): LogEntry;
@@ -257,6 +259,8 @@ export interface ILogEntry {
257
259
  [key: string]: string;
258
260
  } | undefined;
259
261
  tooluse?: ToolUse[] | undefined;
262
+ foreignId?: string | undefined;
263
+ correlationId?: string | undefined;
260
264
  }
261
265
  export declare class LogMaterial implements ILogMaterial {
262
266
  id: number;
@@ -308,8 +312,14 @@ export declare enum LogType {
308
312
  BasketLoadUnload = "BasketLoadUnload",
309
313
  BasketCycle = "BasketCycle",
310
314
  BasketInLocation = "BasketInLocation",
311
- BasketIdentityHint = "BasketIdentityHint",
312
- BasketContentSnapshot = "BasketContentSnapshot"
315
+ BasketIdentityAssociation = "BasketIdentityAssociation",
316
+ BasketContentSnapshot = "BasketContentSnapshot",
317
+ BasketLocationObservation = "BasketLocationObservation",
318
+ BasketLocationObservationCorrection = "BasketLocationObservationCorrection",
319
+ BasketIdentityAssociationCorrection = "BasketIdentityAssociationCorrection",
320
+ BasketRegionSurvey = "BasketRegionSurvey",
321
+ BasketMisload = "BasketMisload",
322
+ BasketMisloadResolution = "BasketMisloadResolution"
313
323
  }
314
324
  export declare class ToolUse implements IToolUse {
315
325
  tool: string;
@@ -1105,7 +1115,7 @@ export declare enum BasketLocationEnum {
1105
1115
  }
1106
1116
  export declare class BasketMoveInstruction implements IBasketMoveInstruction {
1107
1117
  instructionId: string;
1108
- basketId: number;
1118
+ basketId?: number | undefined;
1109
1119
  source: BasketPosition;
1110
1120
  destination: BasketPosition;
1111
1121
  reason: BasketMoveReason;
@@ -1118,7 +1128,7 @@ export declare class BasketMoveInstruction implements IBasketMoveInstruction {
1118
1128
  }
1119
1129
  export interface IBasketMoveInstruction {
1120
1130
  instructionId: string;
1121
- basketId: number;
1131
+ basketId?: number | undefined;
1122
1132
  source: BasketPosition;
1123
1133
  destination: BasketPosition;
1124
1134
  reason: BasketMoveReason;
@@ -2539,6 +2539,8 @@ export class LogEntry {
2539
2539
  for (let item of _data["tooluse"])
2540
2540
  this.tooluse.push(ToolUse.fromJS(item));
2541
2541
  }
2542
+ this.foreignId = _data["foreignId"];
2543
+ this.correlationId = _data["correlationId"];
2542
2544
  }
2543
2545
  }
2544
2546
  static fromJS(data) {
@@ -2583,6 +2585,8 @@ export class LogEntry {
2583
2585
  for (let item of this.tooluse)
2584
2586
  data["tooluse"].push(item ? item.toJSON() : undefined);
2585
2587
  }
2588
+ data["foreignId"] = this.foreignId;
2589
+ data["correlationId"] = this.correlationId;
2586
2590
  return data;
2587
2591
  }
2588
2592
  }
@@ -2653,8 +2657,14 @@ export var LogType;
2653
2657
  LogType["BasketLoadUnload"] = "BasketLoadUnload";
2654
2658
  LogType["BasketCycle"] = "BasketCycle";
2655
2659
  LogType["BasketInLocation"] = "BasketInLocation";
2656
- LogType["BasketIdentityHint"] = "BasketIdentityHint";
2660
+ LogType["BasketIdentityAssociation"] = "BasketIdentityAssociation";
2657
2661
  LogType["BasketContentSnapshot"] = "BasketContentSnapshot";
2662
+ LogType["BasketLocationObservation"] = "BasketLocationObservation";
2663
+ LogType["BasketLocationObservationCorrection"] = "BasketLocationObservationCorrection";
2664
+ LogType["BasketIdentityAssociationCorrection"] = "BasketIdentityAssociationCorrection";
2665
+ LogType["BasketRegionSurvey"] = "BasketRegionSurvey";
2666
+ LogType["BasketMisload"] = "BasketMisload";
2667
+ LogType["BasketMisloadResolution"] = "BasketMisloadResolution";
2658
2668
  })(LogType || (LogType = {}));
2659
2669
  export class ToolUse {
2660
2670
  constructor(data) {
@@ -5513,11 +5523,11 @@ export class ProgramRevision {
5513
5523
  }
5514
5524
  }
5515
5525
  function formatDate(d) {
5516
- return (d.getUTCFullYear() +
5526
+ return (d.getFullYear() +
5517
5527
  "-" +
5518
- (d.getUTCMonth() < 9 ? "0" + (d.getUTCMonth() + 1) : d.getUTCMonth() + 1) +
5528
+ (d.getMonth() < 9 ? "0" + (d.getMonth() + 1) : d.getMonth() + 1) +
5519
5529
  "-" +
5520
- (d.getUTCDate() < 10 ? "0" + d.getUTCDate() : d.getUTCDate()));
5530
+ (d.getDate() < 10 ? "0" + d.getDate() : d.getDate()));
5521
5531
  }
5522
5532
  export class ApiException extends Error {
5523
5533
  constructor(message, status, response, headers, result) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seedtactics/insight-client",
3
- "version": "17.0.0-beta.1",
3
+ "version": "17.0.0-beta.2",
4
4
  "license": "BSD-3-Clause",
5
5
  "repository": {
6
6
  "url": "https://github.com/SeedTactics/fms-insight"