@seedtactics/insight-client 16.6.0 → 16.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/ProgramHighlight-DPTeZ8Si.js +3 -0
- package/dist/assets/index-DL7cXxkt.js +364 -0
- package/dist/cell-status/basket-cycles.d.ts +21 -0
- package/dist/cell-status/basket-cycles.js +80 -0
- package/dist/cell-status/current-status.js +11 -3
- package/dist/cell-status/estimated-cycle-times.js +8 -4
- package/dist/cell-status/inspections.js +2 -2
- package/dist/cell-status/loading.js +4 -0
- package/dist/cell-status/material-details.d.ts +12 -4
- package/dist/cell-status/material-details.js +24 -13
- package/dist/cell-status/rebookings.js +15 -17
- package/dist/cell-status/scheduled-jobs.d.ts +1 -1
- package/dist/cell-status/scheduled-jobs.js +10 -3
- package/dist/cell-status/sim-production.js +3 -3
- package/dist/cell-status/sim-station-use.d.ts +1 -0
- package/dist/cell-status/sim-station-use.js +14 -8
- package/dist/cell-status/station-cycles.d.ts +29 -2
- package/dist/cell-status/station-cycles.js +81 -11
- package/dist/cell-status/tool-replacements.js +1 -1
- package/dist/cell-status/tool-usage.js +1 -1
- package/dist/components/App.js +101 -66
- package/dist/components/BarcodeScanning.js +12 -2
- package/dist/components/ErrorsAndLoading.js +10 -1
- package/dist/components/LogEntry.d.ts +0 -1
- package/dist/components/LogEntry.js +50 -26
- package/dist/components/Navigation.js +30 -4
- package/dist/components/analysis/AnalysisSelectToolbar.js +5 -1
- package/dist/components/analysis/BasketCycleCards.d.ts +1 -0
- package/dist/components/analysis/BasketCycleCards.js +145 -0
- package/dist/components/analysis/BufferChart.js +10 -4
- package/dist/components/analysis/CostPerPiece.js +28 -8
- package/dist/components/analysis/CycleChart.js +1 -1
- package/dist/components/analysis/DataTable.js +6 -4
- package/dist/components/analysis/HeatChart.js +27 -14
- package/dist/components/analysis/InspectionSankey.js +17 -6
- package/dist/components/analysis/PalletCycleCards.js +15 -4
- package/dist/components/analysis/PartCycleCards.js +62 -18
- package/dist/components/analysis/StationDataTable.js +14 -11
- package/dist/components/analysis/ToolReplacements.js +16 -10
- package/dist/components/log-entry-queue-filter.d.ts +2 -0
- package/dist/components/log-entry-queue-filter.js +21 -0
- package/dist/components/operations/AllMaterial.js +26 -10
- package/dist/components/operations/ChartRangeEdit.js +82 -4
- package/dist/components/operations/CloseoutReport.js +13 -4
- package/dist/components/operations/CompletedParts.js +23 -11
- package/dist/components/operations/CurrentWorkorders.js +31 -9
- package/dist/components/operations/OEEChart.js +8 -2
- package/dist/components/operations/Outliers.js +18 -7
- package/dist/components/operations/ProgramHighlight.js +4 -6
- package/dist/components/operations/Programs.js +9 -3
- package/dist/components/operations/Rebookings.js +8 -4
- package/dist/components/operations/RecentCycleChart.js +5 -3
- package/dist/components/operations/RecentProduction.js +30 -13
- package/dist/components/operations/RecentSchedules.js +6 -2
- package/dist/components/operations/RecentStationCycles.js +38 -11
- package/dist/components/operations/ShiftSettings.js +3 -3
- package/dist/components/operations/SimDayUsage.js +27 -4
- package/dist/components/operations/ToolReport.js +5 -1
- package/dist/components/operations/WorkorderGantt.js +22 -2
- package/dist/components/quality/QualityMaterial.js +11 -8
- package/dist/components/quality/RecentFailedInspections.js +3 -1
- package/dist/components/routes.d.ts +3 -0
- package/dist/components/routes.js +2 -0
- package/dist/components/station-monitor/BulkRawMaterial.js +11 -7
- package/dist/components/station-monitor/Closeout.js +14 -2
- package/dist/components/station-monitor/CustomStationMonitorDialog.js +1 -1
- package/dist/components/station-monitor/Inspection.js +23 -11
- package/dist/components/station-monitor/InvalidateCycle.js +3 -3
- package/dist/components/station-monitor/JobDetails.js +15 -2
- package/dist/components/station-monitor/LoadStation.js +274 -31
- package/dist/components/station-monitor/Material.js +71 -11
- package/dist/components/station-monitor/MoveMaterialArrows.js +4 -4
- package/dist/components/station-monitor/QuarantineButton.js +11 -0
- package/dist/components/station-monitor/Queues.js +28 -9
- package/dist/components/station-monitor/QueuesAddMaterial.js +8 -6
- package/dist/components/station-monitor/SelectInspType.js +1 -1
- package/dist/components/station-monitor/SelectWorkorder.js +1 -1
- package/dist/components/station-monitor/StationToolbar.js +17 -5
- package/dist/components/station-monitor/SystemOverview.d.ts +19 -1
- package/dist/components/station-monitor/SystemOverview.js +439 -20
- package/dist/components/station-monitor/Whiteboard.js +15 -7
- package/dist/data/all-material-bins.d.ts +7 -0
- package/dist/data/all-material-bins.js +47 -1
- package/dist/data/cost-per-piece.js +11 -8
- package/dist/data/current-cycles.d.ts +1 -1
- package/dist/data/current-cycles.js +62 -17
- package/dist/data/move-arrows.d.ts +5 -1
- package/dist/data/move-arrows.js +54 -4
- package/dist/data/part-summary.js +13 -13
- package/dist/data/path-lookup.js +6 -23
- package/dist/data/queue-material.d.ts +1 -1
- package/dist/data/queue-material.js +18 -17
- package/dist/data/results.completed-parts.js +4 -3
- package/dist/data/results.cycles.d.ts +15 -6
- package/dist/data/results.cycles.js +51 -30
- package/dist/data/results.inspection.js +11 -6
- package/dist/data/results.oee.js +8 -8
- package/dist/data/results.schedules.js +2 -11
- package/dist/data/tools-programs.js +5 -6
- package/dist/index.html +1 -1
- package/dist/network/api.d.ts +22 -4
- package/dist/network/api.js +40 -5
- package/dist/network/backend-mock.js +15 -8
- package/dist/network/backend.js +3 -3
- package/dist/network/websocket.js +15 -15
- package/dist/util/chart-helpers.d.ts +1 -1
- package/dist/util/chart-helpers.js +14 -8
- package/package.json +30 -32
- package/dist/assets/ProgramHighlight-LvRM40Qr.js +0 -3
- package/dist/assets/index-gAFi3Oss.js +0 -364
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ILogEntry } from "../network/api.js";
|
|
2
|
+
import { HashMap } from "@seedtactics/immutable-collections";
|
|
3
|
+
import type { ServerEventAndTime } from "./loading.js";
|
|
4
|
+
import { Atom } from "jotai";
|
|
5
|
+
import type { PalletCycleData } from "./pallet-cycles.js";
|
|
6
|
+
export type BasketCycleData = PalletCycleData;
|
|
7
|
+
export type BasketCyclesByCntr = HashMap<number, BasketCycleData>;
|
|
8
|
+
export type BasketCyclesByBasket = HashMap<number, BasketCyclesByCntr>;
|
|
9
|
+
export declare const last30BasketCycles: Atom<BasketCyclesByBasket>;
|
|
10
|
+
export declare const last30HasBasketCycleData: Atom<boolean>;
|
|
11
|
+
export declare const specificMonthBasketCycles: Atom<BasketCyclesByBasket>;
|
|
12
|
+
export declare const specificMonthHasBasketCycleData: Atom<boolean>;
|
|
13
|
+
export declare const setLast30BasketCycles: import("jotai").WritableAtom<null, [log: readonly Readonly<ILogEntry>[]], void> & {
|
|
14
|
+
init: null;
|
|
15
|
+
};
|
|
16
|
+
export declare const updateLast30BasketCycles: import("jotai").WritableAtom<null, [ServerEventAndTime], void> & {
|
|
17
|
+
init: null;
|
|
18
|
+
};
|
|
19
|
+
export declare const setSpecificMonthBasketCycles: import("jotai").WritableAtom<null, [log: readonly Readonly<ILogEntry>[]], void> & {
|
|
20
|
+
init: null;
|
|
21
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/* Copyright (c) 2026, John Lenz
|
|
2
|
+
|
|
3
|
+
All rights reserved.
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
* Redistributions of source code must retain the above copyright
|
|
9
|
+
notice, this list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
* Redistributions in binary form must reproduce the above
|
|
12
|
+
copyright notice, this list of conditions and the following
|
|
13
|
+
disclaimer in the documentation and/or other materials provided
|
|
14
|
+
with the distribution.
|
|
15
|
+
|
|
16
|
+
* Neither the name of John Lenz, Black Maple Software, SeedTactics,
|
|
17
|
+
nor the names of other contributors may be used to endorse or
|
|
18
|
+
promote products derived from this software without specific
|
|
19
|
+
prior written permission.
|
|
20
|
+
|
|
21
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
22
|
+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
23
|
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
24
|
+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
25
|
+
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
26
|
+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
27
|
+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
28
|
+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
29
|
+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
30
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
31
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
32
|
+
*/
|
|
33
|
+
import { addDays } from "date-fns";
|
|
34
|
+
import { LogType } from "../network/api.js";
|
|
35
|
+
import { HashMap, LazySeq } from "@seedtactics/immutable-collections";
|
|
36
|
+
import { durationToMinutes } from "../util/parseISODuration.js";
|
|
37
|
+
import { atom } from "jotai";
|
|
38
|
+
const last30BasketCyclesRW = atom(HashMap.empty());
|
|
39
|
+
export const last30BasketCycles = last30BasketCyclesRW;
|
|
40
|
+
export const last30HasBasketCycleData = atom((get) => get(last30BasketCyclesRW).size > 0);
|
|
41
|
+
const specificMonthBasketCyclesRW = atom(HashMap.empty());
|
|
42
|
+
export const specificMonthBasketCycles = specificMonthBasketCyclesRW;
|
|
43
|
+
export const specificMonthHasBasketCycleData = atom((get) => get(specificMonthBasketCyclesRW).size > 0);
|
|
44
|
+
function logToBasketCycle(c) {
|
|
45
|
+
return {
|
|
46
|
+
cntr: c.counter,
|
|
47
|
+
x: c.endUTC,
|
|
48
|
+
y: durationToMinutes(c.elapsed),
|
|
49
|
+
active: durationToMinutes(c.active),
|
|
50
|
+
mats: c.material,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
export const setLast30BasketCycles = atom(null, (_, set, log) => {
|
|
54
|
+
set(last30BasketCyclesRW, (oldCycles) => oldCycles.union(LazySeq.of(log)
|
|
55
|
+
.filter((c) => !c.startofcycle && c.type === LogType.BasketCycle && c.pal !== 0)
|
|
56
|
+
.toLookupMap((c) => c.pal, (c) => c.counter, logToBasketCycle), (e1, e2) => e1.union(e2)));
|
|
57
|
+
});
|
|
58
|
+
export const updateLast30BasketCycles = atom(null, (_, set, { evt, now, expire }) => {
|
|
59
|
+
if (evt.logEntry &&
|
|
60
|
+
!evt.logEntry.startofcycle &&
|
|
61
|
+
evt.logEntry.type === LogType.BasketCycle &&
|
|
62
|
+
evt.logEntry.pal !== 0) {
|
|
63
|
+
const log = evt.logEntry;
|
|
64
|
+
set(last30BasketCyclesRW, (oldCycles) => {
|
|
65
|
+
if (expire) {
|
|
66
|
+
const thirtyDaysAgo = addDays(now, -30);
|
|
67
|
+
oldCycles = oldCycles.collectValues((es) => {
|
|
68
|
+
const newEs = es.filter((e) => e.x >= thirtyDaysAgo);
|
|
69
|
+
return newEs.size > 0 ? newEs : null;
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return oldCycles.modify(log.pal, (old) => (old ?? HashMap.empty()).set(log.counter, logToBasketCycle(log)));
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
export const setSpecificMonthBasketCycles = atom(null, (_, set, log) => {
|
|
77
|
+
set(specificMonthBasketCyclesRW, LazySeq.of(log)
|
|
78
|
+
.filter((c) => !c.startofcycle && c.type === LogType.BasketCycle && c.pal !== 0)
|
|
79
|
+
.toLookupMap((c) => c.pal, (c) => c.counter, logToBasketCycle));
|
|
80
|
+
});
|
|
@@ -31,7 +31,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
31
31
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
32
32
|
*/
|
|
33
33
|
import { JobsBackend, LogBackend } from "../network/backend.js";
|
|
34
|
-
import { InProcessMaterial, LogType, LocType, ActiveJob, WorkorderComment, ActiveWorkorder, } from "../network/api.js";
|
|
34
|
+
import { InProcessMaterial, LogType, LocType, ActiveJob, WorkorderComment, ActiveWorkorder, InProcessMaterialLocation, } from "../network/api.js";
|
|
35
35
|
import { last30JobComment } from "./scheduled-jobs.js";
|
|
36
36
|
import { atom } from "jotai";
|
|
37
37
|
import { currentOperator } from "../data/operators.js";
|
|
@@ -173,7 +173,12 @@ export const reorderQueuedMatInCurrentStatus = atom(null, (_, set, { queue, matI
|
|
|
173
173
|
if (m.materialID === matId) {
|
|
174
174
|
return new InProcessMaterial({
|
|
175
175
|
...m,
|
|
176
|
-
location: {
|
|
176
|
+
location: new InProcessMaterialLocation({
|
|
177
|
+
...m.location,
|
|
178
|
+
type: LocType.InQueue,
|
|
179
|
+
currentQueue: queue,
|
|
180
|
+
queuePosition: newIdx,
|
|
181
|
+
}),
|
|
177
182
|
});
|
|
178
183
|
}
|
|
179
184
|
let idx = m.location.queuePosition;
|
|
@@ -188,7 +193,10 @@ export const reorderQueuedMatInCurrentStatus = atom(null, (_, set, { queue, matI
|
|
|
188
193
|
if (idx !== m.location.queuePosition) {
|
|
189
194
|
return new InProcessMaterial({
|
|
190
195
|
...m,
|
|
191
|
-
location: {
|
|
196
|
+
location: new InProcessMaterialLocation({
|
|
197
|
+
...m.location,
|
|
198
|
+
queuePosition: idx,
|
|
199
|
+
}),
|
|
192
200
|
});
|
|
193
201
|
}
|
|
194
202
|
else {
|
|
@@ -42,12 +42,14 @@ export class PartAndStationOperation {
|
|
|
42
42
|
this.operation = operation;
|
|
43
43
|
}
|
|
44
44
|
static ofLogCycle(c) {
|
|
45
|
-
return new PartAndStationOperation(c.material[0].part, c.loc, c.type === LogType.LoadUnloadCycle && c.material.length > 0
|
|
45
|
+
return new PartAndStationOperation(c.material[0].part, c.loc, (c.type === LogType.LoadUnloadCycle || c.type === LogType.BasketLoadUnload) && c.material.length > 0
|
|
46
46
|
? c.result + "-" + c.material[0].proc.toString()
|
|
47
47
|
: c.program);
|
|
48
48
|
}
|
|
49
49
|
static ofPartCycle(c) {
|
|
50
|
-
return new PartAndStationOperation(c.part, c.stationGroup, c.
|
|
50
|
+
return new PartAndStationOperation(c.part, c.stationGroup, c.carrier.kind !== "machining" && c.material.length > 0
|
|
51
|
+
? c.operation + "-" + c.material[0].proc.toString()
|
|
52
|
+
: c.operation);
|
|
51
53
|
}
|
|
52
54
|
compare(other) {
|
|
53
55
|
let cmp = this.part.localeCompare(other.part);
|
|
@@ -99,7 +101,7 @@ export function isOutlierAbove(s, mins) {
|
|
|
99
101
|
}
|
|
100
102
|
}
|
|
101
103
|
function median(vals) {
|
|
102
|
-
const sorted = vals.
|
|
104
|
+
const sorted = vals.toSortedArray((x) => x);
|
|
103
105
|
const cnt = sorted.length;
|
|
104
106
|
if (cnt === 0) {
|
|
105
107
|
return 0;
|
|
@@ -222,7 +224,9 @@ export function calcElapsedForCycles(eventLog) {
|
|
|
222
224
|
}
|
|
223
225
|
}
|
|
224
226
|
}
|
|
225
|
-
return chunkCyclesWithSimilarEndTime(LazySeq.of(eventLog).filter((e) => (e.type === LogType.LoadUnloadCycle ||
|
|
227
|
+
return chunkCyclesWithSimilarEndTime(LazySeq.of(eventLog).filter((e) => (e.type === LogType.LoadUnloadCycle ||
|
|
228
|
+
e.type === LogType.BasketLoadUnload ||
|
|
229
|
+
e.type === LogType.MachineCycle) &&
|
|
226
230
|
!e.startofcycle &&
|
|
227
231
|
e.loc !== "" &&
|
|
228
232
|
e.material.length > 0), (c) => c.loc + " #" + c.locnum.toString(), (c) => c.endUTC)
|
|
@@ -74,8 +74,8 @@ export function convertLogToInspections(c) {
|
|
|
74
74
|
}
|
|
75
75
|
return c.material.map((m) => {
|
|
76
76
|
if (c.type === LogType.Inspection) {
|
|
77
|
-
|
|
78
|
-
const pathsJson =
|
|
77
|
+
const parsedPaths = JSON.parse((c.details || {}).ActualPath || "[]");
|
|
78
|
+
const pathsJson = Array.isArray(parsedPaths) ? parsedPaths : [];
|
|
79
79
|
const paths = [];
|
|
80
80
|
for (const pathJson of pathsJson) {
|
|
81
81
|
paths.push(MaterialProcessActualPath.fromJS(pathJson));
|
|
@@ -42,6 +42,7 @@ import * as names from "./names.js";
|
|
|
42
42
|
import * as estimated from "./estimated-cycle-times.js";
|
|
43
43
|
import * as tool from "./tool-usage.js";
|
|
44
44
|
import * as palCycles from "./pallet-cycles.js";
|
|
45
|
+
import * as basketCycles from "./basket-cycles.js";
|
|
45
46
|
import * as statCycles from "./station-cycles.js";
|
|
46
47
|
import * as toolReplace from "./tool-replacements.js";
|
|
47
48
|
import * as simDayUsage from "./sim-day-usage.js";
|
|
@@ -60,6 +61,7 @@ export const onServerEvent = atom(null, (_, set, evt) => {
|
|
|
60
61
|
set(names.updateNames, evt);
|
|
61
62
|
set(tool.updateLast30ToolUse, evt);
|
|
62
63
|
set(palCycles.updateLast30PalletCycles, evt);
|
|
64
|
+
set(basketCycles.updateLast30BasketCycles, evt);
|
|
63
65
|
set(toolReplace.updateLastToolReplacements, evt);
|
|
64
66
|
set(statCycles.updateLast30StationCycles, evt);
|
|
65
67
|
set(rebookings.updateLast30Rebookings, evt);
|
|
@@ -86,6 +88,7 @@ export const onLoadLast30Log = atom(null, (_, set, log) => {
|
|
|
86
88
|
set(names.setNamesFromLast30Evts, log);
|
|
87
89
|
set(tool.setLast30ToolUse, log);
|
|
88
90
|
set(palCycles.setLast30PalletCycles, log);
|
|
91
|
+
set(basketCycles.setLast30BasketCycles, log);
|
|
89
92
|
set(toolReplace.setLast30ToolReplacements, log);
|
|
90
93
|
set(statCycles.setLast30StationCycles, log);
|
|
91
94
|
set(rebookings.setLast30Rebookings, log);
|
|
@@ -107,6 +110,7 @@ export const onLoadSpecificMonthLog = atom(null, (_, set, log) => {
|
|
|
107
110
|
set(insp.setSpecificMonthInspections, log);
|
|
108
111
|
set(mats.setSpecificMonthMatSummary, log);
|
|
109
112
|
set(palCycles.setSpecificMonthPalletCycles, log);
|
|
113
|
+
set(basketCycles.setSpecificMonthBasketCycles, log);
|
|
110
114
|
set(toolReplace.setSpecificMonthToolReplacements, log);
|
|
111
115
|
set(statCycles.setSpecificMonthStationCycles, log);
|
|
112
116
|
});
|
|
@@ -32,8 +32,10 @@ export type MaterialToShow = {
|
|
|
32
32
|
export declare const materialDialogOpen: import("jotai").WritableAtom<MaterialToShow | null, [mat: MaterialToShow | null], void>;
|
|
33
33
|
export declare const barcodePotentialNewMaterial: import("jotai").Atom<Promise<Readonly<IScannedPotentialNewMaterial> | null>>;
|
|
34
34
|
export declare const materialInDialogInfo: import("jotai").Atom<Promise<MaterialToShowInfo | null>>;
|
|
35
|
+
export declare const materialInDialogInfoUnwrapped: import("jotai").Atom<MaterialToShowInfo | null>;
|
|
35
36
|
export declare const inProcessMaterialInDialog: import("jotai").Atom<Promise<IInProcessMaterial | null>>;
|
|
36
37
|
export declare const serialInMaterialDialog: import("jotai").Atom<Promise<string | null>>;
|
|
38
|
+
export declare const serialInMaterialDialogUnwrapped: import("jotai").Atom<string | null>;
|
|
37
39
|
export declare const workorderInMaterialDialog: import("jotai").Atom<Promise<string | null>>;
|
|
38
40
|
export declare const materialInDialogEvents: import("jotai").Atom<readonly Readonly<ILogEntry>[]>;
|
|
39
41
|
export type LargestUsedProces = {
|
|
@@ -66,7 +68,10 @@ export interface CompleteCloseoutData {
|
|
|
66
68
|
readonly failed: boolean;
|
|
67
69
|
}
|
|
68
70
|
export declare function useCompleteCloseout(): [(d: CompleteCloseoutData) => void, boolean];
|
|
69
|
-
export declare function useAssignWorkorder(): [
|
|
71
|
+
export declare function useAssignWorkorder(): [
|
|
72
|
+
(mat: MaterialToShowInfo, workorder: string) => void,
|
|
73
|
+
boolean
|
|
74
|
+
];
|
|
70
75
|
export interface AddNoteData {
|
|
71
76
|
readonly matId: number;
|
|
72
77
|
readonly process: number;
|
|
@@ -90,7 +95,10 @@ export interface AddExistingMaterialToQueueData {
|
|
|
90
95
|
readonly queuePosition: number;
|
|
91
96
|
readonly operator: string | null;
|
|
92
97
|
}
|
|
93
|
-
export declare function useAddExistingMaterialToQueue(): [
|
|
98
|
+
export declare function useAddExistingMaterialToQueue(): [
|
|
99
|
+
(d: AddExistingMaterialToQueueData) => void,
|
|
100
|
+
boolean
|
|
101
|
+
];
|
|
94
102
|
export interface AddNewMaterialToQueueData {
|
|
95
103
|
readonly jobUnique: string;
|
|
96
104
|
readonly lastCompletedProcess: number;
|
|
@@ -100,7 +108,7 @@ export interface AddNewMaterialToQueueData {
|
|
|
100
108
|
readonly operator: string | null;
|
|
101
109
|
readonly workorder: string | null;
|
|
102
110
|
readonly onNewMaterial?: (mat: Readonly<IInProcessMaterial>) => void;
|
|
103
|
-
readonly onError?: (reason:
|
|
111
|
+
readonly onError?: (reason: unknown) => void;
|
|
104
112
|
}
|
|
105
113
|
export declare function useAddNewMaterialToQueue(): [(d: AddNewMaterialToQueueData) => void, boolean];
|
|
106
114
|
export interface AddNewCastingToQueueData {
|
|
@@ -111,6 +119,6 @@ export interface AddNewCastingToQueueData {
|
|
|
111
119
|
readonly workorder: string | null;
|
|
112
120
|
readonly operator: string | null;
|
|
113
121
|
readonly onNewMaterial?: (mats: ReadonlyArray<Readonly<IInProcessMaterial>>) => void;
|
|
114
|
-
readonly onError?: (reason:
|
|
122
|
+
readonly onError?: (reason: unknown) => void;
|
|
115
123
|
}
|
|
116
124
|
export declare function useAddNewCastingToQueue(): [(d: AddNewCastingToQueueData) => void, boolean];
|
|
@@ -36,7 +36,7 @@ import { LogType, NewInspectionCompleted, NewCloseout, QueuePosition, } from "..
|
|
|
36
36
|
import { useCallback, useState } from "react";
|
|
37
37
|
import { currentStatus } from "./current-status.js";
|
|
38
38
|
import { atom, useSetAtom } from "jotai";
|
|
39
|
-
import {
|
|
39
|
+
import { unwrap } from "jotai/utils";
|
|
40
40
|
import { isLogEntryInvalidated } from "../components/LogEntry.js";
|
|
41
41
|
import { currentRoute, RouteLocation } from "../components/routes.js";
|
|
42
42
|
const matToShow = atom(null);
|
|
@@ -55,6 +55,9 @@ export const materialDialogOpen = atom((get) => get(matToShow), (_, set, mat) =>
|
|
|
55
55
|
const barcodeMaterialDetail = atom(async (get) => {
|
|
56
56
|
const toShow = get(matToShow);
|
|
57
57
|
if (toShow && toShow.type === "Barcode") {
|
|
58
|
+
if (typeof FmsServerBackend === "undefined") {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
58
61
|
const route = get(currentRoute);
|
|
59
62
|
const queues = toShow.toQueue
|
|
60
63
|
? [toShow.toQueue]
|
|
@@ -101,10 +104,14 @@ export const materialInDialogInfo = atom(async (get) => {
|
|
|
101
104
|
}
|
|
102
105
|
case "ManuallyEnteredSerial":
|
|
103
106
|
case "AddMatWithEnteredSerial": {
|
|
107
|
+
if (typeof LogBackend === "undefined") {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
104
110
|
return (await LogBackend.materialForSerial(curMat.serial))?.[0] ?? null;
|
|
105
111
|
}
|
|
106
112
|
}
|
|
107
113
|
});
|
|
114
|
+
export const materialInDialogInfoUnwrapped = unwrap(materialInDialogInfo, (prev) => prev ?? null);
|
|
108
115
|
export const inProcessMaterialInDialog = atom(async (get) => {
|
|
109
116
|
const status = get(currentStatus);
|
|
110
117
|
const toShow = get(matToShow);
|
|
@@ -113,7 +120,9 @@ export const inProcessMaterialInDialog = atom(async (get) => {
|
|
|
113
120
|
if (toShow.type === "InProcMat")
|
|
114
121
|
return toShow.inproc;
|
|
115
122
|
const matId = (await get(materialInDialogInfo))?.materialID ?? null;
|
|
116
|
-
return matId !== null && matId >= 0
|
|
123
|
+
return matId !== null && matId >= 0
|
|
124
|
+
? (status.material.find((m) => m.materialID === matId) ?? null)
|
|
125
|
+
: null;
|
|
117
126
|
});
|
|
118
127
|
export const serialInMaterialDialog = atom(async (get) => {
|
|
119
128
|
const toShow = get(matToShow);
|
|
@@ -130,13 +139,14 @@ export const serialInMaterialDialog = atom(async (get) => {
|
|
|
130
139
|
return toShow.logMat.serial ?? null;
|
|
131
140
|
case "Barcode": {
|
|
132
141
|
const barcodeMat = await get(barcodeMaterialDetail);
|
|
133
|
-
return barcodeMat?.existingMaterial?.serial ?? barcodeMat?.potentialNewMaterial?.serial ?? null;
|
|
142
|
+
return (barcodeMat?.existingMaterial?.serial ?? barcodeMat?.potentialNewMaterial?.serial ?? null);
|
|
134
143
|
}
|
|
135
144
|
case "ManuallyEnteredSerial":
|
|
136
145
|
case "AddMatWithEnteredSerial":
|
|
137
146
|
return toShow.serial;
|
|
138
147
|
}
|
|
139
148
|
});
|
|
149
|
+
export const serialInMaterialDialogUnwrapped = unwrap(serialInMaterialDialog, (prev) => prev ?? null);
|
|
140
150
|
export const workorderInMaterialDialog = atom(async (get) => {
|
|
141
151
|
const toShow = get(matToShow);
|
|
142
152
|
if (toShow === null)
|
|
@@ -152,7 +162,9 @@ export const workorderInMaterialDialog = atom(async (get) => {
|
|
|
152
162
|
return toShow.logMat.workorder ?? null;
|
|
153
163
|
case "Barcode": {
|
|
154
164
|
const barcodeMat = await get(barcodeMaterialDetail);
|
|
155
|
-
return barcodeMat?.existingMaterial?.workorder ??
|
|
165
|
+
return (barcodeMat?.existingMaterial?.workorder ??
|
|
166
|
+
barcodeMat?.potentialNewMaterial?.workorder ??
|
|
167
|
+
null);
|
|
156
168
|
}
|
|
157
169
|
case "ManuallyEnteredSerial":
|
|
158
170
|
case "AddMatWithEnteredSerial":
|
|
@@ -168,6 +180,9 @@ const localMatEvents = atom(async (get, { signal }) => {
|
|
|
168
180
|
if (mat === null) {
|
|
169
181
|
return [];
|
|
170
182
|
}
|
|
183
|
+
else if (typeof LogBackend === "undefined") {
|
|
184
|
+
return [];
|
|
185
|
+
}
|
|
171
186
|
else if (mat.materialID >= 0) {
|
|
172
187
|
return await LogBackend.logForMaterial(mat.materialID, signal);
|
|
173
188
|
}
|
|
@@ -178,7 +193,7 @@ const localMatEvents = atom(async (get, { signal }) => {
|
|
|
178
193
|
return [];
|
|
179
194
|
}
|
|
180
195
|
});
|
|
181
|
-
const
|
|
196
|
+
const localMatEventsUnwrapped = unwrap(localMatEvents, (prev) => prev ?? []);
|
|
182
197
|
const otherMatEvents = atom(async (get, { signal }) => {
|
|
183
198
|
const serial = await get(serialInMaterialDialog);
|
|
184
199
|
if (serial === null || serial === "")
|
|
@@ -189,13 +204,11 @@ const otherMatEvents = atom(async (get, { signal }) => {
|
|
|
189
204
|
}
|
|
190
205
|
return evts;
|
|
191
206
|
});
|
|
192
|
-
const
|
|
207
|
+
const otherMatEventsUnwrapped = unwrap(otherMatEvents, (prev) => prev ?? []);
|
|
193
208
|
export const materialInDialogEvents = atom((get) => {
|
|
194
|
-
const localEvts = get(localMatEventsLoadable);
|
|
195
|
-
const otherEvts = get(otherMatEventsLoadable);
|
|
196
209
|
const evtsFromUpdate = get(extraLogEventsFromUpdates);
|
|
197
|
-
return LazySeq.of(
|
|
198
|
-
.concat(
|
|
210
|
+
return LazySeq.of(get(localMatEventsUnwrapped))
|
|
211
|
+
.concat(get(otherMatEventsUnwrapped))
|
|
199
212
|
.sortBy((e) => e.endUTC.getTime(), (e) => e.counter)
|
|
200
213
|
.concat(evtsFromUpdate)
|
|
201
214
|
.toRArray();
|
|
@@ -408,9 +421,7 @@ export function useAddNewCastingToQueue() {
|
|
|
408
421
|
const [updating, setUpdating] = useState(false);
|
|
409
422
|
const callback = useCallback((d) => {
|
|
410
423
|
setUpdating(true);
|
|
411
|
-
JobsBackend.addUnallocatedCastingToQueue(d.casting, d.queue, d.quantity, d.operator, d.workorder, [
|
|
412
|
-
...(d.serials || []),
|
|
413
|
-
])
|
|
424
|
+
JobsBackend.addUnallocatedCastingToQueue(d.casting, d.queue, d.quantity, d.operator, d.workorder, [...(d.serials || [])])
|
|
414
425
|
.then((ms) => {
|
|
415
426
|
if (d.onNewMaterial)
|
|
416
427
|
d.onNewMaterial(ms);
|
|
@@ -34,26 +34,24 @@ import { atom } from "jotai";
|
|
|
34
34
|
import { LogType } from "../network/api";
|
|
35
35
|
import { LazySeq, OrderedMap } from "@seedtactics/immutable-collections";
|
|
36
36
|
import { JobsBackend, LogBackend } from "../network/backend";
|
|
37
|
-
import {
|
|
37
|
+
import { unwrap } from "jotai/utils";
|
|
38
38
|
import { addDays } from "date-fns";
|
|
39
39
|
import { useCallback, useState } from "react";
|
|
40
40
|
const rebookingEvts = atom(OrderedMap.empty());
|
|
41
41
|
// unscheduled rebookings should be loaded from the last 30 events, but it is
|
|
42
|
-
// possible for some to be older than 30 days, so load them here
|
|
43
|
-
//
|
|
44
|
-
const unschRebookings =
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
42
|
+
// possible for some to be older than 30 days, so load them here without
|
|
43
|
+
// blocking rendering while the request is still pending.
|
|
44
|
+
const unschRebookings = unwrap(atom(async (_, { signal }) => {
|
|
45
|
+
if (typeof JobsBackend === "undefined") {
|
|
46
|
+
return OrderedMap.empty();
|
|
47
|
+
}
|
|
48
|
+
const bookings = await JobsBackend.unscheduledRebookings(signal);
|
|
49
|
+
return OrderedMap.build(bookings, (booking) => booking.bookingId);
|
|
50
|
+
}), (prev) => prev ?? OrderedMap.empty());
|
|
48
51
|
export const last30Rebookings = atom((get) => {
|
|
49
52
|
const evts = get(rebookingEvts);
|
|
50
53
|
const unsch = get(unschRebookings);
|
|
51
|
-
|
|
52
|
-
return evts.union(unsch.data, (fromEvt, fromUnsch) => fromEvt ?? fromUnsch);
|
|
53
|
-
}
|
|
54
|
-
else {
|
|
55
|
-
return evts;
|
|
56
|
-
}
|
|
54
|
+
return evts.union(unsch, (fromEvt, fromUnsch) => fromEvt ?? fromUnsch);
|
|
57
55
|
});
|
|
58
56
|
const canceledRebookingsRW = atom(OrderedMap.empty());
|
|
59
57
|
export const canceledRebookings = canceledRebookingsRW;
|
|
@@ -85,11 +83,11 @@ export const setLast30Rebookings = atom(null, (get, set, log) => {
|
|
|
85
83
|
function updateJobs(set, jobs, expire) {
|
|
86
84
|
set(scheduledRW, (old) => {
|
|
87
85
|
if (expire) {
|
|
88
|
-
old = old.filter((
|
|
86
|
+
old = old.filter((booking) => booking.scheduledTime >= expire);
|
|
89
87
|
}
|
|
90
88
|
return old.union(jobs
|
|
91
|
-
.flatMap((j) => (j.bookings ?? []).map((
|
|
92
|
-
|
|
89
|
+
.flatMap((j) => (j.bookings ?? []).map((booking) => [
|
|
90
|
+
booking,
|
|
93
91
|
{
|
|
94
92
|
scheduledTime: j.routeStartUTC,
|
|
95
93
|
jobUnique: j.unique,
|
|
@@ -99,7 +97,7 @@ function updateJobs(set, jobs, expire) {
|
|
|
99
97
|
});
|
|
100
98
|
}
|
|
101
99
|
export const setLast30RebookingJobs = atom(null, (_, set, historic) => {
|
|
102
|
-
updateJobs(set, LazySeq.ofObject(historic.jobs).map(([
|
|
100
|
+
updateJobs(set, LazySeq.ofObject(historic.jobs).map(([, j]) => j));
|
|
103
101
|
});
|
|
104
102
|
export const updateLast30Rebookings = atom(null, (get, set, { evt, now, expire }) => {
|
|
105
103
|
if (evt.newJobs) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ServerEventAndTime } from "./loading.js";
|
|
2
|
-
import { IHistoricData,
|
|
2
|
+
import { IHistoricData, IRecentHistoricData, IHistoricJob } from "../network/api.js";
|
|
3
3
|
import { HashMap, HashSet } from "@seedtactics/immutable-collections";
|
|
4
4
|
import { Atom } from "jotai";
|
|
5
5
|
export declare const last30Jobs: Atom<HashMap<string, Readonly<IHistoricJob>>>;
|
|
@@ -31,6 +31,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
31
31
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
32
32
|
*/
|
|
33
33
|
import { addDays } from "date-fns";
|
|
34
|
+
import { HistoricJob } from "../network/api.js";
|
|
34
35
|
import { HashMap, HashSet, LazySeq } from "@seedtactics/immutable-collections";
|
|
35
36
|
import { atom } from "jotai";
|
|
36
37
|
import { atomFamily } from "jotai-family";
|
|
@@ -67,10 +68,16 @@ export const updateLast30Jobs = atom(null, (_, set, { evt, now, expire }) => {
|
|
|
67
68
|
const newJobs = LazySeq.of(evt.newJobs.jobs);
|
|
68
69
|
set(last30JobsRW, (oldJobs) => {
|
|
69
70
|
if (expire) {
|
|
70
|
-
const
|
|
71
|
-
oldJobs = oldJobs.filter((j) => j.routeStartUTC >=
|
|
71
|
+
const expireDate = addDays(now, -30);
|
|
72
|
+
oldJobs = oldJobs.filter((j) => j.routeStartUTC >= expireDate);
|
|
72
73
|
}
|
|
73
|
-
return oldJobs.union(newJobs.toHashMap((j) =>
|
|
74
|
+
return oldJobs.union(newJobs.toHashMap((j) => {
|
|
75
|
+
const historicJob = new HistoricJob({
|
|
76
|
+
...j,
|
|
77
|
+
copiedToSystem: true,
|
|
78
|
+
});
|
|
79
|
+
return [j.unique, historicJob];
|
|
80
|
+
}));
|
|
74
81
|
});
|
|
75
82
|
if (schId) {
|
|
76
83
|
set(last30SchIdsRW, (oldIds) => oldIds.add(schId));
|
|
@@ -74,13 +74,13 @@ export const updateLast30JobProduction = atom(null, (_, set, { evt, now, expire
|
|
|
74
74
|
const apiNewJobs = evt.newJobs.jobs;
|
|
75
75
|
set(last30SimProductionRW, (simProd) => {
|
|
76
76
|
if (expire) {
|
|
77
|
-
const
|
|
77
|
+
const expireDate = addDays(now, -30);
|
|
78
78
|
// check if nothing to expire and no new data
|
|
79
79
|
const minProd = LazySeq.of(simProd).minBy((e) => e.completeTime.getTime());
|
|
80
|
-
if ((minProd === undefined || minProd.completeTime >=
|
|
80
|
+
if ((minProd === undefined || minProd.completeTime >= expireDate) && apiNewJobs.length === 0) {
|
|
81
81
|
return simProd;
|
|
82
82
|
}
|
|
83
|
-
simProd = simProd.filter((e) => e.completeTime >=
|
|
83
|
+
simProd = simProd.filter((e) => e.completeTime >= expireDate);
|
|
84
84
|
}
|
|
85
85
|
return [...simProd, ...jobToPartCompleted(apiNewJobs)];
|
|
86
86
|
});
|
|
@@ -33,25 +33,30 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
33
33
|
import { LazySeq } from "@seedtactics/immutable-collections";
|
|
34
34
|
import { addDays } from "date-fns";
|
|
35
35
|
import { atom } from "jotai";
|
|
36
|
+
import { fmsInformation } from "../network/server-settings.js";
|
|
37
|
+
import { displayStationName } from "./station-cycles.js";
|
|
36
38
|
const last30SimStationUseRW = atom([]);
|
|
37
39
|
export const last30SimStationUse = last30SimStationUseRW;
|
|
38
40
|
const specificMonthSimStationUseRW = atom([]);
|
|
39
41
|
export const specificMonthSimStationUse = specificMonthSimStationUseRW;
|
|
40
|
-
function procSimUse(apiSimUse) {
|
|
42
|
+
function procSimUse(apiSimUse, loadStationNames) {
|
|
41
43
|
return apiSimUse.map((simUse) => ({
|
|
42
44
|
station: simUse.stationGroup + " #" + simUse.stationNum.toString(),
|
|
45
|
+
stationLabel: displayStationName(simUse.stationGroup, simUse.stationNum, loadStationNames),
|
|
43
46
|
start: simUse.startUTC,
|
|
44
47
|
end: simUse.endUTC,
|
|
45
48
|
plannedDown: simUse.planDown ?? false,
|
|
46
|
-
|
|
49
|
+
parts: simUse.parts?.map((p) => ({ uniq: p.jobUnique, proc: p.process, path: p.path })),
|
|
47
50
|
}));
|
|
48
51
|
}
|
|
49
|
-
export const setLast30SimStatUse = atom(null, (
|
|
50
|
-
|
|
52
|
+
export const setLast30SimStatUse = atom(null, (get, set, history) => {
|
|
53
|
+
const loadStationNames = get(fmsInformation)?.loadStationNames;
|
|
54
|
+
set(last30SimStationUseRW, (oldSimUse) => oldSimUse.concat(procSimUse(history.stationUse, loadStationNames)));
|
|
51
55
|
});
|
|
52
|
-
export const updateLast30SimStatUse = atom(null, (
|
|
56
|
+
export const updateLast30SimStatUse = atom(null, (get, set, { evt, now, expire }) => {
|
|
53
57
|
if (evt.newJobs?.stationUse) {
|
|
54
58
|
const apiSimUse = evt.newJobs?.stationUse;
|
|
59
|
+
const loadStationNames = get(fmsInformation)?.loadStationNames;
|
|
55
60
|
set(last30SimStationUseRW, (simUse) => {
|
|
56
61
|
if (expire) {
|
|
57
62
|
const expireT = addDays(now, -30);
|
|
@@ -62,10 +67,11 @@ export const updateLast30SimStatUse = atom(null, (_, set, { evt, now, expire })
|
|
|
62
67
|
}
|
|
63
68
|
simUse = simUse.filter((e) => e.start >= expireT);
|
|
64
69
|
}
|
|
65
|
-
return simUse.concat(procSimUse(apiSimUse));
|
|
70
|
+
return simUse.concat(procSimUse(apiSimUse, loadStationNames));
|
|
66
71
|
});
|
|
67
72
|
}
|
|
68
73
|
});
|
|
69
|
-
export const setSpecificMonthSimStatUse = atom(null, (
|
|
70
|
-
|
|
74
|
+
export const setSpecificMonthSimStatUse = atom(null, (get, set, history) => {
|
|
75
|
+
const loadStationNames = get(fmsInformation)?.loadStationNames;
|
|
76
|
+
set(specificMonthSimStationUseRW, procSimUse(history.stationUse, loadStationNames));
|
|
71
77
|
});
|
|
@@ -2,6 +2,16 @@ import { ILogEntry, ILogMaterial } from "../network/api.js";
|
|
|
2
2
|
import type { ServerEventAndTime } from "./loading.js";
|
|
3
3
|
import { HashMap } from "@seedtactics/immutable-collections";
|
|
4
4
|
import { Atom } from "jotai";
|
|
5
|
+
export type PartCycleCarrier = {
|
|
6
|
+
readonly kind: "machining";
|
|
7
|
+
readonly pallet: number;
|
|
8
|
+
} | {
|
|
9
|
+
readonly kind: "pallet-lul";
|
|
10
|
+
readonly pallet: number;
|
|
11
|
+
} | {
|
|
12
|
+
readonly kind: "basket-lul";
|
|
13
|
+
readonly basket: number;
|
|
14
|
+
};
|
|
5
15
|
export interface PartCycleData {
|
|
6
16
|
readonly endTime: Date;
|
|
7
17
|
readonly elapsedMinsPerMaterial: number;
|
|
@@ -9,10 +19,10 @@ export interface PartCycleData {
|
|
|
9
19
|
readonly part: string;
|
|
10
20
|
readonly stationGroup: string;
|
|
11
21
|
readonly stationNumber: number;
|
|
22
|
+
readonly stationLabel: string;
|
|
12
23
|
readonly operation: string;
|
|
13
|
-
readonly
|
|
24
|
+
readonly carrier: PartCycleCarrier;
|
|
14
25
|
readonly activeMinutes: number;
|
|
15
|
-
readonly isLabor: boolean;
|
|
16
26
|
readonly material: ReadonlyArray<Readonly<ILogMaterial>>;
|
|
17
27
|
readonly operator: string;
|
|
18
28
|
readonly medianCycleMinutes: number;
|
|
@@ -20,9 +30,26 @@ export interface PartCycleData {
|
|
|
20
30
|
readonly isOutlier: boolean;
|
|
21
31
|
}
|
|
22
32
|
export type StationCyclesByCntr = HashMap<number, PartCycleData>;
|
|
33
|
+
export declare function isLaborCycle(cycle: Readonly<PartCycleData>): boolean;
|
|
34
|
+
export declare function isMachineCycle(cycle: Readonly<PartCycleData>): boolean;
|
|
35
|
+
export declare function isPalletLoadCycle(cycle: Readonly<PartCycleData>): boolean;
|
|
36
|
+
export declare function isBasketLoadCycle(cycle: Readonly<PartCycleData>): boolean;
|
|
37
|
+
export declare function palletForCycle(cycle: Readonly<PartCycleData>): number | undefined;
|
|
38
|
+
export declare function basketForCycle(cycle: Readonly<PartCycleData>): number | undefined;
|
|
23
39
|
export declare function stat_name_and_num(stationGroup: string, stationNumber: number): string;
|
|
40
|
+
export declare function loadStationDisplayName(stationNumber: number, loadStationNames: {
|
|
41
|
+
[key: string]: string;
|
|
42
|
+
} | undefined): string;
|
|
43
|
+
export declare function displayStationName(stationGroup: string, stationNumber: number, loadStationNames: {
|
|
44
|
+
[key: string]: string;
|
|
45
|
+
} | undefined): string;
|
|
46
|
+
export declare function basketDisplayName(basketName: string | null | undefined): string;
|
|
47
|
+
export declare function carrierLabel(cycle: Readonly<PartCycleData>, basketName?: string): string;
|
|
48
|
+
export declare function carrierSortKey(cycle: Readonly<PartCycleData>): number;
|
|
24
49
|
export declare const last30StationCycles: Atom<StationCyclesByCntr>;
|
|
50
|
+
export declare const last30HasBasketCycles: Atom<boolean>;
|
|
25
51
|
export declare const specificMonthStationCycles: Atom<StationCyclesByCntr>;
|
|
52
|
+
export declare const specificMonthHasBasketCycles: Atom<boolean>;
|
|
26
53
|
export declare const setLast30StationCycles: import("jotai").WritableAtom<null, [log: readonly Readonly<ILogEntry>[]], void> & {
|
|
27
54
|
init: null;
|
|
28
55
|
};
|