@seedtactics/insight-client 17.0.1-beta.12 → 17.0.1-beta.14
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 +16 -4
- package/dist/assets/ProgramHighlight-CnZe9PDO.js +3 -0
- package/dist/assets/index-DWZg8nkq.js +367 -0
- package/dist/cell-status/current-status.d.ts +1 -0
- package/dist/cell-status/current-status.js +2 -0
- package/dist/cell-status/loading.d.ts +13 -0
- package/dist/cell-status/loading.js +36 -3
- package/dist/cell-status/material-details.d.ts +2 -2
- package/dist/cell-status/material-details.js +26 -13
- package/dist/components/App.d.ts +0 -5
- package/dist/components/App.js +1 -1
- package/dist/components/analysis/BufferChart.js +3 -2
- package/dist/components/operations/AllMaterial.js +9 -12
- package/dist/components/operations/RecentCycleChart.js +16 -21
- package/dist/components/station-monitor/BasketLoadStationWork.d.ts +1 -7
- package/dist/components/station-monitor/BasketLoadStationWork.js +9 -6
- package/dist/components/station-monitor/CancelLoadButton.js +2 -0
- package/dist/components/station-monitor/InvalidateCycle.js +24 -19
- package/dist/components/station-monitor/LoadStation.d.ts +0 -5
- package/dist/components/station-monitor/LoadStation.js +33 -72
- package/dist/components/station-monitor/Material.js +4 -2
- package/dist/components/station-monitor/PrintedLabel.js +15 -19
- package/dist/components/station-monitor/QuarantineButton.js +17 -26
- package/dist/components/station-monitor/SystemOverview.js +3 -2
- package/dist/components/station-monitor/Whiteboard.js +14 -15
- package/dist/data/material-operation-policy.d.ts +6 -0
- package/dist/data/material-operation-policy.js +26 -2
- package/dist/extension.d.ts +1 -1
- package/dist/extension.js +1 -0
- package/dist/index.html +2 -39
- package/dist/index.js +1 -6
- package/dist/network/api.d.ts +14 -34
- package/dist/network/api.js +62 -64
- package/dist/network/backend-mock.js +3 -0
- package/dist/network/backend.d.ts +1 -0
- package/dist/network/websocket.d.ts +1 -0
- package/dist/network/websocket.js +122 -36
- package/docs/material-quarantine.md +5 -4
- package/docs/mazak.md +9 -12
- package/docs/operator-procedures.md +14 -0
- package/package.json +22 -17
- package/dist/assets/ProgramHighlight-C6zSEXCX.js +0 -3
- package/dist/assets/index-kk-GRa5i.js +0 -388
- package/dist/components/station-monitor/BasketMovementArrival.d.ts +0 -40
- package/dist/components/station-monitor/BasketMovementArrival.js +0 -165
|
@@ -5,6 +5,7 @@ export declare const currentStatus: Atom<ICurrentStatus>;
|
|
|
5
5
|
export declare const secondsSinceEpochAtom: import("jotai").PrimitiveAtom<number> & {
|
|
6
6
|
init: number;
|
|
7
7
|
};
|
|
8
|
+
export declare const minutesSinceEpochAtom: Atom<number>;
|
|
8
9
|
export declare const setJobComment: import("jotai").WritableAtom<null, [uniq: string, newVal: string | null], void> & {
|
|
9
10
|
init: null;
|
|
10
11
|
};
|
|
@@ -51,6 +51,8 @@ secondsSinceEpochAtom.onMount = (setSelf) => {
|
|
|
51
51
|
}, 1000);
|
|
52
52
|
return () => clearInterval(interval);
|
|
53
53
|
};
|
|
54
|
+
// Minute precision keeps report time windows current without redrawing charts every second.
|
|
55
|
+
export const minutesSinceEpochAtom = atom((get) => Math.floor(get(secondsSinceEpochAtom) / 60));
|
|
54
56
|
export const setJobComment = atom(null, (_, set, uniq, newVal) => {
|
|
55
57
|
const newComment = newVal === null ? "" : newVal;
|
|
56
58
|
set(currentStatusRW, (st) => {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ICurrentStatus, IHistoricData, ILogEntry, IRecentHistoricData, IServerEvent } from "../network/api.js";
|
|
2
|
+
import * as currentSt from "./current-status.js";
|
|
2
3
|
import { Atom } from "jotai";
|
|
3
4
|
export interface ServerEventAndTime {
|
|
4
5
|
readonly evt: Readonly<IServerEvent>;
|
|
@@ -6,6 +7,12 @@ export interface ServerEventAndTime {
|
|
|
6
7
|
readonly expire: boolean;
|
|
7
8
|
}
|
|
8
9
|
export declare const lastEventCounter: Atom<number | null>;
|
|
10
|
+
export declare const onLiveServerEvent: import("jotai").WritableAtom<null, [evt: ServerEventAndTime], void> & {
|
|
11
|
+
init: null;
|
|
12
|
+
};
|
|
13
|
+
export declare const onHistoricalServerEvent: import("jotai").WritableAtom<null, [evt: ServerEventAndTime], void> & {
|
|
14
|
+
init: null;
|
|
15
|
+
};
|
|
9
16
|
export declare const onServerEvent: import("jotai").WritableAtom<null, [evt: ServerEventAndTime], void> & {
|
|
10
17
|
init: null;
|
|
11
18
|
};
|
|
@@ -18,6 +25,12 @@ export declare const onLoadLast30Log: import("jotai").WritableAtom<null, [log: r
|
|
|
18
25
|
export declare const onLoadCurrentSt: import("jotai").WritableAtom<null, [curSt: Readonly<ICurrentStatus>], void> & {
|
|
19
26
|
init: null;
|
|
20
27
|
};
|
|
28
|
+
export interface QueuedMatMove extends currentSt.QueueReordering {
|
|
29
|
+
readonly operator: string | null;
|
|
30
|
+
}
|
|
31
|
+
export declare const moveQueuedMatInCurrentStatus: import("jotai").WritableAtom<null, [QueuedMatMove], Promise<void>> & {
|
|
32
|
+
init: null;
|
|
33
|
+
};
|
|
21
34
|
export declare const onLoadSpecificMonthJobs: import("jotai").WritableAtom<null, [historicData: Readonly<IHistoricData>], void> & {
|
|
22
35
|
init: null;
|
|
23
36
|
};
|
|
@@ -30,6 +30,8 @@ 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 { QueuePosition, } from "../network/api.js";
|
|
34
|
+
import { JobsBackend } from "../network/backend.js";
|
|
33
35
|
import { LazySeq } from "@seedtactics/immutable-collections";
|
|
34
36
|
import * as simProd from "./sim-production.js";
|
|
35
37
|
import * as simUse from "./sim-station-use.js";
|
|
@@ -50,16 +52,20 @@ import * as simDayUsage from "./sim-day-usage.js";
|
|
|
50
52
|
import * as rebookings from "./rebookings.js";
|
|
51
53
|
import { atom } from "jotai";
|
|
52
54
|
const lastEventCounterRW = atom(null);
|
|
55
|
+
// Highest counter applied to historical projections. Buffered websocket events do not advance it.
|
|
56
|
+
// This is a reconnect cursor, not proof that lower counters cannot arrive later on the socket.
|
|
53
57
|
export const lastEventCounter = lastEventCounterRW;
|
|
54
|
-
export const
|
|
58
|
+
export const onLiveServerEvent = atom(null, (_, set, evt) => {
|
|
55
59
|
if (evt.evt.newCurrentStatus) {
|
|
56
60
|
set(customSt.replaceCustomState, evt.evt.newCurrentStatus.customState ?? null);
|
|
57
61
|
}
|
|
62
|
+
set(currentSt.updateCurrentStatus, evt);
|
|
63
|
+
});
|
|
64
|
+
export const onHistoricalServerEvent = atom(null, (_, set, evt) => {
|
|
58
65
|
set(simProd.updateLast30JobProduction, evt);
|
|
59
66
|
set(simUse.updateLast30SimStatUse, evt);
|
|
60
67
|
set(schJobs.updateLast30Jobs, evt);
|
|
61
68
|
set(buffers.updateLast30Buffer, evt);
|
|
62
|
-
set(currentSt.updateCurrentStatus, evt);
|
|
63
69
|
set(mats.updateLast30MatSummary, evt);
|
|
64
70
|
set(insp.updateLast30Inspections, evt);
|
|
65
71
|
set(names.updateNames, evt);
|
|
@@ -72,9 +78,13 @@ export const onServerEvent = atom(null, (_, set, evt) => {
|
|
|
72
78
|
set(simDayUsage.updateLatestSimDayUsage, evt);
|
|
73
79
|
if (evt.evt.logEntry) {
|
|
74
80
|
const newCntr = evt.evt.logEntry.counter;
|
|
75
|
-
set(lastEventCounterRW, (old) =>
|
|
81
|
+
set(lastEventCounterRW, (old) => Math.max(old ?? newCntr, newCntr));
|
|
76
82
|
}
|
|
77
83
|
});
|
|
84
|
+
export const onServerEvent = atom(null, (_, set, evt) => {
|
|
85
|
+
set(onLiveServerEvent, evt);
|
|
86
|
+
set(onHistoricalServerEvent, evt);
|
|
87
|
+
});
|
|
78
88
|
export const onLoadLast30Jobs = atom(null, (get, set, historicData) => {
|
|
79
89
|
const filtered = schJobs.filterExistingJobs(get(schJobs.last30SchIds), historicData);
|
|
80
90
|
set(simUse.setLast30SimStatUse, filtered);
|
|
@@ -104,6 +114,29 @@ export const onLoadCurrentSt = atom(null, (_, set, curSt) => {
|
|
|
104
114
|
set(customSt.replaceCustomState, curSt.customState ?? null);
|
|
105
115
|
set(names.setNamesFromCurrentStatus, curSt);
|
|
106
116
|
});
|
|
117
|
+
// Show queue drops immediately. Recovery must not replace intervening live updates or drags.
|
|
118
|
+
export const moveQueuedMatInCurrentStatus = atom(null, async (get, set, { operator, ...move }) => {
|
|
119
|
+
const before = get(currentSt.currentStatus);
|
|
120
|
+
set(currentSt.reorderQueuedMatInCurrentStatus, move);
|
|
121
|
+
const optimistic = get(currentSt.currentStatus);
|
|
122
|
+
try {
|
|
123
|
+
await JobsBackend.setMaterialInQueue(move.matId, operator, new QueuePosition({ queue: move.queue, position: move.newIdx }));
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
const beforeReload = get(currentSt.currentStatus);
|
|
127
|
+
try {
|
|
128
|
+
const refreshed = await JobsBackend.currentStatus();
|
|
129
|
+
if (get(currentSt.currentStatus) === beforeReload)
|
|
130
|
+
set(onLoadCurrentSt, refreshed);
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
// If the server is unreachable, undo only this move's untouched optimistic snapshot.
|
|
134
|
+
if (get(currentSt.currentStatus) === optimistic)
|
|
135
|
+
set(onLoadCurrentSt, before);
|
|
136
|
+
}
|
|
137
|
+
throw error;
|
|
138
|
+
}
|
|
139
|
+
});
|
|
107
140
|
export const onLoadSpecificMonthJobs = atom(null, (_, set, historicData) => {
|
|
108
141
|
set(simUse.setSpecificMonthSimStatUse, historicData);
|
|
109
142
|
set(simProd.setSpecificMonthJobProduction, historicData);
|
|
@@ -44,7 +44,7 @@ export declare const barcodePotentialNewMaterial: Atom<Promise<Readonly<IScanned
|
|
|
44
44
|
export declare const materialInDialogInfo: Atom<Promise<MaterialToShowInfo | null>>;
|
|
45
45
|
export declare const materialInDialogInfoUnwrapped: Atom<MaterialToShowInfo | null>;
|
|
46
46
|
export declare const materialInDialogInfoState: Atom<AsyncAtomState<MaterialToShowInfo | null>>;
|
|
47
|
-
export declare const inProcessMaterialInDialog: Atom<Promise<IInProcessMaterial | null
|
|
47
|
+
export declare const inProcessMaterialInDialog: Atom<IInProcessMaterial | Promise<IInProcessMaterial | null> | null>;
|
|
48
48
|
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>>;
|
|
@@ -61,7 +61,7 @@ export interface MaterialToShowInspections {
|
|
|
61
61
|
readonly completedInspections: ReadonlyArray<string>;
|
|
62
62
|
}
|
|
63
63
|
export declare const materialInDialogInspections: Atom<MaterialToShowInspections>;
|
|
64
|
-
export declare const possibleWorkordersForMaterialInDialog: Atom<Promise<readonly IActiveWorkorder[]
|
|
64
|
+
export declare const possibleWorkordersForMaterialInDialog: Atom<Promise<readonly IActiveWorkorder[]> | readonly IActiveWorkorder[]>;
|
|
65
65
|
export interface ForceInspectionData {
|
|
66
66
|
readonly mat: MaterialToShowInfo;
|
|
67
67
|
readonly inspType: string;
|
|
@@ -116,11 +116,26 @@ function asyncAtomState(source) {
|
|
|
116
116
|
return unwrap(atom((get) => get(source).then((data) => ({ state: "hasData", data }), (error) => ({ state: "hasError", error }))), () => ({ state: "loading" }));
|
|
117
117
|
}
|
|
118
118
|
export const materialInDialogInfoState = asyncAtomState(materialInDialogInfo);
|
|
119
|
-
|
|
120
|
-
|
|
119
|
+
// Some cells publish current status every second. Dialog atoms that combine current status with the
|
|
120
|
+
// dialog material must not suspend on each status update, since that hides the dialog content behind
|
|
121
|
+
// its loading fallback. They compute synchronously once the dialog material is known and suspend
|
|
122
|
+
// only while it is still loading (e.g., a barcode or serial lookup).
|
|
123
|
+
function withDialogMaterial(get, compute) {
|
|
124
|
+
const info = get(materialInDialogInfoState);
|
|
125
|
+
switch (info.state) {
|
|
126
|
+
case "hasData":
|
|
127
|
+
return compute(info.data);
|
|
128
|
+
case "hasError":
|
|
129
|
+
throw info.error;
|
|
130
|
+
case "loading":
|
|
131
|
+
return get(materialInDialogInfo).then(compute);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
export const inProcessMaterialInDialog = atom((get) => {
|
|
121
135
|
const toShow = get(matToShow);
|
|
122
136
|
if (toShow === null)
|
|
123
137
|
return null;
|
|
138
|
+
const status = get(currentStatus);
|
|
124
139
|
if (toShow.type === "InProcMat") {
|
|
125
140
|
return (status.material.find((m) => m.materialID === toShow.inproc.materialID &&
|
|
126
141
|
(m.materialID >= 0 ||
|
|
@@ -130,10 +145,9 @@ export const inProcessMaterialInDialog = atom(async (get) => {
|
|
|
130
145
|
m.action.loadToBasketId === toShow.inproc.action.loadToBasketId &&
|
|
131
146
|
m.jobUnique === toShow.inproc.jobUnique))) ?? null);
|
|
132
147
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
: null;
|
|
148
|
+
return withDialogMaterial(get, (mat) => mat !== null && mat.materialID >= 0
|
|
149
|
+
? (status.material.find((m) => m.materialID === mat.materialID) ?? null)
|
|
150
|
+
: null);
|
|
137
151
|
});
|
|
138
152
|
export const serialInMaterialDialog = atom(async (get) => {
|
|
139
153
|
const toShow = get(matToShow);
|
|
@@ -284,14 +298,13 @@ export const materialInDialogInspections = atom((get) => {
|
|
|
284
298
|
//--------------------------------------------------------------------------------
|
|
285
299
|
// Workorders
|
|
286
300
|
//--------------------------------------------------------------------------------
|
|
287
|
-
export const possibleWorkordersForMaterialInDialog = atom(
|
|
288
|
-
const mat = await get(materialInDialogInfo);
|
|
289
|
-
if (mat === null || mat.partName === "")
|
|
290
|
-
return [];
|
|
301
|
+
export const possibleWorkordersForMaterialInDialog = atom((get) => {
|
|
291
302
|
const works = get(currentStatus)?.workorders ?? [];
|
|
292
|
-
return
|
|
293
|
-
|
|
294
|
-
|
|
303
|
+
return withDialogMaterial(get, (mat) => mat === null || mat.partName === ""
|
|
304
|
+
? []
|
|
305
|
+
: LazySeq.of(works)
|
|
306
|
+
.filter((w) => w.part === mat.partName)
|
|
307
|
+
.toSortedArray((w) => w.dueDate.getTime(), (w) => -w.priority));
|
|
295
308
|
});
|
|
296
309
|
export function useForceInspection() {
|
|
297
310
|
const [updating, setUpdating] = useState(false);
|
package/dist/components/App.d.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { ReactNode, ComponentType } from "react";
|
|
2
2
|
import { ChooseModeItem } from "./ChooseMode.js";
|
|
3
3
|
import * as serverSettings from "../network/server-settings.js";
|
|
4
|
-
import type { SubmitBasketLoadStationCommand } from "./station-monitor/BasketLoadStationWork.js";
|
|
5
|
-
import type { SubmitBasketLocationCorrection, SubmitBasketMovementCompletion } from "./station-monitor/BasketMovementArrival.js";
|
|
6
4
|
export declare function NavTabs({ children }: {
|
|
7
5
|
children?: ReactNode;
|
|
8
6
|
}): import("react").JSX.Element;
|
|
@@ -12,9 +10,6 @@ export interface AppProps {
|
|
|
12
10
|
readonly page: ReactNode;
|
|
13
11
|
};
|
|
14
12
|
readonly chooseModes?: (i: serverSettings.FMSInfoAndUser) => ReadonlyArray<ChooseModeItem> | null;
|
|
15
|
-
readonly submitBasketLoadStationCommand?: SubmitBasketLoadStationCommand;
|
|
16
|
-
readonly submitBasketMovementCompletion?: SubmitBasketMovementCompletion;
|
|
17
|
-
readonly submitBasketLocationCorrection?: SubmitBasketLocationCorrection;
|
|
18
13
|
}
|
|
19
14
|
declare const App: import("react").MemoExoticComponent<(props: AppProps) => import("react").JSX.Element>;
|
|
20
15
|
export default App;
|
package/dist/components/App.js
CHANGED
|
@@ -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
|
|
313
|
+
page = (_jsx(LoadStation, { loadNum: route.loadNum, queues: route.queues, completed: route.completed }, route.loadNum));
|
|
314
314
|
nav1 = StationToolbar;
|
|
315
315
|
nav2 = StationToolbarOverviewButton;
|
|
316
316
|
showOperator = true;
|
|
@@ -74,6 +74,7 @@ const marginBottom = 50;
|
|
|
74
74
|
const marginTop = 20;
|
|
75
75
|
const marginRight = 20;
|
|
76
76
|
function BufferChartSVG({ width, height, series, disabled, }) {
|
|
77
|
+
const [emptyDate] = useState(() => new Date());
|
|
77
78
|
const [dateMin, dateMax] = useMemo(() => {
|
|
78
79
|
let min = null;
|
|
79
80
|
let max = null;
|
|
@@ -87,8 +88,8 @@ function BufferChartSVG({ width, height, series, disabled, }) {
|
|
|
87
88
|
}
|
|
88
89
|
}
|
|
89
90
|
}
|
|
90
|
-
return [min ??
|
|
91
|
-
}, [series]);
|
|
91
|
+
return [min ?? emptyDate, max ?? emptyDate];
|
|
92
|
+
}, [series, emptyDate]);
|
|
92
93
|
const xScale = useMemo(() => scaleTime()
|
|
93
94
|
.range([0, width - marginLeft - marginRight])
|
|
94
95
|
.domain([dateMin, dateMax]), [width, dateMin, dateMax]);
|
|
@@ -33,8 +33,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
33
33
|
*/
|
|
34
34
|
import { forwardRef, memo, PureComponent, useState, useCallback, useMemo, } from "react";
|
|
35
35
|
import { selectAllMaterialIntoBins, MaterialBinType, moveMaterialBin, currentMaterialBinOrder, moveMaterialInBin, findMaterialInQuarantineQueues, findQueueInQuarantineQueues, } from "../../data/all-material-bins.js";
|
|
36
|
-
import * as matDetails from "../../cell-status/material-details.js";
|
|
37
36
|
import * as currentSt from "../../cell-status/current-status.js";
|
|
37
|
+
import { moveQueuedMatInCurrentStatus } from "../../cell-status/loading.js";
|
|
38
38
|
import { Box } from "@mui/material";
|
|
39
39
|
import { Typography } from "@mui/material";
|
|
40
40
|
import { LazySeq } from "@seedtactics/immutable-collections";
|
|
@@ -48,6 +48,7 @@ import { useSetTitle } from "../routes.js";
|
|
|
48
48
|
import { useAtom, useAtomValue, useSetAtom } from "jotai";
|
|
49
49
|
import { fmsInformation } from "../../network/server-settings.js";
|
|
50
50
|
import { basketDisplayName, loadStationDisplayName } from "../../cell-status/station-cycles.js";
|
|
51
|
+
import { canSetMaterialInQueue } from "../../data/material-operation-policy.js";
|
|
51
52
|
const ColumnWithTitle = forwardRef(function MaterialBin(props, ref) {
|
|
52
53
|
return (_jsxs(Box, { sx: {
|
|
53
54
|
margin: props.isDragOverlay ? undefined : "0.75em",
|
|
@@ -187,8 +188,7 @@ export function AllMaterial(props) {
|
|
|
187
188
|
useSetTitle("All Material");
|
|
188
189
|
const st = useAtomValue(currentSt.currentStatus);
|
|
189
190
|
const [matBinOrder, setMatBinOrder] = useAtom(currentMaterialBinOrder);
|
|
190
|
-
const
|
|
191
|
-
const reorderQueuedMat = useSetAtom(currentSt.reorderQueuedMatInCurrentStatus);
|
|
191
|
+
const moveQueuedMat = useSetAtom(moveQueuedMatInCurrentStatus);
|
|
192
192
|
const [activeDrag, setActiveDrag] = useState(null);
|
|
193
193
|
const allBins = useMemo(() => {
|
|
194
194
|
const bins = selectAllMaterialIntoBins(st, matBinOrder);
|
|
@@ -232,6 +232,8 @@ export function AllMaterial(props) {
|
|
|
232
232
|
: findMaterialInQuarantineQueues(over.id, allBins);
|
|
233
233
|
if (!overCol)
|
|
234
234
|
return;
|
|
235
|
+
if (!canSetMaterialInQueue(activeDrag.mat, overCol.bin.queueName))
|
|
236
|
+
return;
|
|
235
237
|
if (overCol.bin.binId !== activeDrag.curOverBinId) {
|
|
236
238
|
setActiveDrag({
|
|
237
239
|
...activeDrag,
|
|
@@ -258,18 +260,13 @@ export function AllMaterial(props) {
|
|
|
258
260
|
const overCol = typeof over.id === "string"
|
|
259
261
|
? findQueueInQuarantineQueues(over.id, allBins)
|
|
260
262
|
: findMaterialInQuarantineQueues(over.id, allBins);
|
|
261
|
-
if (overCol) {
|
|
262
|
-
|
|
263
|
-
materialId: activeDrag.mat.materialID,
|
|
264
|
-
queue: overCol.bin.queueName,
|
|
265
|
-
queuePosition: overCol.idx,
|
|
266
|
-
operator: null,
|
|
267
|
-
}).catch(console.error);
|
|
268
|
-
reorderQueuedMat({
|
|
263
|
+
if (overCol && canSetMaterialInQueue(activeDrag.mat, overCol.bin.queueName)) {
|
|
264
|
+
moveQueuedMat({
|
|
269
265
|
queue: overCol.bin.queueName,
|
|
270
266
|
matId: activeDrag.mat.materialID,
|
|
271
267
|
newIdx: overCol.idx,
|
|
272
|
-
|
|
268
|
+
operator: null,
|
|
269
|
+
}).catch(console.error);
|
|
273
270
|
}
|
|
274
271
|
}
|
|
275
272
|
setActiveDrag(null);
|
|
@@ -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, useCallback,
|
|
34
|
+
import { useMemo, useCallback, useRef } from "react";
|
|
35
35
|
import { last30StationCycles } from "../../cell-status/station-cycles.js";
|
|
36
36
|
import { last30EstimatedCycleTimes } from "../../cell-status/estimated-cycle-times.js";
|
|
37
37
|
import { recentCycles } from "../../data/results.cycles.js";
|
|
@@ -43,7 +43,7 @@ import { localPoint } from "../../util/chart-helpers.js";
|
|
|
43
43
|
import { Stack } from "@mui/material";
|
|
44
44
|
import { Tooltip } from "../ChartTooltip.js";
|
|
45
45
|
import { currentCycles } from "../../data/current-cycles.js";
|
|
46
|
-
import { currentStatus } from "../../cell-status/current-status.js";
|
|
46
|
+
import { currentStatus, minutesSinceEpochAtom } from "../../cell-status/current-status.js";
|
|
47
47
|
import { last30Jobs } from "../../cell-status/scheduled-jobs.js";
|
|
48
48
|
import { atom, useAtomValue, useSetAtom } from "jotai";
|
|
49
49
|
import { fmsInformation } from "../../network/server-settings.js";
|
|
@@ -56,11 +56,12 @@ const occupiedNonOutlierColor = green[900];
|
|
|
56
56
|
const occupiedOutlierColor = red[700];
|
|
57
57
|
const simColor = grey[400];
|
|
58
58
|
const downtimeColor = grey[100];
|
|
59
|
-
|
|
59
|
+
const chartTime = atom((get) => Math.floor(get(minutesSinceEpochAtom) / 5) * 5 * 60000);
|
|
60
|
+
function useSimCycles(now) {
|
|
60
61
|
const jobs = useAtomValue(last30Jobs);
|
|
61
62
|
const statUse = useAtomValue(last30SimStationUse);
|
|
62
63
|
return useMemo(() => {
|
|
63
|
-
const cutoff = addHours(
|
|
64
|
+
const cutoff = addHours(now, -12);
|
|
64
65
|
return (LazySeq.of(statUse)
|
|
65
66
|
.filter((s) => s.end >= cutoff)
|
|
66
67
|
// make sure all planned downtimes come last so they are drawn over the top of any cycles
|
|
@@ -81,7 +82,7 @@ function useSimCycles() {
|
|
|
81
82
|
.toRArray(),
|
|
82
83
|
}))
|
|
83
84
|
.toRArray());
|
|
84
|
-
}, [jobs, statUse]);
|
|
85
|
+
}, [jobs, statUse, now]);
|
|
85
86
|
}
|
|
86
87
|
const tooltipData = atom(null);
|
|
87
88
|
const stationFontSize = 14;
|
|
@@ -213,30 +214,24 @@ function NowLine({ now, xScale, yScale, }) {
|
|
|
213
214
|
export function RecentCycleChart({ height, width }) {
|
|
214
215
|
const last30Cycles = useAtomValue(last30StationCycles);
|
|
215
216
|
const estimated = useAtomValue(last30EstimatedCycleTimes);
|
|
216
|
-
const sim = useSimCycles();
|
|
217
217
|
const currentSt = useAtomValue(currentStatus);
|
|
218
|
+
const time = useAtomValue(chartTime);
|
|
219
|
+
// Keep history filtering independent of frequent status updates.
|
|
220
|
+
const historyNow = useMemo(() => new Date(time), [time]);
|
|
221
|
+
// Align the axis and now marker with cycles started after the chart's last clock tick.
|
|
222
|
+
const displayNow = useMemo(() => new Date(Math.max(time, currentSt.timeOfCurrentStatusUTC.getTime())), [time, currentSt.timeOfCurrentStatusUTC]);
|
|
223
|
+
const sim = useSimCycles(historyNow);
|
|
218
224
|
const fmsInfo = useAtomValue(fmsInformation);
|
|
219
225
|
const cycles = useMemo(() => {
|
|
220
|
-
const cutoff = addHours(
|
|
226
|
+
const cutoff = addHours(historyNow, -12);
|
|
221
227
|
return recentCycles(last30Cycles.valuesToLazySeq().filter((e) => e.endTime >= cutoff));
|
|
222
|
-
}, [last30Cycles]);
|
|
228
|
+
}, [last30Cycles, historyNow]);
|
|
223
229
|
const current = useMemo(() => {
|
|
224
230
|
return currentCycles(currentSt, estimated, fmsInfo.loadStationNames);
|
|
225
231
|
}, [currentSt, estimated, fmsInfo.loadStationNames]);
|
|
226
|
-
|
|
227
|
-
const now = new Date();
|
|
228
|
-
const [, forceRerender] = useState(0);
|
|
229
|
-
const refreshRef = useRef(null);
|
|
230
|
-
useEffect(() => {
|
|
231
|
-
if (refreshRef.current !== null)
|
|
232
|
-
clearTimeout(refreshRef.current);
|
|
233
|
-
refreshRef.current = setTimeout(() => {
|
|
234
|
-
forceRerender((x) => x + 1);
|
|
235
|
-
}, 5 * 60 * 1000);
|
|
236
|
-
});
|
|
237
|
-
const { xScale, yScale, actualPlannedScale, marginLeft } = useScales(cycles, current, now, width, height);
|
|
232
|
+
const { xScale, yScale, actualPlannedScale, marginLeft } = useScales(cycles, current, displayNow, width, height);
|
|
238
233
|
const hideTooltipRef = useRef(null);
|
|
239
234
|
if (height <= 0 || width <= 0)
|
|
240
235
|
return null;
|
|
241
|
-
return (_jsxs("div", { style: { position: "relative", overflow: "hidden" }, children: [_jsx("svg", { height: height, width: width, children: _jsxs("g", { transform: `translate(${marginLeft}, ${marginTop})`, children: [_jsx("clipPath", { id: "recent-cycle-clip-body", children: _jsx("rect", { x: 0, y: 0, width: width - marginRight - marginLeft, height: height - marginBottom - marginTop }) }), _jsx(AxisAndGrid, { xScale: xScale, yScale: yScale }), _jsxs("g", { clipPath: "url(#recent-cycle-clip-body)", children: [_jsx(RecentSeries, { cycles: cycles, xScale: xScale, yScale: yScale, hideTooltipRef: hideTooltipRef, actualPlannedScale: actualPlannedScale }), _jsx(CurrentSeries, { now:
|
|
236
|
+
return (_jsxs("div", { style: { position: "relative", overflow: "hidden" }, children: [_jsx("svg", { height: height, width: width, children: _jsxs("g", { transform: `translate(${marginLeft}, ${marginTop})`, children: [_jsx("clipPath", { id: "recent-cycle-clip-body", children: _jsx("rect", { x: 0, y: 0, width: width - marginRight - marginLeft, height: height - marginBottom - marginTop }) }), _jsx(AxisAndGrid, { xScale: xScale, yScale: yScale }), _jsxs("g", { clipPath: "url(#recent-cycle-clip-body)", children: [_jsx(RecentSeries, { cycles: cycles, xScale: xScale, yScale: yScale, hideTooltipRef: hideTooltipRef, actualPlannedScale: actualPlannedScale }), _jsx(CurrentSeries, { now: displayNow, cycles: current, xScale: xScale, yScale: yScale, hideTooltipRef: hideTooltipRef, actualPlannedScale: actualPlannedScale }), _jsx(SimSeries, { sim: sim, xScale: xScale, yScale: yScale, actualPlannedScale: actualPlannedScale, hideTooltipRef: hideTooltipRef })] }), _jsx(NowLine, { now: displayNow, xScale: xScale, yScale: yScale })] }) }), _jsx(Tooltip, { chartHeight: height, chartWidth: width, atom: tooltipData, TooltipContent: RecentTooltip })] }));
|
|
242
237
|
}
|
|
@@ -1,15 +1,9 @@
|
|
|
1
1
|
import * as api from "../../network/api.js";
|
|
2
2
|
import { MatCardFontSize } from "./Material.js";
|
|
3
|
-
export interface BasketLoadStationCommand {
|
|
4
|
-
readonly workId: string;
|
|
5
|
-
}
|
|
6
|
-
export type SubmitBasketLoadStationCommand = (stationNumber: number, command: BasketLoadStationCommand) => Promise<"accepted" | "conflict">;
|
|
7
3
|
interface BasketLoadStationWorkflowProps {
|
|
8
|
-
readonly stationNumber: number;
|
|
9
4
|
readonly basket: Readonly<api.IBasketStatus>;
|
|
10
5
|
readonly material: ReadonlyArray<Readonly<api.IInProcessMaterial>>;
|
|
11
6
|
readonly fsize: MatCardFontSize;
|
|
12
|
-
readonly submitCommand: SubmitBasketLoadStationCommand | undefined;
|
|
13
7
|
}
|
|
14
|
-
export declare function BasketLoadStationWorkflow({
|
|
8
|
+
export declare function BasketLoadStationWorkflow({ basket, material, fsize, }: BasketLoadStationWorkflowProps): import("react").JSX.Element;
|
|
15
9
|
export {};
|
|
@@ -33,6 +33,7 @@ import { Alert, Box, Button, Stack, Typography } from "@mui/material";
|
|
|
33
33
|
import { LazySeq } from "@seedtactics/immutable-collections";
|
|
34
34
|
import { basketSlotLabel } from "../../cell-status/station-cycles.js";
|
|
35
35
|
import * as api from "../../network/api.js";
|
|
36
|
+
import { JobsBackend } from "../../network/backend.js";
|
|
36
37
|
import { InProcMaterial } from "./Material.js";
|
|
37
38
|
import { MoveMaterialArrowNode } from "./MoveMaterialArrows.js";
|
|
38
39
|
import { MoveMaterialNodeKindType } from "../../data/move-arrows.js";
|
|
@@ -138,7 +139,7 @@ function loadingInstruction(mat) {
|
|
|
138
139
|
function SlotMaterial({ material, fsize, }) {
|
|
139
140
|
return material.map((mat) => (_jsx(MoveMaterialArrowNode, { kind: { type: MoveMaterialNodeKindType.Material, material: mat }, children: _jsx(InProcMaterial, { mat: mat, fsize: fsize }) }, mat.materialID)));
|
|
140
141
|
}
|
|
141
|
-
export function BasketLoadStationWorkflow({
|
|
142
|
+
export function BasketLoadStationWorkflow({ basket, material, fsize, }) {
|
|
142
143
|
const [submission, setSubmission] = useState();
|
|
143
144
|
const workState = useMemo(() => stationWork(material, basket), [basket, material]);
|
|
144
145
|
const blockedReason = basket.loadStationWork?.confirmationBlockedReason?.trim();
|
|
@@ -170,17 +171,19 @@ export function BasketLoadStationWorkflow({ stationNumber, basket, material, fsi
|
|
|
170
171
|
.distinctBy((slot) => slot)
|
|
171
172
|
.toSortedArray((slot) => slot), [basket.emptySlots, basket.unknownSlots, materialBySlot, loadsBySlot]);
|
|
172
173
|
async function submit() {
|
|
173
|
-
if (work === undefined || !work.ready
|
|
174
|
+
if (work === undefined || !work.ready)
|
|
174
175
|
return;
|
|
175
176
|
const submittedWorkId = work.workId;
|
|
176
177
|
const pending = { workId: submittedWorkId, basket, material, state: "submitting" };
|
|
177
178
|
setSubmission(pending);
|
|
178
179
|
try {
|
|
179
|
-
|
|
180
|
+
await JobsBackend.completeBasketLoadStation(new api.BasketLoadStationCompletion({ workId: submittedWorkId }));
|
|
181
|
+
const state = "accepted";
|
|
180
182
|
setSubmission((current) => (current === pending ? { ...pending, state } : current));
|
|
181
183
|
}
|
|
182
|
-
catch {
|
|
183
|
-
|
|
184
|
+
catch (error) {
|
|
185
|
+
const state = api.ApiException.isApiException(error) && error.status === 409 ? "conflict" : "error";
|
|
186
|
+
setSubmission((current) => (current === pending ? { ...pending, state } : current));
|
|
184
187
|
}
|
|
185
188
|
}
|
|
186
189
|
const submissionDisabled = !work?.ready ||
|
|
@@ -205,5 +208,5 @@ export function BasketLoadStationWorkflow({ stationNumber, basket, material, fsi
|
|
|
205
208
|
}) }), work?.confirmEmpty && _jsxs(Typography, { children: ["Confirm basket ", basket.basketId, " is empty."] }), work && !work.ready && (_jsx(Alert, { severity: "info", children: blockedReason ||
|
|
206
209
|
(work.awaitingSlots.length > 0
|
|
207
210
|
? `Waiting for material for slots ${work.awaitingSlots.map(basketSlotLabel).join(", ")}.`
|
|
208
|
-
: "Basket work is not ready for confirmation.") })), work
|
|
211
|
+
: "Basket work is not ready for confirmation.") })), work ? (_jsx(Box, { sx: { display: "flex", justifyContent: "flex-end" }, children: _jsx(Button, { variant: "contained", disabled: submissionDisabled, onClick: () => void submit(), children: "Confirm" }) })) : 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] }));
|
|
209
212
|
}
|
|
@@ -41,6 +41,8 @@ export function CancelLoadButton({ onClose, ignoreOperator = false, }) {
|
|
|
41
41
|
expectedLoadCancellationId: snapshot.cancellationId,
|
|
42
42
|
reason: reason || undefined,
|
|
43
43
|
}));
|
|
44
|
+
setOpen(false);
|
|
45
|
+
setSnapshot(null);
|
|
44
46
|
setMaterialDialogOpen(null);
|
|
45
47
|
onClose?.();
|
|
46
48
|
}
|
|
@@ -41,7 +41,7 @@ import { currentStatus } from "../../cell-status/current-status.js";
|
|
|
41
41
|
import { barcodePotentialNewMaterial, inProcessMaterialInDialog, materialDialogOpen, materialInDialogInfo, materialInDialogLargestUsedProcess, materialInDialogLocalEvents, } from "../../cell-status/material-details.js";
|
|
42
42
|
import { currentOperator } from "../../data/operators.js";
|
|
43
43
|
import { fmsInformation } from "../../network/server-settings.js";
|
|
44
|
-
import { useAtomValue, useSetAtom } from "jotai";
|
|
44
|
+
import { useAtomValue, useAtomValueRawSync, useSetAtom } from "jotai";
|
|
45
45
|
import { useEffect, useRef } from "react";
|
|
46
46
|
import { last30Jobs } from "../../cell-status/scheduled-jobs.js";
|
|
47
47
|
import { PartIdenticon } from "./Material.js";
|
|
@@ -82,12 +82,15 @@ function InvalidateSelect(props) {
|
|
|
82
82
|
job: currentSt.jobs[j.jobUnique] ?? history.get(j.jobUnique) ?? null,
|
|
83
83
|
}));
|
|
84
84
|
const hasProc = lastMat !== null && lastMat.process >= 1;
|
|
85
|
-
const
|
|
85
|
+
const canChangeAssignment = lastMat === null || lastMat.process <= 1;
|
|
86
|
+
const hasChangeMat = canChangeAssignment && (castings.length > 0 || jobs.length > 0);
|
|
86
87
|
const selectedCasting = props.st?.changeRawMat !== null &&
|
|
87
88
|
props.st?.changeRawMat !== undefined &&
|
|
89
|
+
canChangeAssignment &&
|
|
88
90
|
castings.includes(props.st.changeRawMat);
|
|
89
91
|
const selectedJob = props.st?.changeJobUnique !== null &&
|
|
90
92
|
props.st?.changeJobUnique !== undefined &&
|
|
93
|
+
canChangeAssignment &&
|
|
91
94
|
jobs.some((job) => job.jobUnique === props.st?.changeJobUnique);
|
|
92
95
|
function change(e) {
|
|
93
96
|
props.setState({
|
|
@@ -102,7 +105,7 @@ function InvalidateSelect(props) {
|
|
|
102
105
|
? "rawMat" + props.st.changeRawMat
|
|
103
106
|
: selectedJob
|
|
104
107
|
? "job" + props.st.changeJobUnique
|
|
105
|
-
: props.st?.process && hasProc
|
|
108
|
+
: props.st?.process && hasProc && props.st.process === lastMat.process
|
|
106
109
|
? "proc" + props.st.process.toString()
|
|
107
110
|
: "", select: true, onChange: (e) => change(e.target.value), variant: "outlined", label: props.st?.changeRawMat
|
|
108
111
|
? "Change Raw Material"
|
|
@@ -119,11 +122,6 @@ function InvalidateSelect(props) {
|
|
|
119
122
|
hasProc
|
|
120
123
|
? [
|
|
121
124
|
_jsxs(MenuItem, { value: "proc" + lastMat.process.toString(), children: ["Invalidate Process ", lastMat.process] }, lastMat.process),
|
|
122
|
-
...(lastMat.process > 1
|
|
123
|
-
? [
|
|
124
|
-
_jsx(MenuItem, { value: "proc1", children: "Invalidate All Processes" }, "all"),
|
|
125
|
-
]
|
|
126
|
-
: []),
|
|
127
125
|
]
|
|
128
126
|
: [],
|
|
129
127
|
hasProc && hasChangeMat
|
|
@@ -131,15 +129,17 @@ function InvalidateSelect(props) {
|
|
|
131
129
|
: [],
|
|
132
130
|
hasChangeMat
|
|
133
131
|
? [
|
|
134
|
-
...castings.map((c) => (_jsxs(MenuItem, { value: "rawMat" + c, children: ["Invalidate
|
|
135
|
-
...jobs.map(({ jobUnique, job }) => (_jsxs(MenuItem, { value: "job" + jobUnique, children: [job ? (_jsx(ListItemIcon, { children: _jsx(PartIdenticon, { part: job.partName }) })) : undefined, _jsx(ListItemText, { primary: "Invalidate
|
|
132
|
+
...castings.map((c) => (_jsxs(MenuItem, { value: "rawMat" + c, children: [hasProc ? "Invalidate Process 1 and Change to " : "Change to ", c] }, "rawMat" + c))),
|
|
133
|
+
...jobs.map(({ jobUnique, job }) => (_jsxs(MenuItem, { value: "job" + jobUnique, children: [job ? (_jsx(ListItemIcon, { children: _jsx(PartIdenticon, { part: job.partName }) })) : undefined, _jsx(ListItemText, { primary: (hasProc ? "Invalidate Process 1 and Change to Job " : "Change to Job ") +
|
|
134
|
+
jobUnique, secondary: job?.partName })] }, "job" + jobUnique))),
|
|
136
135
|
]
|
|
137
136
|
: [],
|
|
138
137
|
] }));
|
|
139
138
|
}
|
|
140
139
|
export function InvalidateCycleDialogContent(props) {
|
|
141
140
|
const curMat = useAtomValue(materialInDialogInfo);
|
|
142
|
-
|
|
141
|
+
// History can resolve while InvalidateSelect suspends, before this component subscribes.
|
|
142
|
+
const events = useAtomValueRawSync(materialInDialogLocalEvents);
|
|
143
143
|
const status = useAtomValue(currentStatus);
|
|
144
144
|
const show = props.st !== null && curMat !== null;
|
|
145
145
|
const boxRef = useRef(null);
|
|
@@ -182,13 +182,15 @@ export function InvalidateCycleDialogButton(props) {
|
|
|
182
182
|
return null;
|
|
183
183
|
if (!canInvalidateMaterial(inProcMat))
|
|
184
184
|
return null;
|
|
185
|
-
const allowChange =
|
|
185
|
+
const allowChange = (lastMat === null || lastMat.process <= 1) &&
|
|
186
|
+
possibleNew !== null &&
|
|
186
187
|
(!LazySeq.ofObject(possibleNew.possibleCastingsByQueue ?? {}).isEmpty() ||
|
|
187
188
|
!LazySeq.ofObject(possibleNew.possibleJobsByQueue ?? {}).isEmpty());
|
|
188
189
|
if (!allowChange && (lastMat === null || lastMat.process < 1))
|
|
189
190
|
return null;
|
|
190
191
|
if (props.ignoreOperator)
|
|
191
192
|
operator = null;
|
|
193
|
+
const changingAssignment = props.st !== null && (props.st.changeRawMat !== null || props.st.changeJobUnique !== null);
|
|
192
194
|
function showInvalidate() {
|
|
193
195
|
props.setState({
|
|
194
196
|
process: null,
|
|
@@ -229,17 +231,20 @@ export function InvalidateCycleDialogButton(props) {
|
|
|
229
231
|
}
|
|
230
232
|
}
|
|
231
233
|
return (_jsxs(_Fragment, { children: [props.st === null ? (_jsx(Button, { color: "primary", onClick: showInvalidate, children: "Invalidate Cycle" })) : undefined, props.st !== null ? (_jsx(Button, { color: "primary", onClick: invalidateCycle, disabled: props.st.updating ||
|
|
234
|
+
(changingAssignment && !allowChange) ||
|
|
235
|
+
(!changingAssignment &&
|
|
236
|
+
props.st.process !== null &&
|
|
237
|
+
props.st.process !== lastMat?.process) ||
|
|
232
238
|
(props.st.process === null &&
|
|
233
239
|
props.st.changeRawMat === null &&
|
|
234
240
|
props.st.changeJobUnique === null), children: props.st.changeJobUnique !== null
|
|
235
|
-
?
|
|
241
|
+
? (lastMat?.process === 1
|
|
242
|
+
? "Invalidate Process 1 and Change to Job "
|
|
243
|
+
: "Change to Job ") + props.st.changeJobUnique
|
|
236
244
|
: props.st.changeRawMat !== null
|
|
237
|
-
? "Invalidate
|
|
245
|
+
? (lastMat?.process === 1 ? "Invalidate Process 1 and Change to " : "Change to ") +
|
|
246
|
+
props.st.changeRawMat
|
|
238
247
|
: props.st.process !== null
|
|
239
|
-
? props.st.process
|
|
240
|
-
? lastMat && lastMat.process > 1
|
|
241
|
-
? "Invalidate All Processes"
|
|
242
|
-
: "Invalidate Process 1"
|
|
243
|
-
: "Invalidate Process " + props.st.process.toString()
|
|
248
|
+
? "Invalidate Process " + props.st.process.toString()
|
|
244
249
|
: "Invalidate Cycle" })) : undefined] }));
|
|
245
250
|
}
|
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
import { ReactNode } from "react";
|
|
2
|
-
import { SubmitBasketLoadStationCommand } from "./BasketLoadStationWork.js";
|
|
3
|
-
import { SubmitBasketLocationCorrection, SubmitBasketMovementCompletion } from "./BasketMovementArrival.js";
|
|
4
2
|
interface LoadStationProps {
|
|
5
3
|
readonly loadNum: number;
|
|
6
4
|
readonly queues: ReadonlyArray<string>;
|
|
7
5
|
readonly completed: boolean;
|
|
8
6
|
readonly whiteBackground?: boolean;
|
|
9
|
-
readonly submitBasketLoadStationCommand?: SubmitBasketLoadStationCommand;
|
|
10
|
-
readonly submitBasketMovementCompletion?: SubmitBasketMovementCompletion;
|
|
11
|
-
readonly submitBasketLocationCorrection?: SubmitBasketLocationCorrection;
|
|
12
7
|
}
|
|
13
8
|
export declare function LoadStation(props: LoadStationProps): import("react").JSX.Element;
|
|
14
9
|
declare function LoadStationCheckWidth(props: LoadStationProps): ReactNode;
|