@seedtactics/insight-client 16.5.2 → 16.7.0

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.
Files changed (110) hide show
  1. package/dist/assets/ProgramHighlight-DPTeZ8Si.js +3 -0
  2. package/dist/assets/index-bPAFn3jp.js +364 -0
  3. package/dist/cell-status/basket-cycles.d.ts +21 -0
  4. package/dist/cell-status/basket-cycles.js +80 -0
  5. package/dist/cell-status/current-status.js +11 -3
  6. package/dist/cell-status/estimated-cycle-times.js +8 -4
  7. package/dist/cell-status/inspections.js +2 -2
  8. package/dist/cell-status/loading.js +4 -0
  9. package/dist/cell-status/material-details.d.ts +12 -4
  10. package/dist/cell-status/material-details.js +24 -13
  11. package/dist/cell-status/rebookings.js +15 -17
  12. package/dist/cell-status/scheduled-jobs.d.ts +1 -1
  13. package/dist/cell-status/scheduled-jobs.js +10 -3
  14. package/dist/cell-status/sim-production.js +3 -3
  15. package/dist/cell-status/sim-station-use.d.ts +1 -0
  16. package/dist/cell-status/sim-station-use.js +14 -8
  17. package/dist/cell-status/station-cycles.d.ts +29 -2
  18. package/dist/cell-status/station-cycles.js +81 -11
  19. package/dist/cell-status/tool-replacements.js +1 -1
  20. package/dist/cell-status/tool-usage.js +1 -1
  21. package/dist/components/App.js +101 -66
  22. package/dist/components/BarcodeScanning.js +12 -2
  23. package/dist/components/ErrorsAndLoading.js +10 -1
  24. package/dist/components/LogEntry.d.ts +0 -1
  25. package/dist/components/LogEntry.js +50 -26
  26. package/dist/components/Navigation.js +30 -4
  27. package/dist/components/analysis/AnalysisSelectToolbar.js +5 -1
  28. package/dist/components/analysis/BasketCycleCards.d.ts +1 -0
  29. package/dist/components/analysis/BasketCycleCards.js +145 -0
  30. package/dist/components/analysis/BufferChart.js +10 -4
  31. package/dist/components/analysis/CostPerPiece.js +28 -8
  32. package/dist/components/analysis/CycleChart.js +1 -1
  33. package/dist/components/analysis/DataTable.js +6 -4
  34. package/dist/components/analysis/HeatChart.js +27 -14
  35. package/dist/components/analysis/InspectionSankey.js +17 -6
  36. package/dist/components/analysis/PalletCycleCards.js +15 -4
  37. package/dist/components/analysis/PartCycleCards.js +62 -18
  38. package/dist/components/analysis/StationDataTable.js +14 -11
  39. package/dist/components/analysis/ToolReplacements.js +16 -10
  40. package/dist/components/log-entry-queue-filter.d.ts +2 -0
  41. package/dist/components/log-entry-queue-filter.js +21 -0
  42. package/dist/components/operations/AllMaterial.js +26 -10
  43. package/dist/components/operations/ChartRangeEdit.js +82 -4
  44. package/dist/components/operations/CloseoutReport.js +13 -4
  45. package/dist/components/operations/CompletedParts.js +23 -11
  46. package/dist/components/operations/CurrentWorkorders.js +31 -9
  47. package/dist/components/operations/OEEChart.js +8 -2
  48. package/dist/components/operations/Outliers.js +18 -7
  49. package/dist/components/operations/ProgramHighlight.js +4 -6
  50. package/dist/components/operations/Programs.js +9 -3
  51. package/dist/components/operations/Rebookings.js +8 -4
  52. package/dist/components/operations/RecentCycleChart.js +5 -3
  53. package/dist/components/operations/RecentProduction.js +30 -13
  54. package/dist/components/operations/RecentSchedules.js +6 -2
  55. package/dist/components/operations/RecentStationCycles.js +38 -11
  56. package/dist/components/operations/ShiftSettings.js +3 -3
  57. package/dist/components/operations/SimDayUsage.js +27 -4
  58. package/dist/components/operations/ToolReport.js +5 -1
  59. package/dist/components/operations/WorkorderGantt.js +22 -2
  60. package/dist/components/quality/QualityMaterial.js +11 -8
  61. package/dist/components/quality/RecentFailedInspections.js +3 -1
  62. package/dist/components/routes.d.ts +3 -0
  63. package/dist/components/routes.js +2 -0
  64. package/dist/components/station-monitor/BulkRawMaterial.js +11 -7
  65. package/dist/components/station-monitor/Closeout.js +14 -2
  66. package/dist/components/station-monitor/CustomStationMonitorDialog.js +1 -1
  67. package/dist/components/station-monitor/Inspection.js +23 -11
  68. package/dist/components/station-monitor/InvalidateCycle.js +3 -3
  69. package/dist/components/station-monitor/JobDetails.js +15 -2
  70. package/dist/components/station-monitor/LoadStation.js +274 -31
  71. package/dist/components/station-monitor/Material.js +71 -11
  72. package/dist/components/station-monitor/MoveMaterialArrows.js +4 -4
  73. package/dist/components/station-monitor/QuarantineButton.js +11 -0
  74. package/dist/components/station-monitor/Queues.js +28 -9
  75. package/dist/components/station-monitor/QueuesAddMaterial.js +8 -6
  76. package/dist/components/station-monitor/SelectInspType.js +1 -1
  77. package/dist/components/station-monitor/SelectWorkorder.js +1 -1
  78. package/dist/components/station-monitor/StationToolbar.js +17 -5
  79. package/dist/components/station-monitor/SystemOverview.d.ts +19 -1
  80. package/dist/components/station-monitor/SystemOverview.js +439 -20
  81. package/dist/components/station-monitor/Whiteboard.js +15 -7
  82. package/dist/data/all-material-bins.d.ts +7 -0
  83. package/dist/data/all-material-bins.js +47 -1
  84. package/dist/data/cost-per-piece.js +11 -8
  85. package/dist/data/current-cycles.d.ts +1 -1
  86. package/dist/data/current-cycles.js +62 -17
  87. package/dist/data/move-arrows.d.ts +5 -1
  88. package/dist/data/move-arrows.js +54 -4
  89. package/dist/data/part-summary.js +13 -13
  90. package/dist/data/path-lookup.js +6 -23
  91. package/dist/data/queue-material.d.ts +1 -1
  92. package/dist/data/queue-material.js +18 -17
  93. package/dist/data/results.completed-parts.js +4 -3
  94. package/dist/data/results.cycles.d.ts +15 -6
  95. package/dist/data/results.cycles.js +51 -30
  96. package/dist/data/results.inspection.js +11 -6
  97. package/dist/data/results.oee.js +8 -8
  98. package/dist/data/results.schedules.js +2 -11
  99. package/dist/data/tools-programs.js +5 -6
  100. package/dist/index.html +1 -1
  101. package/dist/network/api.d.ts +22 -4
  102. package/dist/network/api.js +40 -5
  103. package/dist/network/backend-mock.js +15 -8
  104. package/dist/network/backend.js +3 -3
  105. package/dist/network/websocket.js +15 -15
  106. package/dist/util/chart-helpers.d.ts +1 -1
  107. package/dist/util/chart-helpers.js +14 -8
  108. package/package.json +29 -31
  109. package/dist/assets/ProgramHighlight-LvRM40Qr.js +0 -3
  110. package/dist/assets/index-gAFi3Oss.js +0 -364
@@ -36,6 +36,25 @@ import { durationToMinutes } from "../util/parseISODuration.js";
36
36
  import { addDays } from "date-fns";
37
37
  import { HashMap } from "@seedtactics/immutable-collections";
38
38
  import { atom } from "jotai";
39
+ import { fmsInformation } from "../network/server-settings.js";
40
+ export function isLaborCycle(cycle) {
41
+ return cycle.carrier.kind !== "machining";
42
+ }
43
+ export function isMachineCycle(cycle) {
44
+ return cycle.carrier.kind === "machining";
45
+ }
46
+ export function isPalletLoadCycle(cycle) {
47
+ return cycle.carrier.kind === "pallet-lul";
48
+ }
49
+ export function isBasketLoadCycle(cycle) {
50
+ return cycle.carrier.kind === "basket-lul";
51
+ }
52
+ export function palletForCycle(cycle) {
53
+ return cycle.carrier.kind === "basket-lul" ? undefined : cycle.carrier.pallet;
54
+ }
55
+ export function basketForCycle(cycle) {
56
+ return cycle.carrier.kind === "basket-lul" ? cycle.carrier.basket : undefined;
57
+ }
39
58
  export function stat_name_and_num(stationGroup, stationNumber) {
40
59
  if (stationGroup.startsWith("Inspect")) {
41
60
  return stationGroup;
@@ -44,21 +63,68 @@ export function stat_name_and_num(stationGroup, stationNumber) {
44
63
  return stationGroup + " #" + stationNumber.toString();
45
64
  }
46
65
  }
66
+ export function loadStationDisplayName(stationNumber, loadStationNames) {
67
+ const name = loadStationNames?.[stationNumber.toString()];
68
+ return name && name.trim().length > 0 ? name : `L/U #${stationNumber}`;
69
+ }
70
+ export function displayStationName(stationGroup, stationNumber, loadStationNames) {
71
+ return stationGroup === "L/U"
72
+ ? loadStationDisplayName(stationNumber, loadStationNames)
73
+ : stat_name_and_num(stationGroup, stationNumber);
74
+ }
75
+ export function basketDisplayName(basketName) {
76
+ return basketName && basketName.trim().length > 0 ? basketName : "Basket";
77
+ }
78
+ export function carrierLabel(cycle, basketName = "Basket") {
79
+ switch (cycle.carrier.kind) {
80
+ case "machining":
81
+ case "pallet-lul":
82
+ return `Pallet ${cycle.carrier.pallet}`;
83
+ case "basket-lul":
84
+ return `${basketName} ${cycle.carrier.basket}`;
85
+ }
86
+ }
87
+ export function carrierSortKey(cycle) {
88
+ switch (cycle.carrier.kind) {
89
+ case "machining":
90
+ case "pallet-lul":
91
+ return cycle.carrier.pallet;
92
+ case "basket-lul":
93
+ return cycle.carrier.basket + 1000000;
94
+ }
95
+ }
96
+ function hasBasketCycles(cycles) {
97
+ return cycles.valuesToLazySeq().some((cycle) => cycle.carrier.kind === "basket-lul");
98
+ }
47
99
  const last30StationCyclesRW = atom(HashMap.empty());
48
100
  export const last30StationCycles = last30StationCyclesRW;
101
+ export const last30HasBasketCycles = atom((get) => hasBasketCycles(get(last30StationCyclesRW)));
49
102
  const specificMonthStationCyclesRW = atom(HashMap.empty());
50
103
  export const specificMonthStationCycles = specificMonthStationCyclesRW;
51
- function convertLogToCycle(estimatedCycleTimes, cycle, elapsedPerMat) {
104
+ export const specificMonthHasBasketCycles = atom((get) => hasBasketCycles(get(specificMonthStationCyclesRW)));
105
+ function convertLogToCycle(estimatedCycleTimes, cycle, elapsedPerMat, loadStationNames) {
52
106
  if (cycle.startofcycle ||
53
- (cycle.type !== LogType.LoadUnloadCycle && cycle.type !== LogType.MachineCycle) ||
107
+ (cycle.type !== LogType.LoadUnloadCycle &&
108
+ cycle.type !== LogType.BasketLoadUnload &&
109
+ cycle.type !== LogType.MachineCycle) ||
54
110
  cycle.loc === "") {
55
111
  return null;
56
112
  }
113
+ const carrier = cycle.type === LogType.MachineCycle
114
+ ? { kind: "machining", pallet: cycle.pal }
115
+ : cycle.type === LogType.LoadUnloadCycle
116
+ ? { kind: "pallet-lul", pallet: cycle.pal }
117
+ : { kind: "basket-lul", basket: cycle.pal };
118
+ const activeMinsFromLog = durationToMinutes(cycle.active);
119
+ if (carrier.kind === "basket-lul" && elapsedPerMat <= 0 && activeMinsFromLog <= 0) {
120
+ // For pallet <-> basket transfers, the basket-side companion event carries zero time.
121
+ return null;
122
+ }
57
123
  const part = cycle.material.length > 0 ? cycle.material[0].part : "";
58
124
  const stats = cycle.material.length > 0
59
125
  ? estimatedCycleTimes.get(PartAndStationOperation.ofLogCycle(cycle))
60
126
  : undefined;
61
- let activeMins = durationToMinutes(cycle.active);
127
+ let activeMins = activeMinsFromLog;
62
128
  if (cycle.active === "" || activeMins <= 0 || cycle.material.length == 0) {
63
129
  activeMins = (stats?.expectedCycleMinutesForSingleMat ?? 0) * cycle.material.length;
64
130
  }
@@ -70,19 +136,19 @@ function convertLogToCycle(estimatedCycleTimes, cycle, elapsedPerMat) {
70
136
  medianCycleMinutes: (stats?.medianMinutesForSingleMat ?? 0) * cycle.material.length,
71
137
  MAD_aboveMinutes: stats?.MAD_aboveMinutes ?? 0,
72
138
  part: part,
73
- pallet: cycle.pal,
139
+ carrier: carrier,
74
140
  material: cycle.material,
75
- isLabor: cycle.type === LogType.LoadUnloadCycle,
76
141
  isOutlier: stats ? isOutlier(stats, elapsedPerMat) : false,
77
142
  stationGroup: cycle.loc,
78
143
  stationNumber: cycle.locnum,
79
- operation: cycle.type === LogType.LoadUnloadCycle ? cycle.result : cycle.program,
144
+ stationLabel: displayStationName(cycle.loc, cycle.locnum, loadStationNames),
145
+ operation: carrier.kind === "machining" ? cycle.program : cycle.result,
80
146
  operator: cycle.details ? cycle.details.operator || "" : "",
81
147
  };
82
148
  }
83
- function convertOldLogsToCycles(estimateCycleTimes, log) {
149
+ function convertOldLogsToCycles(estimateCycleTimes, log, loadStationNames) {
84
150
  return calcElapsedForCycles(log)
85
- .collect((c) => convertLogToCycle(estimateCycleTimes, c.cycle, c.elapsedForSingleMaterialMinutes))
151
+ .collect((c) => convertLogToCycle(estimateCycleTimes, c.cycle, c.elapsedForSingleMaterialMinutes, loadStationNames))
86
152
  .buildHashMap((c) => c.cntr);
87
153
  }
88
154
  function process_swap(swap, partCycles) {
@@ -97,7 +163,8 @@ function process_swap(swap, partCycles) {
97
163
  }
98
164
  export const setLast30StationCycles = atom(null, (get, set, log) => {
99
165
  const estimatedCycleTimes = get(last30EstimatedCycleTimes);
100
- set(last30StationCyclesRW, (oldCycles) => oldCycles.union(convertOldLogsToCycles(estimatedCycleTimes, log)));
166
+ const loadStationNames = get(fmsInformation)?.loadStationNames;
167
+ set(last30StationCyclesRW, (oldCycles) => oldCycles.union(convertOldLogsToCycles(estimatedCycleTimes, log, loadStationNames)));
101
168
  });
102
169
  export const updateLast30StationCycles = atom(null, (get, set, { evt, now, expire }) => {
103
170
  if (evt.logEntry && evt.logEntry.type === LogType.InvalidateCycle) {
@@ -122,7 +189,8 @@ export const updateLast30StationCycles = atom(null, (get, set, { evt, now, expir
122
189
  const elapsedPerMat = evt.logEntry.material.length > 0
123
190
  ? durationToMinutes(evt.logEntry.elapsed) / evt.logEntry.material.length
124
191
  : 0;
125
- const converted = convertLogToCycle(estimatedCycleTimes, evt.logEntry, elapsedPerMat);
192
+ const loadStationNames = get(fmsInformation)?.loadStationNames;
193
+ const converted = convertLogToCycle(estimatedCycleTimes, evt.logEntry, elapsedPerMat, loadStationNames);
126
194
  if (!converted)
127
195
  return;
128
196
  set(last30StationCyclesRW, (cycles) => {
@@ -141,5 +209,7 @@ export const updateLast30StationCycles = atom(null, (get, set, { evt, now, expir
141
209
  });
142
210
  export const setSpecificMonthStationCycles = atom(null, (get, set, log) => {
143
211
  const estimatedCycleTimes = get(specificMonthEstimatedCycleTimes);
144
- set(specificMonthStationCyclesRW, convertOldLogsToCycles(estimatedCycleTimes, log));
212
+ const loadStationNames = get(fmsInformation)?.loadStationNames;
213
+ const cycles = convertOldLogsToCycles(estimatedCycleTimes, log, loadStationNames);
214
+ set(specificMonthStationCyclesRW, cycles);
145
215
  });
@@ -77,7 +77,7 @@ function addReplacementsFromLog(old, e) {
77
77
  const totalCntAtEnd = use.totalToolUseCountAtEndOfCycle ?? null;
78
78
  if ((useDuring || cntDuring) && useDuring === totalUseAtEnd && cntDuring === totalCntAtEnd) {
79
79
  // replace before cycle start
80
- const last = old.recentUse.get(key)?.find((e) => e.tool === use.tool && e.pocket === use.pocket);
80
+ const last = old.recentUse.get(key)?.find((entry) => entry.tool === use.tool && entry.pocket === use.pocket);
81
81
  if (last) {
82
82
  const lastTotalUse = last.totalToolUseAtEndOfCycle
83
83
  ? durationToMinutes(last.totalToolUseAtEndOfCycle)
@@ -84,7 +84,7 @@ function process_tools(cycle, estimatedCycleTimes, toolUsage) {
84
84
  }
85
85
  export const setLast30ToolUse = atom(null, (get, set, log) => {
86
86
  const estimated = get(last30EstimatedCycleTimes);
87
- set(last30ToolUseRW, (oldUsage) => log.reduce((usage, log) => process_tools(log, estimated, usage), oldUsage));
87
+ set(last30ToolUseRW, (oldUsage) => log.reduce((usage, entry) => process_tools(entry, estimated, usage), oldUsage));
88
88
  });
89
89
  export const updateLast30ToolUse = atom(null, (get, set, { evt }) => {
90
90
  if (evt.logEntry) {
@@ -70,6 +70,7 @@ import { BufferOccupancyChart } from "./analysis/BufferChart.js";
70
70
  import { CompletedCountHeatmap, StationOeeHeatmap } from "./analysis/EfficiencyPage.js";
71
71
  import { PartLoadStationCycleChart, PartMachineCycleChart } from "./analysis/PartCycleCards.js";
72
72
  import { PalletCycleChart } from "./analysis/PalletCycleCards.js";
73
+ import { BasketCycleChart } from "./analysis/BasketCycleCards.js";
73
74
  import { ToolReplacementPage } from "./analysis/ToolReplacements.js";
74
75
  import { CurrentWorkordersPage } from "./operations/CurrentWorkorders.js";
75
76
  import { useAtom, useAtomValue } from "jotai";
@@ -78,6 +79,8 @@ import { latestSimDayUsage } from "../cell-status/sim-day-usage.js";
78
79
  import { CloseoutReport } from "./operations/CloseoutReport.js";
79
80
  import { RebookingsPage } from "./operations/Rebookings.js";
80
81
  import { RecentCompletedPartsPage } from "./operations/CompletedParts.js";
82
+ import { basketDisplayName } from "../cell-status/station-cycles.js";
83
+ import { last30HasBasketCycleData, specificMonthHasBasketCycleData } from "../cell-status/basket-cycles.js";
81
84
  const OperationsReportsTab = "bms-operations-reports-tab";
82
85
  const operationsReports = [
83
86
  { separator: "Load/Unload" },
@@ -166,59 +169,82 @@ const operationsReports = [
166
169
  hidden: (info) => info.supportsRebookings === null || info.supportsRebookings === "",
167
170
  },
168
171
  ];
169
- const analysisReports = [
170
- { separator: "Efficiency" },
171
- { name: "Buffers", route: { route: routes.RouteLocation.Analysis_Buffers }, icon: _jsx(BufferIcon, {}) },
172
- {
173
- name: "Station OEE",
174
- route: { route: routes.RouteLocation.Analysis_StationOEE },
175
- icon: _jsx(HourglassIcon, {}),
176
- },
177
- {
178
- name: "Completed Parts",
179
- route: { route: routes.RouteLocation.Analysis_PartsCompleted },
180
- icon: _jsx(ExtensionIcon, {}),
181
- },
182
- { separator: "Cycles" },
183
- {
184
- name: "Machine Cycles",
185
- route: { route: routes.RouteLocation.Analysis_MachineCycles },
186
- icon: _jsx(WorkIcon, {}),
187
- },
188
- {
189
- name: "L/U Cycles",
190
- route: { route: routes.RouteLocation.Analysis_LoadCycles },
191
- icon: _jsx(LoadUnloadIcon, {}),
192
- },
193
- {
194
- name: "Pallet Cycles",
195
- route: { route: routes.RouteLocation.Analysis_PalletCycles },
196
- icon: _jsx(PalletIcon, {}),
197
- },
198
- { separator: "Cell" },
199
- { name: "Quality", route: { route: routes.RouteLocation.Analysis_Quality }, icon: _jsx(BuildIcon, {}) },
200
- {
201
- name: "Tool Replacements",
202
- route: { route: routes.RouteLocation.Analysis_ToolReplacements },
203
- icon: _jsx(ToolIcon, {}),
204
- },
205
- {
206
- name: "Schedules",
207
- route: { route: routes.RouteLocation.Analysis_Schedules },
208
- icon: _jsx(ScheduleIcon, {}),
209
- },
210
- { separator: "Costs" },
211
- {
212
- name: "Percentages",
213
- route: { route: routes.RouteLocation.Analysis_CostPercents },
214
- icon: _jsx(CallSplit, {}),
215
- },
216
- {
217
- name: "Cost/Piece",
218
- route: { route: routes.RouteLocation.Analysis_CostPerPiece },
219
- icon: _jsx(CostIcon, {}),
220
- },
221
- ];
172
+ function analysisReports(hasBasketCycles) {
173
+ return [
174
+ { separator: "Efficiency" },
175
+ { name: "Buffers", route: { route: routes.RouteLocation.Analysis_Buffers }, icon: _jsx(BufferIcon, {}) },
176
+ {
177
+ name: "Station OEE",
178
+ route: { route: routes.RouteLocation.Analysis_StationOEE },
179
+ icon: _jsx(HourglassIcon, {}),
180
+ },
181
+ {
182
+ name: "Completed Parts",
183
+ route: { route: routes.RouteLocation.Analysis_PartsCompleted },
184
+ icon: _jsx(ExtensionIcon, {}),
185
+ },
186
+ { separator: "Cycles" },
187
+ {
188
+ name: "Machine Cycles",
189
+ route: { route: routes.RouteLocation.Analysis_MachineCycles },
190
+ icon: _jsx(WorkIcon, {}),
191
+ },
192
+ {
193
+ name: "L/U Cycles",
194
+ route: { route: routes.RouteLocation.Analysis_LoadCycles },
195
+ icon: _jsx(LoadUnloadIcon, {}),
196
+ },
197
+ {
198
+ name: "Pallet Cycles",
199
+ route: { route: routes.RouteLocation.Analysis_PalletCycles },
200
+ icon: _jsx(PalletIcon, {}),
201
+ },
202
+ {
203
+ name: (info) => basketDisplayName(info.basketName) + " Cycles",
204
+ route: { route: routes.RouteLocation.Analysis_BasketCycles },
205
+ icon: _jsx(PalletIcon, {}),
206
+ hidden: () => !hasBasketCycles,
207
+ },
208
+ { separator: "Cell" },
209
+ { name: "Quality", route: { route: routes.RouteLocation.Analysis_Quality }, icon: _jsx(BuildIcon, {}) },
210
+ {
211
+ name: "Tool Replacements",
212
+ route: { route: routes.RouteLocation.Analysis_ToolReplacements },
213
+ icon: _jsx(ToolIcon, {}),
214
+ },
215
+ {
216
+ name: "Schedules",
217
+ route: { route: routes.RouteLocation.Analysis_Schedules },
218
+ icon: _jsx(ScheduleIcon, {}),
219
+ },
220
+ { separator: "Costs" },
221
+ {
222
+ name: "Percentages",
223
+ route: { route: routes.RouteLocation.Analysis_CostPercents },
224
+ icon: _jsx(CallSplit, {}),
225
+ },
226
+ {
227
+ name: "Cost/Piece",
228
+ route: { route: routes.RouteLocation.Analysis_CostPerPiece },
229
+ icon: _jsx(CostIcon, {}),
230
+ },
231
+ ];
232
+ }
233
+ function isRouteLocation(value) {
234
+ return typeof value === "string" && Object.values(routes.RouteLocation).includes(value);
235
+ }
236
+ function isSimpleRouteLocation(value) {
237
+ return (isRouteLocation(value) &&
238
+ value !== routes.RouteLocation.Client_Custom &&
239
+ value !== routes.RouteLocation.Operations_CurrentWorkorders &&
240
+ value !== routes.RouteLocation.Station_InspectionMonitorWithType &&
241
+ value !== routes.RouteLocation.Station_LoadMonitor &&
242
+ value !== routes.RouteLocation.Station_Queues);
243
+ }
244
+ function toRouteState(simpleRoute) {
245
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- these routes do not carry extra payload.
246
+ return { route: simpleRoute };
247
+ }
222
248
  export function NavTabs({ children }) {
223
249
  const [route, setRoute] = useAtom(routes.currentRoute);
224
250
  const theme = useTheme();
@@ -229,8 +255,8 @@ export function NavTabs({ children }) {
229
255
  if (v === OperationsReportsTab) {
230
256
  setRoute({ route: routes.RouteLocation.Operations_MachineCycles });
231
257
  }
232
- else {
233
- setRoute({ route: v });
258
+ else if (isSimpleRouteLocation(v)) {
259
+ setRoute(toRouteState(v));
234
260
  }
235
261
  }), textColor: "inherit", scrollButtons: true, allowScrollButtonsMobile: true, indicatorColor: "secondary", children: children }));
236
262
  }
@@ -255,7 +281,10 @@ function SalesTabs() {
255
281
  const App = memo(function App(props) {
256
282
  routes.useWatchHistory();
257
283
  const fmsInfo = useAtomValue(serverSettings.fmsInformation);
284
+ const hasLast30BasketCycles = useAtomValue(last30HasBasketCycleData);
285
+ const hasSpecificMonthBasketCycles = useAtomValue(specificMonthHasBasketCycleData);
258
286
  const [route, setRoute] = useAtom(routes.currentRoute);
287
+ const analysisMenuNavItems = analysisReports(hasLast30BasketCycles || hasSpecificMonthBasketCycles);
259
288
  const showLogout = fmsInfo.user !== null && fmsInfo.user !== undefined;
260
289
  let page;
261
290
  let nav1 = undefined;
@@ -314,67 +343,73 @@ const App = memo(function App(props) {
314
343
  case routes.RouteLocation.Analysis_Quality:
315
344
  page = _jsx(AnalysisQualityPage, {});
316
345
  nav1 = AnalysisSelectToolbar;
317
- menuNavItems = analysisReports;
346
+ menuNavItems = analysisMenuNavItems;
318
347
  showAlarms = false;
319
348
  break;
320
349
  case routes.RouteLocation.Analysis_ToolReplacements:
321
350
  page = _jsx(ToolReplacementPage, {});
322
351
  nav1 = AnalysisSelectToolbar;
323
- menuNavItems = analysisReports;
352
+ menuNavItems = analysisMenuNavItems;
324
353
  showAlarms = false;
325
354
  break;
326
355
  case routes.RouteLocation.Analysis_Schedules:
327
356
  page = _jsx(ScheduleHistory, {});
328
357
  nav1 = AnalysisSelectToolbar;
329
- menuNavItems = analysisReports;
358
+ menuNavItems = analysisMenuNavItems;
330
359
  showAlarms = false;
331
360
  break;
332
361
  case routes.RouteLocation.Analysis_Buffers:
333
362
  page = _jsx(BufferOccupancyChart, {});
334
363
  nav1 = AnalysisSelectToolbar;
335
- menuNavItems = analysisReports;
364
+ menuNavItems = analysisMenuNavItems;
336
365
  showAlarms = false;
337
366
  break;
338
367
  case routes.RouteLocation.Analysis_StationOEE:
339
368
  page = _jsx(StationOeeHeatmap, {});
340
369
  nav1 = AnalysisSelectToolbar;
341
- menuNavItems = analysisReports;
370
+ menuNavItems = analysisMenuNavItems;
342
371
  showAlarms = false;
343
372
  break;
344
373
  case routes.RouteLocation.Analysis_PartsCompleted:
345
374
  page = _jsx(CompletedCountHeatmap, {});
346
375
  nav1 = AnalysisSelectToolbar;
347
- menuNavItems = analysisReports;
376
+ menuNavItems = analysisMenuNavItems;
348
377
  showAlarms = false;
349
378
  break;
350
379
  case routes.RouteLocation.Analysis_MachineCycles:
351
380
  page = _jsx(PartMachineCycleChart, {});
352
381
  nav1 = AnalysisSelectToolbar;
353
- menuNavItems = analysisReports;
382
+ menuNavItems = analysisMenuNavItems;
354
383
  showAlarms = false;
355
384
  break;
356
385
  case routes.RouteLocation.Analysis_LoadCycles:
357
386
  page = _jsx(PartLoadStationCycleChart, {});
358
387
  nav1 = AnalysisSelectToolbar;
359
- menuNavItems = analysisReports;
388
+ menuNavItems = analysisMenuNavItems;
360
389
  showAlarms = false;
361
390
  break;
362
391
  case routes.RouteLocation.Analysis_PalletCycles:
363
392
  page = _jsx(PalletCycleChart, {});
364
393
  nav1 = AnalysisSelectToolbar;
365
- menuNavItems = analysisReports;
394
+ menuNavItems = analysisMenuNavItems;
395
+ showAlarms = false;
396
+ break;
397
+ case routes.RouteLocation.Analysis_BasketCycles:
398
+ page = _jsx(BasketCycleChart, {});
399
+ nav1 = AnalysisSelectToolbar;
400
+ menuNavItems = analysisMenuNavItems;
366
401
  showAlarms = false;
367
402
  break;
368
403
  case routes.RouteLocation.Analysis_CostPercents:
369
404
  page = _jsx(CostBreakdownPage, {});
370
405
  nav1 = AnalysisSelectToolbar;
371
- menuNavItems = analysisReports;
406
+ menuNavItems = analysisMenuNavItems;
372
407
  showAlarms = false;
373
408
  break;
374
409
  case routes.RouteLocation.Analysis_CostPerPiece:
375
410
  page = _jsx(CostPerPiecePage, {});
376
411
  nav1 = AnalysisSelectToolbar;
377
- menuNavItems = analysisReports;
412
+ menuNavItems = analysisMenuNavItems;
378
413
  showAlarms = false;
379
414
  break;
380
415
  case routes.RouteLocation.Operations_Dashboard:
@@ -114,7 +114,12 @@ export const QRScanButton = memo(function QRScanButton() {
114
114
  setDialogOpen(false);
115
115
  setManual("");
116
116
  }
117
- return (_jsxs(_Fragment, { children: [_jsxs(Dialog, { open: dialogOpen, onClose: () => setDialogOpen(false), children: [_jsx(DialogTitle, { children: "Scan a QR Code" }), _jsxs(DialogContent, { children: [_jsx(Box, { width: "15em", height: "15em", children: dialogOpen ? _jsx(Scanner, { onScan: onScan }) : undefined }), _jsx(Box, { marginTop: "1em", children: _jsx(TextField, { label: "Manual Entry", value: manual, variant: "outlined", fullWidth: true, onKeyDown: (e) => {
117
+ return (_jsxs(_Fragment, { children: [_jsxs(Dialog, { open: dialogOpen, onClose: () => setDialogOpen(false), children: [_jsx(DialogTitle, { children: "Scan a QR Code" }), _jsxs(DialogContent, { children: [_jsx(Box, { sx: {
118
+ width: "15em",
119
+ height: "15em",
120
+ }, children: dialogOpen ? _jsx(Scanner, { onScan: onScan }) : undefined }), _jsx(Box, { sx: {
121
+ marginTop: "1em",
122
+ }, children: _jsx(TextField, { label: "Manual Entry", value: manual, variant: "outlined", fullWidth: true, onKeyDown: (e) => {
118
123
  if (e.key === "Enter") {
119
124
  onManual();
120
125
  }
@@ -142,7 +147,12 @@ export const AddByBarcodeDialog = memo(function AddByBarcodeDialog() {
142
147
  setManual("");
143
148
  }
144
149
  return (_jsxs(Dialog, { open: queue !== null, onClose: () => setQueue(null), maxWidth: "md", children: [_jsxs(DialogTitle, { children: ["Scan a Barcode To Add To ", queue] }), _jsxs(DialogContent, { children: [window.location.protocol === "https:" ||
145
- (import.meta.env.DEV && window.location.hostname === "localhost") ? (_jsx(Box, { width: "15em", height: "15em", children: queue !== null ? _jsx(Scanner, { onScan: onScan }) : undefined })) : undefined, _jsx(Box, { marginTop: "1em", children: _jsx(TextField, { label: "Manual Entry", value: manual, variant: "outlined", fullWidth: true, onKeyDown: (e) => {
150
+ (import.meta.env.DEV && window.location.hostname === "localhost") ? (_jsx(Box, { sx: {
151
+ width: "15em",
152
+ height: "15em",
153
+ }, children: queue !== null ? _jsx(Scanner, { onScan: onScan }) : undefined })) : undefined, _jsx(Box, { sx: {
154
+ marginTop: "1em",
155
+ }, children: _jsx(TextField, { label: "Manual Entry", value: manual, variant: "outlined", fullWidth: true, onKeyDown: (e) => {
146
156
  if (e.key === "Enter") {
147
157
  onManual();
148
158
  }
@@ -37,6 +37,15 @@ import { Card } from "@mui/material";
37
37
  import { CardContent } from "@mui/material";
38
38
  import { ErrorBoundary } from "react-error-boundary";
39
39
  import { ApiException } from "../network/api.js";
40
+ function displayUnknownError(error) {
41
+ if (typeof error === "string") {
42
+ return error;
43
+ }
44
+ if (typeof error === "number" || typeof error === "boolean" || typeof error === "bigint") {
45
+ return error.toString();
46
+ }
47
+ return JSON.stringify(error) ?? "";
48
+ }
40
49
  export function Loading() {
41
50
  return (_jsxs("div", { style: { textAlign: "center", marginTop: "4em" }, children: [_jsx(CircularProgress, {}), _jsx("p", { children: "Loading" })] }));
42
51
  }
@@ -50,7 +59,7 @@ export function DisplayError({ error }) {
50
59
  return (_jsx(Card, { children: _jsx(CardContent, { children: error.message }) }));
51
60
  }
52
61
  else {
53
- return (_jsx(Card, { children: _jsxs(CardContent, { children: [_jsx("h3", { children: "Unknown Error" }), _jsx("p", { children: String(error) })] }) }));
62
+ return (_jsx(Card, { children: _jsxs(CardContent, { children: [_jsx("h3", { children: "Unknown Error" }), _jsx("p", { children: displayUnknownError(error) })] }) }));
54
63
  }
55
64
  }
56
65
  export function DisplayLoadingAndError(props) {
@@ -7,7 +7,6 @@ export interface LogEntryProps {
7
7
  }
8
8
  export declare function isLogEntryInvalidated(e: api.ILogEntry): boolean;
9
9
  export declare const LogEntry: import("react").MemoExoticComponent<(props: LogEntryProps) => import("react/jsx-runtime").JSX.Element>;
10
- export declare function filterRemoveAddQueue(entries: Iterable<Readonly<api.ILogEntry>>): Iterable<Readonly<api.ILogEntry>>;
11
10
  export interface LogEntriesProps {
12
11
  entries: Iterable<Readonly<api.ILogEntry>>;
13
12
  copyToClipboard?: boolean;
@@ -44,6 +44,10 @@ import { ChevronRight as ChevronRightIcon, ImportExport } from "@mui/icons-mater
44
44
  import { copyLogEntriesToClipboard } from "../data/results.cycles.js";
45
45
  import { durationToMinutes, durationToSeconds } from "../util/parseISODuration.js";
46
46
  import { LazySeq } from "@seedtactics/immutable-collections";
47
+ import { useAtomValue } from "jotai";
48
+ import { fmsInformation } from "../network/server-settings.js";
49
+ import { basketDisplayName, loadStationDisplayName } from "../cell-status/station-cycles.js";
50
+ import { filterRemoveAddQueue } from "./log-entry-queue-filter.js";
47
51
  const ColoredSpan = styled("span", { shouldForwardProp: (prop) => prop.toString()[0] !== "$" })(({ $type }) => {
48
52
  switch ($type) {
49
53
  case "machine":
@@ -62,7 +66,8 @@ const ColoredSpan = styled("span", { shouldForwardProp: (prop) => prop.toString(
62
66
  return { color: "red" };
63
67
  }
64
68
  });
65
- function logType(entry) {
69
+ function logType(entry, fmsInfo) {
70
+ const basketName = basketDisplayName(fmsInfo.basketName);
66
71
  switch (entry.type) {
67
72
  case api.LogType.LoadUnloadCycle:
68
73
  if (entry.startofcycle) {
@@ -71,6 +76,22 @@ function logType(entry) {
71
76
  else {
72
77
  return "End " + entry.result.charAt(0).toUpperCase() + entry.result.substring(1).toLowerCase();
73
78
  }
79
+ case api.LogType.BasketLoadUnload:
80
+ if (entry.program === "LOAD") {
81
+ return `${basketName} Load`;
82
+ }
83
+ else {
84
+ return `${basketName} Unload`;
85
+ }
86
+ case api.LogType.BasketCycle:
87
+ return `${basketName} Cycle`;
88
+ case api.LogType.BasketInLocation:
89
+ if (entry.startofcycle) {
90
+ return "Depart";
91
+ }
92
+ else {
93
+ return "Arrive";
94
+ }
74
95
  case api.LogType.MachineCycle:
75
96
  if (entry.startofcycle) {
76
97
  return "Start Cycle";
@@ -133,7 +154,7 @@ function displayMat(mats) {
133
154
  }
134
155
  else if (mats.length == 1) {
135
156
  if (mats[0].numproc == 1) {
136
- return `${mats[0].part}`;
157
+ return mats[0].part;
137
158
  }
138
159
  else {
139
160
  return `${mats[0].part}-${mats[0].proc}`;
@@ -154,11 +175,33 @@ function displayQueueMat(mats) {
154
175
  return "";
155
176
  }
156
177
  }
157
- function display(props) {
178
+ function display(props, fmsInfo) {
158
179
  const entry = props.entry;
159
180
  switch (entry.type) {
160
181
  case api.LogType.LoadUnloadCycle:
161
- return (_jsxs("span", { children: [displayMat(entry.material), " on ", _jsxs(ColoredSpan, { "$type": "pallet", children: ["pallet ", entry.pal] }), " at", " ", _jsxs(ColoredSpan, { "$type": "loadStation", children: ["station ", entry.locnum.toString()] })] }));
182
+ return (_jsxs("span", { children: [displayMat(entry.material), " on ", _jsxs(ColoredSpan, { "$type": "pallet", children: ["pallet ", entry.pal] }), " at", " ", _jsx(ColoredSpan, { "$type": "loadStation", children: loadStationDisplayName(entry.locnum, fmsInfo.loadStationNames) })] }));
183
+ case api.LogType.BasketLoadUnload: {
184
+ const basketName = basketDisplayName(fmsInfo.basketName);
185
+ return (_jsxs("span", { children: [displayMat(entry.material), " ", entry.program === "LOAD" ? "loaded onto" : "unloaded from", " ", _jsxs(ColoredSpan, { "$type": "pallet", children: [basketName, " ", entry.pal] }), " ", "at", " ", _jsx(ColoredSpan, { "$type": "loadStation", children: loadStationDisplayName(entry.locnum, fmsInfo.loadStationNames) })] }));
186
+ }
187
+ case api.LogType.BasketCycle: {
188
+ const basketName = basketDisplayName(fmsInfo.basketName);
189
+ if (entry.startofcycle) {
190
+ return (_jsxs("span", { children: [basketName, " ", entry.pal, " started cycle"] }));
191
+ }
192
+ else {
193
+ return (_jsxs("span", { children: [basketName, " ", entry.pal, " completed cycle"] }));
194
+ }
195
+ }
196
+ case api.LogType.BasketInLocation: {
197
+ const basketName = basketDisplayName(fmsInfo.basketName);
198
+ if (entry.startofcycle) {
199
+ return (_jsxs("span", { children: [basketName, " ", entry.pal, " departed from ", entry.loc, entry.locnum > 0 ? ` position ${entry.locnum}` : ""] }));
200
+ }
201
+ else {
202
+ return (_jsxs("span", { children: [basketName, " ", entry.pal, " arrived at ", entry.loc, entry.locnum > 0 ? ` position ${entry.locnum}` : ""] }));
203
+ }
204
+ }
162
205
  case api.LogType.MachineCycle:
163
206
  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] }));
164
207
  case api.LogType.PartMark:
@@ -312,6 +355,7 @@ const logTypesToHighlight = [
312
355
  api.LogType.AddToQueue,
313
356
  api.LogType.RemoveFromQueue,
314
357
  api.LogType.LoadUnloadCycle,
358
+ api.LogType.BasketLoadUnload,
315
359
  api.LogType.MachineCycle,
316
360
  ];
317
361
  const LogEntryTableRow = styled(TableRow, { shouldForwardProp: (prop) => prop.toString()[0] !== "$" })(({ $highlightProc, $invalidCycle }) => ({
@@ -321,6 +365,7 @@ const LogEntryTableRow = styled(TableRow, { shouldForwardProp: (prop) => prop.to
321
365
  export const LogEntry = memo(function LogEntry(props) {
322
366
  const details = detailsForEntry(props.entry);
323
367
  const highlight = props.highlightProcsGreaterOrEqualTo;
368
+ const fmsInfo = useAtomValue(fmsInformation);
324
369
  return (_jsxs(_Fragment, { children: [_jsxs(LogEntryTableRow, { "$highlightProc": highlight !== undefined &&
325
370
  props.entry.material.findIndex((m) => m.proc >= highlight) >= 0 &&
326
371
  logTypesToHighlight.indexOf(props.entry.type) >= 0, "$invalidCycle": isLogEntryInvalidated(props.entry), children: [_jsx(TableCell, { size: "small", children: props.entry.endUTC.toLocaleDateString(undefined, {
@@ -330,7 +375,7 @@ export const LogEntry = memo(function LogEntry(props) {
330
375
  }) }), _jsx(TableCell, { size: "small", children: props.entry.endUTC.toLocaleTimeString(undefined, {
331
376
  hour: "numeric",
332
377
  minute: "2-digit",
333
- }) }), _jsx(TableCell, { size: "small", children: logType(props.entry) }), _jsx(TableCell, { size: "small", children: display(props) }), _jsx(TableCell, { padding: "checkbox", children: details.length > 0 ? (_jsx(IconButton, { style: {
378
+ }) }), _jsx(TableCell, { size: "small", children: logType(props.entry, fmsInfo) }), _jsx(TableCell, { size: "small", children: display(props, fmsInfo) }), _jsx(TableCell, { padding: "checkbox", children: details.length > 0 ? (_jsx(IconButton, { style: {
334
379
  transition: "all ease 200ms",
335
380
  transform: props.entry.counter === props.detailLogCounter ? "rotate(90deg)" : "none",
336
381
  }, onClick: (event) => {
@@ -338,27 +383,6 @@ export const LogEntry = memo(function LogEntry(props) {
338
383
  event.stopPropagation();
339
384
  }, size: "small", children: _jsx(ChevronRightIcon, { fontSize: "inherit" }) })) : undefined })] }), details.length > 0 && props.entry.counter === props.detailLogCounter ? (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: 5, children: _jsx("ul", { children: details.map((d, idx) => (_jsxs("li", { children: [d.name, ": ", d.value] }, idx))) }) }) })) : undefined] }));
340
385
  });
341
- export function* filterRemoveAddQueue(entries) {
342
- let prev = null;
343
- for (const e of entries) {
344
- if (prev != null &&
345
- prev.type === api.LogType.RemoveFromQueue &&
346
- e.type === api.LogType.AddToQueue &&
347
- prev.loc === e.loc) {
348
- // skip both prev and e
349
- prev = null;
350
- }
351
- else {
352
- if (prev !== null) {
353
- yield prev;
354
- }
355
- prev = e;
356
- }
357
- }
358
- if (prev !== null) {
359
- yield prev;
360
- }
361
- }
362
386
  export const LogEntries = memo(function LogEntriesF(props) {
363
387
  const [curDetail, setDetail] = useState(null);
364
388
  return (_jsxs(Table, { children: [_jsx(TableHead, { children: _jsxs(TableRow, { children: [_jsx(TableCell, { children: "Date" }), _jsx(TableCell, { children: "Time" }), _jsx(TableCell, { children: "Type" }), _jsx(TableCell, { children: "Details" }), _jsx(TableCell, { padding: "checkbox", children: props.copyToClipboard ? (_jsx(Tooltip, { title: "Copy to Clipboard", children: _jsx(IconButton, { onClick: () => copyLogEntriesToClipboard(props.entries), style: { height: "25px", paddingTop: 0, paddingBottom: 0 }, size: "large", children: _jsx(ImportExport, {}) }) })) : undefined })] }) }), _jsx(TableBody, { children: Array.from(filterRemoveAddQueue(props.entries)).map((e, idx) => (_jsx(LogEntry, { entry: e, detailLogCounter: curDetail, setDetail: setDetail, highlightProcsGreaterOrEqualTo: props.highlightProcsGreaterOrEqualTo }, idx))) })] }));