@seedtactics/insight-client 16.6.0 → 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.
- package/dist/assets/ProgramHighlight-DPTeZ8Si.js +3 -0
- package/dist/assets/index-bPAFn3jp.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 +29 -31
- package/dist/assets/ProgramHighlight-LvRM40Qr.js +0 -3
- package/dist/assets/index-gAFi3Oss.js +0 -364
|
@@ -32,7 +32,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
32
32
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
33
33
|
*/
|
|
34
34
|
import { useTransition } from "react";
|
|
35
|
-
import { Notifications,
|
|
35
|
+
import { Notifications, HelpOutlined, ExitToApp } from "@mui/icons-material";
|
|
36
36
|
import { Typography, Tooltip, Badge, IconButton, AppBar, Toolbar, Box, List, ListItem, ListItemIcon, ListItemButton, ListItemText, ListSubheader, Paper, Select, MenuItem, FormControl, } from "@mui/material";
|
|
37
37
|
import { startOfToday, differenceInDays } from "date-fns";
|
|
38
38
|
import { currentStatus } from "../cell-status/current-status";
|
|
@@ -77,7 +77,7 @@ function Alarms() {
|
|
|
77
77
|
}
|
|
78
78
|
function HelpButton() {
|
|
79
79
|
const route = useAtomValue(currentRoute);
|
|
80
|
-
return (_jsx(Tooltip, { title: "Help", children: _jsx(IconButton, { "aria-label": "Help", href: helpUrl(route), target: "_help", size: "large", children: _jsx(
|
|
80
|
+
return (_jsx(Tooltip, { title: "Help", children: _jsx(IconButton, { "aria-label": "Help", href: helpUrl(route), target: "_help", size: "large", children: _jsx(HelpOutlined, {}) }) }));
|
|
81
81
|
}
|
|
82
82
|
function LogoutButton() {
|
|
83
83
|
return (_jsx(Tooltip, { title: "Logout", children: _jsx(IconButton, { "aria-label": "Logout", onClick: logout, size: "large", children: _jsx(ExitToApp, {}) }) }));
|
|
@@ -98,11 +98,37 @@ function MenuNavSelect({ menuNavs }) {
|
|
|
98
98
|
const item = menuNavs.find((i) => ("separator" in i ? false : i.route.route === curRoute.route));
|
|
99
99
|
if (!item || "separator" in item)
|
|
100
100
|
return null;
|
|
101
|
-
return (_jsxs(Box, {
|
|
101
|
+
return (_jsxs(Box, { sx: {
|
|
102
|
+
display: "flex",
|
|
103
|
+
alignItems: "center",
|
|
104
|
+
}, children: [item.icon, _jsxs(Typography, { variant: "h6", style: { marginLeft: "1em" }, children: [typeof item.name === "string" ? item.name : item.name(fmsInfo), isPending ? "..." : ""] })] }));
|
|
102
105
|
}, children: menuNavs.map((item, idx) => "separator" in item ? (_jsx(ListSubheader, { children: item.separator }, item.separator)) : item.hidden?.(fmsInfo) ? undefined : (_jsxs(MenuItem, { value: item.route.route, children: [_jsx(ListItemIcon, { children: item.icon }), _jsx(ListItemText, { primary: typeof item.name === "string" ? item.name : item.name(fmsInfo) })] }, idx))) }) }));
|
|
103
106
|
}
|
|
104
107
|
export function Header({ showAlarms, showLogout, showSearch, showOperator, Nav1, Nav2, menuNavs, }) {
|
|
105
|
-
return (_jsxs(_Fragment, { children: [_jsx(AppBar, { position: "static", sx: { display: { xs: "none", md: "block" } }, children: _jsx(Toolbar, { children: _jsxs(Box, {
|
|
108
|
+
return (_jsxs(_Fragment, { children: [_jsx(AppBar, { position: "static", sx: { display: { xs: "none", md: "block" } }, children: _jsx(Toolbar, { children: _jsxs(Box, { sx: {
|
|
109
|
+
display: "grid",
|
|
110
|
+
gridTemplateColumns: Nav2 ? "1fr auto 1fr" : "minmax(200px, 1fr) auto",
|
|
111
|
+
width: "100vw",
|
|
112
|
+
gridTemplateAreas: Nav2 ? '"nav navCenter tools"' : '"nav tools"',
|
|
113
|
+
}, children: [_jsxs(Box, { sx: {
|
|
114
|
+
gridArea: "nav",
|
|
115
|
+
display: "flex",
|
|
116
|
+
alignItems: "center",
|
|
117
|
+
}, children: [_jsx(Brand, {}), Nav1 ? _jsx(Nav1, {}) : undefined] }), Nav2 ? (_jsx(Box, { sx: {
|
|
118
|
+
gridArea: "navCenter",
|
|
119
|
+
}, children: _jsx(Nav2, {}) })) : undefined, _jsxs(Box, { sx: {
|
|
120
|
+
gridArea: "tools",
|
|
121
|
+
display: "flex",
|
|
122
|
+
alignItems: "center",
|
|
123
|
+
justifyContent: "flex-end",
|
|
124
|
+
}, children: [showOperator ? _jsx(OperatorSelect, {}) : undefined, _jsx(ToolButtons, { showAlarms, showLogout, showSearch })] })] }) }) }), _jsxs(AppBar, { position: "static", sx: { display: { xs: "block", md: "none" } }, children: [_jsxs(Toolbar, { children: [_jsx(Brand, {}), _jsx("div", { style: { flexGrow: 1 } }), _jsx(ToolButtons, { showAlarms, showLogout, showSearch })] }), _jsxs(Box, { sx: {
|
|
125
|
+
display: "grid",
|
|
126
|
+
gridTemplateColumns: "minmax(200px, 1fr) auto",
|
|
127
|
+
}, children: [_jsx(Box, { sx: {
|
|
128
|
+
gridColumn: "1",
|
|
129
|
+
}, children: Nav1 ? _jsx(Nav1, {}) : undefined }), _jsx(Box, { sx: {
|
|
130
|
+
gridColumn: "2",
|
|
131
|
+
}, children: Nav2 ? _jsx(Nav2, {}) : undefined })] })] }), menuNavs && menuNavs.length > 0 ? (_jsx(Paper, { elevation: 4, square: true, component: "nav", sx: { display: { xs: "block", xl: "none" }, padding: "2px" }, children: _jsx(MenuNavSelect, { menuNavs: menuNavs }) })) : undefined] }));
|
|
106
132
|
}
|
|
107
133
|
export function SideMenu({ menuItems }) {
|
|
108
134
|
const fmsInfo = useAtomValue(fmsInformation);
|
|
@@ -42,7 +42,11 @@ export const AnalysisSelectToolbar = memo(function AnalysisSelectToolbar() {
|
|
|
42
42
|
const [selMonth, analyzeMonth] = useAtom(selectedMonth);
|
|
43
43
|
const setMonthWithoutLoading = useSetSpecificMonthWithoutLoading();
|
|
44
44
|
const setLast30 = useSetLast30();
|
|
45
|
-
return (_jsxs(Stack, { component: "nav", direction: "row", spacing: 3,
|
|
45
|
+
return (_jsxs(Stack, { component: "nav", direction: "row", spacing: 3, sx: {
|
|
46
|
+
paddingLeft: "24px",
|
|
47
|
+
paddingRight: "24px",
|
|
48
|
+
alignItems: "center",
|
|
49
|
+
}, children: [_jsx(FormControlLabel, { control: _jsx(Radio, { checked: period.type === "Last30", color: "secondary", onChange: (e, checked) => (checked ? setLast30() : null) }), label: "Last 30 days" }), _jsxs("div", { style: { display: "flex", alignItems: "center" }, children: [_jsx(FormControlLabel, { control: _jsx(Radio, { checked: period.type === "SpecificMonth", color: "secondary", onChange: (e, checked) => (checked ? analyzeMonth(selMonth) : null) }), label: "Select Month" }), _jsx(MonthSelect, { curMonth: selMonth, onSelectMonth: (m) => {
|
|
46
50
|
if (period.type === "SpecificMonth") {
|
|
47
51
|
// if month type is selected, reload data
|
|
48
52
|
analyzeMonth(m);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function BasketCycleChart(): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/* Copyright (c) 2026, John Lenz
|
|
3
|
+
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
* Redistributions of source code must retain the above copyright
|
|
10
|
+
notice, this list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
* Redistributions in binary form must reproduce the above
|
|
13
|
+
copyright notice, this list of conditions and the following
|
|
14
|
+
disclaimer in the documentation and/or other materials provided
|
|
15
|
+
with the distribution.
|
|
16
|
+
|
|
17
|
+
* Neither the name of John Lenz, Black Maple Software, SeedTactics,
|
|
18
|
+
nor the names of other contributors may be used to endorse or
|
|
19
|
+
promote products derived from this software without specific
|
|
20
|
+
prior written permission.
|
|
21
|
+
|
|
22
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
23
|
+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
24
|
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
25
|
+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
26
|
+
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
27
|
+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
28
|
+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
29
|
+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
30
|
+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
31
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
32
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
33
|
+
*/
|
|
34
|
+
import { addMonths, addDays, startOfToday } from "date-fns";
|
|
35
|
+
import { Box, FormControl, Typography, Tooltip, IconButton, MenuItem, Select } from "@mui/material";
|
|
36
|
+
import { ImportExport } from "@mui/icons-material";
|
|
37
|
+
import { useCallback } from "react";
|
|
38
|
+
import { atom, useAtom, useAtomValue, useSetAtom } from "jotai";
|
|
39
|
+
import { LazySeq } from "@seedtactics/immutable-collections";
|
|
40
|
+
import { selectedAnalysisPeriod } from "../../network/load-specific-month.js";
|
|
41
|
+
import { CycleChart } from "./CycleChart.js";
|
|
42
|
+
import { copyPalletCyclesToClipboard } from "../../data/results.cycles.js";
|
|
43
|
+
import { useSetTitle } from "../routes.js";
|
|
44
|
+
import { last30BasketCycles, specificMonthBasketCycles, } from "../../cell-status/basket-cycles.js";
|
|
45
|
+
import { PartIdenticon } from "../station-monitor/Material.js";
|
|
46
|
+
import { materialDialogOpen } from "../../cell-status/material-details.js";
|
|
47
|
+
import { fmsInformation } from "../../network/server-settings.js";
|
|
48
|
+
import { basketDisplayName } from "../../cell-status/station-cycles.js";
|
|
49
|
+
const selectedBasketAtom = atom(undefined);
|
|
50
|
+
const selectedPartAtom = atom(undefined);
|
|
51
|
+
const zoomDateRangeAtom = atom(undefined);
|
|
52
|
+
function isBasketCycleData(point) {
|
|
53
|
+
return "mats" in point && Array.isArray(point.mats);
|
|
54
|
+
}
|
|
55
|
+
const yZoomAtom = atom(null);
|
|
56
|
+
const filteredPoints = atom((get) => {
|
|
57
|
+
const selBasket = get(selectedBasketAtom);
|
|
58
|
+
const selPart = get(selectedPartAtom);
|
|
59
|
+
if (!selBasket && !selPart)
|
|
60
|
+
return new Map();
|
|
61
|
+
const period = get(selectedAnalysisPeriod);
|
|
62
|
+
const basketCycles = get(period.type === "Last30" ? last30BasketCycles : specificMonthBasketCycles);
|
|
63
|
+
if (selBasket) {
|
|
64
|
+
let cyclesForBasket = basketCycles.get(selBasket)?.valuesToLazySeq() ?? LazySeq.of([]);
|
|
65
|
+
if (selPart) {
|
|
66
|
+
cyclesForBasket = cyclesForBasket.filter((c) => c.mats.some((m) => m.part === selPart.part && m.proc === selPart.proc));
|
|
67
|
+
}
|
|
68
|
+
return new Map([
|
|
69
|
+
[selBasket.toString(), Array.from(cyclesForBasket)],
|
|
70
|
+
]);
|
|
71
|
+
}
|
|
72
|
+
else if (selPart) {
|
|
73
|
+
return LazySeq.of(basketCycles)
|
|
74
|
+
.collect(([basket, cycles]) => {
|
|
75
|
+
const cyclesForPart = cycles
|
|
76
|
+
.valuesToLazySeq()
|
|
77
|
+
.filter((c) => c.mats.some((m) => m.part === selPart.part && m.proc === selPart.proc))
|
|
78
|
+
.toRArray();
|
|
79
|
+
if (cyclesForPart.length > 0) {
|
|
80
|
+
return [basket, cyclesForPart];
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
})
|
|
86
|
+
.toRMap(([basket, cycles]) => [basket.toString(), cycles]);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
return new Map();
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
const allPartNames = atom((get) => {
|
|
93
|
+
const period = get(selectedAnalysisPeriod);
|
|
94
|
+
const basketCycles = get(period.type === "Last30" ? last30BasketCycles : specificMonthBasketCycles);
|
|
95
|
+
return LazySeq.of(basketCycles)
|
|
96
|
+
.flatMap(([, cycles]) => cycles)
|
|
97
|
+
.flatMap(([, c]) => c.mats)
|
|
98
|
+
.distinctAndSortBy((m) => m.part, (m) => m.proc)
|
|
99
|
+
.map((m) => ({ part: m.part, proc: m.proc }))
|
|
100
|
+
.toRArray();
|
|
101
|
+
});
|
|
102
|
+
export function BasketCycleChart() {
|
|
103
|
+
const fmsInfo = useAtomValue(fmsInformation);
|
|
104
|
+
const basketName = basketDisplayName(fmsInfo.basketName);
|
|
105
|
+
useSetTitle(`${basketName} Cycles`);
|
|
106
|
+
const [selectedBasket, setSelectedBasket] = useAtom(selectedBasketAtom);
|
|
107
|
+
const [selectedPart, setSelectedPart] = useAtom(selectedPartAtom);
|
|
108
|
+
const [zoomDateRange, setZoomRange] = useAtom(zoomDateRangeAtom);
|
|
109
|
+
const [yZoom, setYZoom] = useAtom(yZoomAtom);
|
|
110
|
+
const period = useAtomValue(selectedAnalysisPeriod);
|
|
111
|
+
const defaultDateRange = period.type === "Last30"
|
|
112
|
+
? [addDays(startOfToday(), -29), addDays(startOfToday(), 1)]
|
|
113
|
+
: [period.month, addMonths(period.month, 1)];
|
|
114
|
+
const basketCycles = useAtomValue(period.type === "Last30" ? last30BasketCycles : specificMonthBasketCycles);
|
|
115
|
+
const points = useAtomValue(filteredPoints);
|
|
116
|
+
const partNames = useAtomValue(allPartNames);
|
|
117
|
+
const setMatToShow = useSetAtom(materialDialogOpen);
|
|
118
|
+
const extraBasketTooltip = useCallback(function extraBasketTooltip(point) {
|
|
119
|
+
if (!isBasketCycleData(point)) {
|
|
120
|
+
return [];
|
|
121
|
+
}
|
|
122
|
+
return point.mats.map((mat) => ({
|
|
123
|
+
title: mat.part + "-" + mat.proc,
|
|
124
|
+
value: mat.serial ?? "",
|
|
125
|
+
link: mat.serial ? () => setMatToShow({ type: "LogMat", logMat: mat }) : undefined,
|
|
126
|
+
}));
|
|
127
|
+
}, [setMatToShow]);
|
|
128
|
+
return (_jsxs(Box, { sx: {
|
|
129
|
+
paddingLeft: "24px",
|
|
130
|
+
paddingRight: "24px",
|
|
131
|
+
paddingTop: "10px",
|
|
132
|
+
}, children: [_jsxs(Box, { component: "nav", sx: {
|
|
133
|
+
display: "flex",
|
|
134
|
+
minHeight: "2.5em",
|
|
135
|
+
alignItems: "center",
|
|
136
|
+
maxWidth: "calc(100vw - 24px - 24px)",
|
|
137
|
+
}, children: [_jsxs(Typography, { variant: "subtitle1", children: [basketName, " Cycles"] }), _jsx(Box, { sx: { flexGrow: 1 } }), _jsx(FormControl, { size: "small", children: _jsxs(Select, { autoWidth: true, displayEmpty: true, value: selectedBasket ?? -1, onChange: (e) => setSelectedBasket(e.target.value === -1 ? undefined : (e.target.value)), children: [_jsx(MenuItem, { value: -1, children: _jsxs("em", { children: ["Any ", basketName] }) }, 0), basketCycles
|
|
138
|
+
.keysToLazySeq()
|
|
139
|
+
.sortBy((x) => x)
|
|
140
|
+
.map((n) => (_jsx(MenuItem, { value: n, children: _jsx("div", { style: { display: "flex", alignItems: "center" }, children: _jsx("span", { style: { marginRight: "1em" }, children: n }) }) }, n)))] }) }), partNames.length > 0 ? (_jsx(FormControl, { size: "small", children: _jsxs(Select, { autoWidth: true, displayEmpty: true, value: selectedPart
|
|
141
|
+
? partNames.findIndex((o) => selectedPart.part === o.part && selectedPart.proc === o.proc)
|
|
142
|
+
: -1, style: { marginLeft: "1em" }, onChange: (e) => {
|
|
143
|
+
setSelectedPart(e.target.value === -1 ? undefined : partNames[e.target.value]);
|
|
144
|
+
}, children: [_jsx(MenuItem, { value: -1, children: _jsx("em", { children: "Any Part" }) }, 0), partNames.map((n, idx) => (_jsx(MenuItem, { value: idx, children: _jsxs("div", { style: { display: "flex", alignItems: "center" }, children: [_jsx(PartIdenticon, { part: n.part, size: 20 }), _jsxs("span", { style: { marginRight: "1em" }, children: [n.part, "-", n.proc] })] }) }, idx)))] }) })) : undefined, _jsx(Tooltip, { title: "Copy to Clipboard", children: _jsx(IconButton, { onClick: () => copyPalletCyclesToClipboard(basketCycles, basketName), style: { height: "25px", paddingTop: 0, paddingBottom: 0 }, size: "large", children: _jsx(ImportExport, {}) }) })] }), _jsx("main", { children: _jsx(CycleChart, { points: points, series_label: basketName, default_date_range: defaultDateRange, current_date_zoom: zoomDateRange, set_date_zoom_range: (z) => setZoomRange(z.zoom), yZoom: yZoom, setYZoom: setYZoom, extra_tooltip: extraBasketTooltip }) })] }));
|
|
145
|
+
}
|
|
@@ -55,7 +55,7 @@ const AnimatedPath = memo(function AnimatedPath({ series, xScale, yScale, color,
|
|
|
55
55
|
.y((p) => yScale(p.y))(series.points) ?? "";
|
|
56
56
|
// don't update in quick succession
|
|
57
57
|
const previous = useRef(d);
|
|
58
|
-
//
|
|
58
|
+
// oxlint-disable-next-line react/exhaustive-deps
|
|
59
59
|
const setPrevious = useCallback(debounce((val) => {
|
|
60
60
|
previous.current = val;
|
|
61
61
|
}, 50), []);
|
|
@@ -125,15 +125,21 @@ const BufferChart = memo(function BufferChart(props) {
|
|
|
125
125
|
}, children: width && height && width > 0 && height > 0 && (_jsx(BufferChartSVG, { width: width, height: height, series: series, disabled: disabledBuffers })) }), _jsx("div", { style: { marginTop: "1em", display: "flex", flexWrap: "wrap", justifyContent: "space-around" }, children: series.map((s, idx) => (_jsx(ToggleButton, { selected: !disabledBuffers.has(s.label), value: s.label, onChange: () => setDisabledBuffers((db) => (db.has(s.label) ? db.delete(s.label) : db.add(s.label))), children: _jsxs("div", { style: { display: "flex", alignItems: "center" }, children: [_jsx("div", { style: { width: "14px", height: "14px", backgroundColor: seriesColor(idx, series.length) } }), _jsx("div", { style: { marginLeft: "1em" }, children: s.label })] }) }, s.label))) })] }));
|
|
126
126
|
});
|
|
127
127
|
// https://github.com/mui-org/material-ui/issues/20191
|
|
128
|
-
//
|
|
128
|
+
// oxlint-disable-next-line typescript/no-explicit-any
|
|
129
129
|
const SliderAny = Slider;
|
|
130
130
|
export function BufferOccupancyChart() {
|
|
131
131
|
useSetTitle("Buffer Occupancy");
|
|
132
132
|
const [movingAverageHours, setMovingAverage] = useState(12);
|
|
133
|
-
return (_jsxs(Box, {
|
|
133
|
+
return (_jsxs(Box, { sx: {
|
|
134
|
+
paddingLeft: "24px",
|
|
135
|
+
paddingRight: "24px",
|
|
136
|
+
paddingTop: "10px",
|
|
137
|
+
}, children: [_jsxs(Box, { component: "nav", sx: {
|
|
134
138
|
display: "flex",
|
|
135
139
|
minHeight: "2.5em",
|
|
136
140
|
alignItems: "center",
|
|
137
141
|
maxWidth: "calc(100vw - 24px - 24px)",
|
|
138
|
-
}, children: [_jsx(Typography, { variant: "subtitle1", children: "Average material occupancy of rotary tables and stocker positions over time" }), _jsx(Box, {
|
|
142
|
+
}, children: [_jsx(Typography, { variant: "subtitle1", children: "Average material occupancy of rotary tables and stocker positions over time" }), _jsx(Box, { sx: {
|
|
143
|
+
flexGrow: 1,
|
|
144
|
+
} }), _jsx("span", { style: { fontSize: "small", marginRight: "1em" }, children: "Moving Average Window: " }), _jsx(SliderAny, { style: { width: "10em" }, min: 1, max: 36, steps: 0.2, valueLabelDisplay: "off", value: movingAverageHours, onChange: (e, v) => setMovingAverage(v) })] }), _jsx("main", { children: _jsx(BufferChart, { movingAverageDistanceInHours: movingAverageHours }) })] }));
|
|
139
145
|
}
|
|
@@ -91,12 +91,14 @@ const computedCosts = atom((get) => {
|
|
|
91
91
|
function AutomationCostInput() {
|
|
92
92
|
const [cost, setCost] = useState(null);
|
|
93
93
|
const [automationCost, saveAutomationCostPerYear] = useAtom(automationCostPerYear);
|
|
94
|
-
return (_jsx(TextField, { id: "auotmation-cost-year", type: "number", label: "Cost for automated handling system per year", style: { marginTop: "1.5em" },
|
|
94
|
+
return (_jsx(TextField, { id: "auotmation-cost-year", type: "number", label: "Cost for automated handling system per year", style: { marginTop: "1.5em" }, variant: "outlined", value: cost === null ? (automationCost ?? "") : isNaN(cost) ? "" : cost, onChange: (e) => setCost(parseFloat(e.target.value)), onBlur: () => {
|
|
95
95
|
if (cost != null) {
|
|
96
96
|
const newCost = isNaN(cost) ? null : cost;
|
|
97
97
|
saveAutomationCostPerYear(newCost);
|
|
98
98
|
}
|
|
99
99
|
setCost(null);
|
|
100
|
+
}, slotProps: {
|
|
101
|
+
htmlInput: { min: 0 },
|
|
100
102
|
} }));
|
|
101
103
|
}
|
|
102
104
|
function LaborCost() {
|
|
@@ -107,18 +109,20 @@ function LaborCost() {
|
|
|
107
109
|
return (_jsx(TextField, { type: "number", label: "Total labor cost for " +
|
|
108
110
|
(month === null
|
|
109
111
|
? "last 30 days"
|
|
110
|
-
: month.toLocaleDateString(undefined, { month: "long", year: "numeric" })),
|
|
112
|
+
: month.toLocaleDateString(undefined, { month: "long", year: "numeric" })), variant: "outlined", value: cost === null ? (laborCost ?? "") : isNaN(cost) ? "" : cost, onChange: (e) => setCost(parseFloat(e.target.value)), onBlur: () => {
|
|
111
113
|
if (cost != null) {
|
|
112
114
|
const newCost = isNaN(cost) ? null : cost;
|
|
113
115
|
saveCost(newCost);
|
|
114
116
|
}
|
|
115
117
|
setCost(null);
|
|
118
|
+
}, slotProps: {
|
|
119
|
+
htmlInput: { min: 0 },
|
|
116
120
|
} }));
|
|
117
121
|
}
|
|
118
122
|
function SingleStationCostInput(props) {
|
|
119
123
|
const [cost, setCost] = useState(null);
|
|
120
124
|
const [machineCost, saveCost] = useAtom(machineCostPerYear);
|
|
121
|
-
return (_jsx(TextField, { type: "number",
|
|
125
|
+
return (_jsx(TextField, { type: "number", variant: "outlined", label: "Cost for " + props.machineGroup + " per station per year", style: { marginTop: "1.5em" }, value: cost === null ? (machineCost[props.machineGroup] ?? "") : isNaN(cost) ? "" : cost, onChange: (e) => setCost(parseFloat(e.target.value)), onBlur: () => {
|
|
122
126
|
if (cost != null) {
|
|
123
127
|
const newCost = { ...machineCost };
|
|
124
128
|
if (isNaN(cost)) {
|
|
@@ -130,6 +134,8 @@ function SingleStationCostInput(props) {
|
|
|
130
134
|
saveCost(newCost);
|
|
131
135
|
}
|
|
132
136
|
setCost(null);
|
|
137
|
+
}, slotProps: {
|
|
138
|
+
htmlInput: { min: 0 },
|
|
133
139
|
} }));
|
|
134
140
|
}
|
|
135
141
|
function StationCostInputs() {
|
|
@@ -148,17 +154,25 @@ const pctFormat = new Intl.NumberFormat(undefined, {
|
|
|
148
154
|
export function CostBreakdownPage() {
|
|
149
155
|
useSetTitle("Cost Percentages");
|
|
150
156
|
const costs = useAtomValue(costPercentages);
|
|
151
|
-
return (_jsxs(Box, {
|
|
157
|
+
return (_jsxs(Box, { sx: {
|
|
158
|
+
paddingLeft: "24px",
|
|
159
|
+
paddingRight: "24px",
|
|
160
|
+
paddingTop: "10px",
|
|
161
|
+
}, children: [_jsxs(Box, { component: "nav", sx: {
|
|
152
162
|
display: "flex",
|
|
153
163
|
minHeight: "2.5em",
|
|
154
164
|
alignItems: "center",
|
|
155
165
|
maxWidth: "calc(100vw - 24px - 24px)",
|
|
156
|
-
}, children: [_jsx(Typography, { variant: "subtitle1", children: "Part Cost Percentage Breakdown" }), _jsx(Box, {
|
|
166
|
+
}, children: [_jsx(Typography, { variant: "subtitle1", children: "Part Cost Percentage Breakdown" }), _jsx(Box, { sx: {
|
|
167
|
+
flexGrow: 1,
|
|
168
|
+
} }), _jsx(Tooltip, { title: "Copy to Clipboard", children: _jsx(IconButton, { style: { height: "25px", paddingTop: 0, paddingBottom: 0 }, onClick: () => copyCostBreakdownToClipboard(costs), size: "large", children: _jsx(ImportExport, {}) }) })] }), _jsx("main", { children: _jsxs(Table, { stickyHeader: true, children: [_jsx(TableHead, { children: _jsxs(TableRow, { children: [_jsx(TableCell, { children: "Part" }), _jsx(TableCell, { align: "right", children: "Completed Quantity" }), costs.machineQuantities.keysToAscLazySeq().map((m) => (_jsxs(TableCell, { align: "right", children: [m, " Cost %"] }, m))), _jsx(TableCell, { align: "right", children: "Labor Cost %" }), _jsx(TableCell, { align: "right", children: "Automation Cost %" })] }) }), _jsx(TableBody, { children: LazySeq.of(costs.parts)
|
|
157
169
|
.sortBy((c) => c.part)
|
|
158
170
|
.map((c, idx) => (_jsxs(TableRow, { children: [_jsx(TableCell, { children: _jsxs(Box, { sx: {
|
|
159
171
|
display: "flex",
|
|
160
172
|
alignItems: "center",
|
|
161
|
-
}, children: [_jsx(Box, { sx: { mr: "0.2em" }, children: _jsx(PartIdenticon, { part: c.part, size: 25 }) }), _jsx("div", { children: _jsx(Typography, { variant: "body2", component: "span",
|
|
173
|
+
}, children: [_jsx(Box, { sx: { mr: "0.2em" }, children: _jsx(PartIdenticon, { part: c.part, size: 25 }) }), _jsx("div", { children: _jsx(Typography, { variant: "body2", component: "span", sx: {
|
|
174
|
+
display: "block",
|
|
175
|
+
}, children: c.part }) })] }) }), _jsx(TableCell, { align: "right", children: c.parts_completed }), costs.machineQuantities.keysToAscLazySeq().map((m) => (_jsx(TableCell, { align: "right", children: pctFormat.format(c.machine.get(m) ?? 0) }, m))), _jsx(TableCell, { align: "right", children: pctFormat.format(c.labor) }), _jsx(TableCell, { align: "right", children: pctFormat.format(c.automation) })] }, idx))) })] }) })] }));
|
|
162
176
|
}
|
|
163
177
|
function CostOutputCard() {
|
|
164
178
|
const costs = useAtomValue(computedCosts);
|
|
@@ -167,9 +181,15 @@ function CostOutputCard() {
|
|
|
167
181
|
.map((c, idx) => (_jsxs(TableRow, { children: [_jsx(TableCell, { children: _jsxs(Box, { sx: {
|
|
168
182
|
display: "flex",
|
|
169
183
|
alignItems: "center",
|
|
170
|
-
}, children: [_jsx(Box, { sx: { mr: "0.2em" }, children: _jsx(PartIdenticon, { part: c.part, size: 25 }) }), _jsx("div", { children: _jsx(Typography, { variant: "body2", component: "span",
|
|
184
|
+
}, children: [_jsx(Box, { sx: { mr: "0.2em" }, children: _jsx(PartIdenticon, { part: c.part, size: 25 }) }), _jsx("div", { children: _jsx(Typography, { variant: "body2", component: "span", sx: {
|
|
185
|
+
display: "block",
|
|
186
|
+
}, children: c.part }) })] }) }), _jsx(TableCell, { align: "right", children: c.parts_completed }), costs.machineQuantities.keysToAscLazySeq().map((m) => (_jsx(TableCell, { align: "right", children: decimalFormat.format(c.machine.get(m) ?? 0) }, m))), _jsx(TableCell, { align: "right", children: decimalFormat.format(c.labor) }), _jsx(TableCell, { align: "right", children: decimalFormat.format(c.automation) }), _jsx(TableCell, { align: "right", children: decimalFormat.format(c.machine.valuesToAscLazySeq().sumBy((x) => x) + c.labor + c.automation) })] }, idx))) })] }));
|
|
171
187
|
}
|
|
172
188
|
export const CostPerPiecePage = memo(function CostPerPiecePage() {
|
|
173
189
|
useSetTitle("Cost Per Piece");
|
|
174
|
-
return (_jsx(Box, { component: "main",
|
|
190
|
+
return (_jsx(Box, { component: "main", sx: {
|
|
191
|
+
paddingLeft: "24px",
|
|
192
|
+
paddingRight: "24px",
|
|
193
|
+
paddingTop: "20px",
|
|
194
|
+
}, children: _jsxs(Stack, { direction: "column", spacing: 4, children: [_jsxs(Stack, { direction: "column", spacing: 2, children: [_jsx(LaborCost, {}), _jsx(AutomationCostInput, {}), _jsx(StationCostInputs, {})] }), _jsx(CostOutputCard, {})] }) }));
|
|
175
195
|
});
|
|
@@ -150,7 +150,7 @@ const SingleSeries = memo(function SingleSeries({ seriesName, points, color, xSc
|
|
|
150
150
|
const p = localPoint(e);
|
|
151
151
|
if (p === null)
|
|
152
152
|
return;
|
|
153
|
-
const idxS = e.
|
|
153
|
+
const idxS = e.currentTarget.dataset.idx;
|
|
154
154
|
if (idxS === undefined)
|
|
155
155
|
return;
|
|
156
156
|
showTooltip({
|
|
@@ -94,9 +94,11 @@ function SelectDateRange(props) {
|
|
|
94
94
|
});
|
|
95
95
|
setOpen(false);
|
|
96
96
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
97
|
+
const matchingOnChange = (value) => {
|
|
98
|
+
if (Array.isArray(value) && value[0] instanceof Date && value[1] instanceof Date) {
|
|
99
|
+
onChange([value[0], value[1]]);
|
|
100
|
+
}
|
|
101
|
+
};
|
|
100
102
|
return (_jsxs(_Fragment, { children: [_jsxs("span", { children: [dateFormat.format(props.zoom.current_date_zoom?.start ?? props.zoom.default_date_range[0]), " -", " ", dateFormat.format(props.zoom.current_date_zoom?.end ?? props.zoom.default_date_range[1])] }), _jsx(Tooltip, { title: "Zoom To Date Range", children: _jsx(IconButton, { onClick: () => setOpen(true), size: "large", children: _jsx(ZoomInIcon, {}) }) }), _jsx(Tooltip, { title: "Reset Date Range", children: _jsx(IconButton, { onClick: () => props.zoom.set_date_zoom_range(undefined), size: "large", children: _jsx(ZoomOutIcon, {}) }) }), _jsxs(Dialog, { open: open, onClose: () => setOpen(false), children: [_jsxs(DialogTitle, { children: ["Select Date Range ", monthFormat.format(props.zoom.default_date_range[0])] }), _jsx(DialogContent, { children: _jsx(StyledCalendar, { minDate: props.zoom.default_date_range[0], maxDate: props.zoom.default_date_range[1], calendarType: "gregory", selectRange: true, showNavigation: false, showNeighboringMonth: false, value: [start, end], onChange: matchingOnChange }) }), _jsx(DialogActions, { children: _jsx(Button, { onClick: () => setOpen(false), children: "Close" }) })] })] }));
|
|
101
103
|
}
|
|
102
104
|
export const DataTableActions = memo(function DataTableActions({ zoom, tpage, count, }) {
|
|
@@ -211,5 +213,5 @@ export function buildClipboardTableAsString(columns, rows) {
|
|
|
211
213
|
return table;
|
|
212
214
|
}
|
|
213
215
|
export function copyTableToClipboard(columns, rows) {
|
|
214
|
-
copy(buildClipboardTableAsString(columns, rows));
|
|
216
|
+
void copy(buildClipboardTableAsString(columns, rows));
|
|
215
217
|
}
|
|
@@ -42,6 +42,9 @@ import { AxisBottom, AxisLeft } from "../AxisAndGrid.js";
|
|
|
42
42
|
import { measureSvgString } from "../../util/chart-helpers.js";
|
|
43
43
|
import { atom, useSetAtom } from "jotai";
|
|
44
44
|
import { ChartWithTooltip } from "../ChartTooltip.js";
|
|
45
|
+
function isSelectableOption(options, value) {
|
|
46
|
+
return options.some((option) => option === value);
|
|
47
|
+
}
|
|
45
48
|
const marginBottom = 20;
|
|
46
49
|
const marginTop = 10;
|
|
47
50
|
const marginRight = 2;
|
|
@@ -73,29 +76,29 @@ function useScales({ yType, dateRange, containerWidth, points, }) {
|
|
|
73
76
|
}
|
|
74
77
|
return scaleBand().domain(xValues).range([0, xMax]).align(0).padding(0.05);
|
|
75
78
|
}, [dateRangeStart, dateRangeEnd, xMax]);
|
|
76
|
-
const { yScale, height, colorScale } = useMemo(() => {
|
|
79
|
+
const { yScale: bandScale, height: chartHeight, colorScale: fillScale, } = useMemo(() => {
|
|
77
80
|
const yValues = new Set();
|
|
78
|
-
for (const
|
|
79
|
-
yValues.add(
|
|
81
|
+
for (const point of points) {
|
|
82
|
+
yValues.add(point.y);
|
|
80
83
|
}
|
|
81
84
|
const yMax = 60 * yValues.size;
|
|
82
|
-
const
|
|
83
|
-
const
|
|
84
|
-
.domain(
|
|
85
|
+
const nextChartHeight = yMax + marginTop + marginBottom;
|
|
86
|
+
const nextBandScale = scaleBand()
|
|
87
|
+
.domain(LazySeq.of(yValues).toSortedArray((x) => x))
|
|
85
88
|
.range([0, yMax])
|
|
86
89
|
.align(0)
|
|
87
90
|
.padding(0.05);
|
|
88
|
-
let
|
|
91
|
+
let nextFillScale;
|
|
89
92
|
if (yType === "Station") {
|
|
90
|
-
|
|
93
|
+
nextFillScale = scaleLinear().domain([0, 1]).range([color1, color2]);
|
|
91
94
|
}
|
|
92
95
|
else {
|
|
93
96
|
const maxCnt = LazySeq.of(points).maxBy((pt) => pt.color)?.color ?? 1;
|
|
94
|
-
|
|
97
|
+
nextFillScale = scaleLinear().domain([0, maxCnt]).range([color1, color2]);
|
|
95
98
|
}
|
|
96
|
-
return { yScale, height, colorScale };
|
|
99
|
+
return { yScale: nextBandScale, height: nextChartHeight, colorScale: nextFillScale };
|
|
97
100
|
}, [points, yType]);
|
|
98
|
-
return { height, width, xScale, yScale, colorScale, marginLeft };
|
|
101
|
+
return { height: chartHeight, width, xScale, yScale: bandScale, colorScale: fillScale, marginLeft };
|
|
99
102
|
}
|
|
100
103
|
const HeatAxis = memo(function HeatAxis({ xScale, yScale }) {
|
|
101
104
|
return (_jsxs(_Fragment, { children: [_jsx(AxisBottom, { scale: xScale, top: yScale.range()[1], tickFormat: (d) => d.toLocaleDateString(undefined, { month: "short", day: "numeric" }) }), _jsx(AxisLeft, { scale: yScale, left: xScale.range()[0] })] }));
|
|
@@ -110,7 +113,7 @@ const HeatSeries = memo(function HeatSeries({ points, xScale, yScale, colorScale
|
|
|
110
113
|
clearTimeout(hideRef.current);
|
|
111
114
|
hideRef.current = null;
|
|
112
115
|
}
|
|
113
|
-
const idxS = e.
|
|
116
|
+
const idxS = e.currentTarget.dataset.idx;
|
|
114
117
|
if (idxS === undefined)
|
|
115
118
|
return;
|
|
116
119
|
const heatPoint = points[parseInt(idxS)];
|
|
@@ -149,7 +152,13 @@ function ChartToolbar(props) {
|
|
|
149
152
|
minHeight: "2.5em",
|
|
150
153
|
alignItems: "center",
|
|
151
154
|
maxWidth: "calc(100vw - 24px - 24px)",
|
|
152
|
-
}, children: [_jsx(Typography, { variant: "subtitle1", children: props.label }), _jsx(Box, {
|
|
155
|
+
}, children: [_jsx(Typography, { variant: "subtitle1", children: props.label }), _jsx(Box, { sx: {
|
|
156
|
+
flexGrow: 1,
|
|
157
|
+
} }), setSelected ? (_jsx(FormControl, { size: "small", children: _jsx(Select, { autoWidth: true, displayEmpty: true, value: props.cur_selected, onChange: (e) => {
|
|
158
|
+
if (isSelectableOption(props.options, e.target.value)) {
|
|
159
|
+
setSelected(e.target.value);
|
|
160
|
+
}
|
|
161
|
+
}, children: props.options.map((v, idx) => (_jsx(MenuItem, { value: v, children: v }, idx))) }) })) : undefined, _jsx(Tooltip, { title: "Copy to Clipboard", children: _jsx(IconButton, { onClick: props.onExport, style: { height: "25px", paddingTop: 0, paddingBottom: 0 }, size: "large", children: _jsx(ImportExport, {}) }) })] }));
|
|
153
162
|
}
|
|
154
163
|
export function SelectableHeatChart(props) {
|
|
155
164
|
const tooltipAtom = useMemo(() => atom(null), []);
|
|
@@ -157,5 +166,9 @@ export function SelectableHeatChart(props) {
|
|
|
157
166
|
const pointerLeave = useCallback(() => {
|
|
158
167
|
setTooltip(null);
|
|
159
168
|
}, [setTooltip]);
|
|
160
|
-
return (_jsxs(Box, {
|
|
169
|
+
return (_jsxs(Box, { sx: {
|
|
170
|
+
paddingLeft: "24px",
|
|
171
|
+
paddingRight: "24px",
|
|
172
|
+
paddingTop: "10px",
|
|
173
|
+
}, children: [_jsx(ChartToolbar, { label: props.label, setSelected: props.setSelected, cur_selected: props.cur_selected, onExport: props.onExport, options: props.options }), _jsx("main", { onPointerLeave: pointerLeave, children: _jsx(ChartWithTooltip, { sx: { width: "100%" }, tooltipAtom: tooltipAtom, autoHeight: true, chart: ({ width }) => (_jsx(HeatChart, { points: props.points, y_title: props.y_title, dateRange: props.dateRange, parentWidth: width, setTooltip: setTooltip })), TooltipContent: ({ tooltip }) => (_jsx(HeatTooltip, { yType: props.y_title, seriesLabel: props.label_title, tooltip: tooltip })) }) })] }));
|
|
161
174
|
}
|
|
@@ -74,8 +74,8 @@ function NodeDisplay({ node }) {
|
|
|
74
74
|
}
|
|
75
75
|
const SankeyDisplay = memo(function InspectionSankeyDiagram({ data, setTooltip, parentHeight, parentWidth, }) {
|
|
76
76
|
const { nodes, links } = useMemo(() => {
|
|
77
|
-
const
|
|
78
|
-
if (nodes.length === 0) {
|
|
77
|
+
const sankeyData = inspectionDataToSankey(data);
|
|
78
|
+
if (sankeyData.nodes.length === 0) {
|
|
79
79
|
return { nodes: [], links: [] };
|
|
80
80
|
}
|
|
81
81
|
const generator = sankey()
|
|
@@ -86,8 +86,13 @@ const SankeyDisplay = memo(function InspectionSankeyDiagram({ data, setTooltip,
|
|
|
86
86
|
[marginLeft, marginTop],
|
|
87
87
|
[parentWidth - marginRight, parentHeight - marginBottom - marginTop],
|
|
88
88
|
]);
|
|
89
|
-
generator({ nodes, links });
|
|
90
|
-
return {
|
|
89
|
+
const layout = generator({ nodes: sankeyData.nodes, links: sankeyData.links });
|
|
90
|
+
return {
|
|
91
|
+
nodes: layout.nodes,
|
|
92
|
+
links: layout.links.flatMap((link) => typeof link.source === "object" && typeof link.target === "object" && link.width !== undefined
|
|
93
|
+
? [{ ...link, source: link.source, target: link.target, width: link.width }]
|
|
94
|
+
: []),
|
|
95
|
+
};
|
|
91
96
|
}, [data, parentWidth, parentHeight]);
|
|
92
97
|
const path = sankeyLinkHorizontal();
|
|
93
98
|
return (_jsxs("svg", { width: parentWidth, height: parentHeight, children: [_jsx("g", { children: nodes.map((node, i) => (_jsx(NodeDisplay, { node: node }, i))) }), _jsx("g", { children: links.map((link, i) => (_jsx(LinkDisplay, { link: link, path: path(link), strokeWidth: Math.max(link.width ?? 1, 1), setTooltip: setTooltip }, i))) })] }));
|
|
@@ -129,12 +134,18 @@ export function InspectionSankey(props) {
|
|
|
129
134
|
.map((e) => e.inspType)
|
|
130
135
|
.distinct()
|
|
131
136
|
.toSortedArray((x) => x);
|
|
132
|
-
return (_jsxs(Box, {
|
|
137
|
+
return (_jsxs(Box, { sx: {
|
|
138
|
+
paddingLeft: "24px",
|
|
139
|
+
paddingRight: "24px",
|
|
140
|
+
paddingTop: "10px",
|
|
141
|
+
}, children: [_jsxs(Box, { component: "nav", sx: {
|
|
133
142
|
display: "flex",
|
|
134
143
|
minHeight: "2.5em",
|
|
135
144
|
alignItems: "center",
|
|
136
145
|
maxWidth: "calc(100vw - 24px - 24px)",
|
|
137
|
-
}, children: [props.subtitle ? _jsx(Typography, { variant: "subtitle1", children: props.subtitle }) : undefined, _jsx(Box, {
|
|
146
|
+
}, children: [props.subtitle ? _jsx(Typography, { variant: "subtitle1", children: props.subtitle }) : undefined, _jsx(Box, { sx: {
|
|
147
|
+
flexGrow: 1,
|
|
148
|
+
} }), props.onlyTable ? undefined : (_jsx(FormControl, { size: "small", children: _jsxs(Select, { autoWidth: true, value: showTable ? "table" : "sankey", onChange: (e) => setShowTable(e.target.value === "table"), children: [_jsx(MenuItem, { value: "sankey", children: "Sankey" }, "sankey"), _jsx(MenuItem, { value: "table", children: "Table" }, "table")] }) })), _jsx(FormControl, { size: "small", children: _jsxs(Select, { name: "inspection-sankey-select-type", autoWidth: true, displayEmpty: true, style: { marginRight: "1em", marginLeft: "1em" }, value: selectedInspectType || "", onChange: (e) => setSelectedInspectType(e.target.value), children: [selectedInspectType ? undefined : (_jsx(MenuItem, { value: "", children: _jsx("em", { children: "Select Inspection Type" }) }, 0)), inspTypes.map((n) => (_jsx(MenuItem, { value: n, children: n }, n)))] }) }), props.restrictToPart === undefined ? (_jsx(FormControl, { size: "small", children: _jsxs(Select, { name: "inspection-sankey-select-part", autoWidth: true, displayEmpty: true, value: selectedPart || "", onChange: (e) => setSelectedPart(e.target.value), children: [selectedPart ? undefined : (_jsx(MenuItem, { value: "", children: _jsx("em", { children: "Select Part" }) }, 0)), parts.map((n) => (_jsx(MenuItem, { value: n, children: _jsxs("div", { style: { display: "flex", alignItems: "center" }, children: [_jsx(PartIdenticon, { part: n, size: 30 }), _jsx("span", { style: { marginRight: "1em" }, children: n })] }) }, n)))] }) })) : undefined, curData ? (_jsx(Tooltip, { title: "Copy to Clipboard", children: _jsx(IconButton, { onClick: () => curData
|
|
138
149
|
? copyInspectionEntriesToClipboard(selectedPart || "", selectedInspectType || "", curData)
|
|
139
150
|
: undefined, style: { height: "25px", paddingTop: 0, paddingBottom: 0 }, size: "large", children: _jsx(ImportExport, {}) }) })) : undefined] }), _jsx("main", { children: curData ? (showTable || props.onlyTable ? (_jsx(InspectionDataTable, { zoomType: props.zoomType, points: curData, default_date_range: props.default_date_range, extendDateRange: props.extendDateRange, hideOpenDetailColumn: props.hideOpenDetailColumn })) : (_jsx(InspectionDiagram, { data: curData }))) : undefined })] }));
|
|
140
151
|
}
|
|
@@ -53,6 +53,9 @@ const selectedPalletAtom = atomWithDefault((get) => (get(isDemoAtom) ? 3 : undef
|
|
|
53
53
|
const selectedPartAtom = atom(undefined);
|
|
54
54
|
const zoomDateRangeAtom = atom(undefined);
|
|
55
55
|
const yZoomAtom = atom(null);
|
|
56
|
+
function isPalletCycleData(point) {
|
|
57
|
+
return "mats" in point && Array.isArray(point.mats);
|
|
58
|
+
}
|
|
56
59
|
const filteredPoints = atom((get) => {
|
|
57
60
|
const selPal = get(selectedPalletAtom);
|
|
58
61
|
const selPart = get(selectedPartAtom);
|
|
@@ -114,19 +117,27 @@ export function PalletCycleChart() {
|
|
|
114
117
|
const partNames = useAtomValue(allPartNames);
|
|
115
118
|
const setMatToShow = useSetAtom(materialDialogOpen);
|
|
116
119
|
const extraPalletTooltip = useCallback(function extraPalletTooltip(point) {
|
|
117
|
-
|
|
118
|
-
|
|
120
|
+
if (!isPalletCycleData(point)) {
|
|
121
|
+
return [];
|
|
122
|
+
}
|
|
123
|
+
return point.mats.map((mat) => ({
|
|
119
124
|
title: mat.part + "-" + mat.proc,
|
|
120
125
|
value: mat.serial ?? "",
|
|
121
126
|
link: mat.serial ? () => setMatToShow({ type: "LogMat", logMat: mat }) : undefined,
|
|
122
127
|
}));
|
|
123
128
|
}, [setMatToShow]);
|
|
124
|
-
return (_jsxs(Box, {
|
|
129
|
+
return (_jsxs(Box, { sx: {
|
|
130
|
+
paddingLeft: "24px",
|
|
131
|
+
paddingRight: "24px",
|
|
132
|
+
paddingTop: "10px",
|
|
133
|
+
}, children: [_jsxs(Box, { component: "nav", sx: {
|
|
125
134
|
display: "flex",
|
|
126
135
|
minHeight: "2.5em",
|
|
127
136
|
alignItems: "center",
|
|
128
137
|
maxWidth: "calc(100vw - 24px - 24px)",
|
|
129
|
-
}, children: [_jsx(Typography, { variant: "subtitle1", children: "Pallet Cycles" }), _jsx(Box, {
|
|
138
|
+
}, children: [_jsx(Typography, { variant: "subtitle1", children: "Pallet Cycles" }), _jsx(Box, { sx: {
|
|
139
|
+
flexGrow: 1,
|
|
140
|
+
} }), _jsx(FormControl, { size: "small", children: _jsxs(Select, { autoWidth: true, displayEmpty: true, value: selectedPallet ?? -1, onChange: (e) => setSelectedPallet(e.target.value === -1 ? undefined : (e.target.value)), children: [_jsx(MenuItem, { value: -1, children: _jsx("em", { children: "Any Pallet" }) }, 0), palletCycles
|
|
130
141
|
.keysToLazySeq()
|
|
131
142
|
.sortBy((x) => x)
|
|
132
143
|
.map((n) => (_jsx(MenuItem, { value: n, children: _jsx("div", { style: { display: "flex", alignItems: "center" }, children: _jsx("span", { style: { marginRight: "1em" }, children: n }) }) }, n)))] }) }), partNames.length > 0 ? (_jsx(FormControl, { size: "small", children: _jsxs(Select, { autoWidth: true, displayEmpty: true, value: selectedPart
|