@seedtactics/insight-client 16.4.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/README.md +37 -0
- package/dist/cell-status/buffers.d.ts +36 -0
- package/dist/cell-status/buffers.js +127 -0
- package/dist/cell-status/current-status.d.ts +30 -0
- package/dist/cell-status/current-status.js +200 -0
- package/dist/cell-status/estimated-cycle-times.d.ts +41 -0
- package/dist/cell-status/estimated-cycle-times.js +257 -0
- package/dist/cell-status/inspections.d.ts +55 -0
- package/dist/cell-status/inspections.js +213 -0
- package/dist/cell-status/loading.d.ts +26 -0
- package/dist/cell-status/loading.js +112 -0
- package/dist/cell-status/material-details.d.ts +116 -0
- package/dist/cell-status/material-details.js +422 -0
- package/dist/cell-status/material-summary.d.ts +52 -0
- package/dist/cell-status/material-summary.js +312 -0
- package/dist/cell-status/names.d.ts +19 -0
- package/dist/cell-status/names.js +134 -0
- package/dist/cell-status/pallet-cycles.d.ts +24 -0
- package/dist/cell-status/pallet-cycles.js +78 -0
- package/dist/cell-status/rebookings.d.ts +30 -0
- package/dist/cell-status/rebookings.js +139 -0
- package/dist/cell-status/scheduled-jobs.d.ts +18 -0
- package/dist/cell-status/scheduled-jobs.js +94 -0
- package/dist/cell-status/sim-day-usage.d.ts +14 -0
- package/dist/cell-status/sim-day-usage.js +54 -0
- package/dist/cell-status/sim-production.d.ts +22 -0
- package/dist/cell-status/sim-production.js +91 -0
- package/dist/cell-status/sim-station-use.d.ts +25 -0
- package/dist/cell-status/sim-station-use.js +71 -0
- package/dist/cell-status/station-cycles.d.ts +34 -0
- package/dist/cell-status/station-cycles.js +145 -0
- package/dist/cell-status/tool-replacements.d.ts +44 -0
- package/dist/cell-status/tool-replacements.js +155 -0
- package/dist/cell-status/tool-usage.d.ts +25 -0
- package/dist/cell-status/tool-usage.js +95 -0
- package/dist/components/App.d.ts +15 -0
- package/dist/components/App.js +549 -0
- package/dist/components/AxisAndGrid.d.ts +51 -0
- package/dist/components/AxisAndGrid.js +47 -0
- package/dist/components/BarcodeScanning.d.ts +6 -0
- package/dist/components/BarcodeScanning.js +150 -0
- package/dist/components/ChartTooltip.d.ts +28 -0
- package/dist/components/ChartTooltip.js +95 -0
- package/dist/components/ChooseMode.d.ts +18 -0
- package/dist/components/ChooseMode.js +136 -0
- package/dist/components/ChooseOperator.d.ts +1 -0
- package/dist/components/ChooseOperator.js +93 -0
- package/dist/components/ErrorsAndLoading.d.ts +9 -0
- package/dist/components/ErrorsAndLoading.js +55 -0
- package/dist/components/LoadingIcon.d.ts +1 -0
- package/dist/components/LoadingIcon.js +48 -0
- package/dist/components/LogEntry.d.ts +16 -0
- package/dist/components/LogEntry.js +365 -0
- package/dist/components/ManualSerialEntry.d.ts +5 -0
- package/dist/components/ManualSerialEntry.js +91 -0
- package/dist/components/MonthSelect.d.ts +6 -0
- package/dist/components/MonthSelect.js +67 -0
- package/dist/components/Navigation.d.ts +23 -0
- package/dist/components/Navigation.js +120 -0
- package/dist/components/VerboseLogging.d.ts +1 -0
- package/dist/components/VerboseLogging.js +47 -0
- package/dist/components/analysis/AnalysisSelectToolbar.d.ts +1 -0
- package/dist/components/analysis/AnalysisSelectToolbar.js +55 -0
- package/dist/components/analysis/BufferChart.d.ts +1 -0
- package/dist/components/analysis/BufferChart.js +139 -0
- package/dist/components/analysis/CostPerPiece.d.ts +2 -0
- package/dist/components/analysis/CostPerPiece.js +175 -0
- package/dist/components/analysis/CycleChart.d.ts +42 -0
- package/dist/components/analysis/CycleChart.js +281 -0
- package/dist/components/analysis/DataTable.d.ts +83 -0
- package/dist/components/analysis/DataTable.js +215 -0
- package/dist/components/analysis/EfficiencyPage.d.ts +2 -0
- package/dist/components/analysis/EfficiencyPage.js +138 -0
- package/dist/components/analysis/HeatChart.d.ts +22 -0
- package/dist/components/analysis/HeatChart.js +161 -0
- package/dist/components/analysis/InspectionDataTable.d.ts +10 -0
- package/dist/components/analysis/InspectionDataTable.js +148 -0
- package/dist/components/analysis/InspectionSankey.d.ts +12 -0
- package/dist/components/analysis/InspectionSankey.js +140 -0
- package/dist/components/analysis/PalletCycleCards.d.ts +1 -0
- package/dist/components/analysis/PalletCycleCards.js +137 -0
- package/dist/components/analysis/PartCycleCards.d.ts +2 -0
- package/dist/components/analysis/PartCycleCards.js +331 -0
- package/dist/components/analysis/QualityPage.d.ts +1 -0
- package/dist/components/analysis/QualityPage.js +49 -0
- package/dist/components/analysis/ScheduleHistory.d.ts +3 -0
- package/dist/components/analysis/ScheduleHistory.js +108 -0
- package/dist/components/analysis/StationDataTable.d.ts +25 -0
- package/dist/components/analysis/StationDataTable.js +246 -0
- package/dist/components/analysis/ToolReplacements.d.ts +1 -0
- package/dist/components/analysis/ToolReplacements.js +370 -0
- package/dist/components/operations/AllMaterial.d.ts +5 -0
- package/dist/components/operations/AllMaterial.js +267 -0
- package/dist/components/operations/ChartRangeEdit.d.ts +4 -0
- package/dist/components/operations/ChartRangeEdit.js +148 -0
- package/dist/components/operations/CloseoutReport.d.ts +2 -0
- package/dist/components/operations/CloseoutReport.js +172 -0
- package/dist/components/operations/CompletedParts.d.ts +2 -0
- package/dist/components/operations/CompletedParts.js +286 -0
- package/dist/components/operations/CurrentWorkorders.d.ts +3 -0
- package/dist/components/operations/CurrentWorkorders.js +368 -0
- package/dist/components/operations/Dashboard.d.ts +2 -0
- package/dist/components/operations/Dashboard.js +90 -0
- package/dist/components/operations/OEEChart.d.ts +10 -0
- package/dist/components/operations/OEEChart.js +173 -0
- package/dist/components/operations/Outliers.d.ts +4 -0
- package/dist/components/operations/Outliers.js +69 -0
- package/dist/components/operations/ProgramHighlight.d.ts +1 -0
- package/dist/components/operations/ProgramHighlight.js +9 -0
- package/dist/components/operations/Programs.d.ts +5 -0
- package/dist/components/operations/Programs.js +363 -0
- package/dist/components/operations/Rebookings.d.ts +1 -0
- package/dist/components/operations/Rebookings.js +213 -0
- package/dist/components/operations/RecentCycleChart.d.ts +4 -0
- package/dist/components/operations/RecentCycleChart.js +240 -0
- package/dist/components/operations/RecentProduction.d.ts +2 -0
- package/dist/components/operations/RecentProduction.js +213 -0
- package/dist/components/operations/RecentSchedules.d.ts +12 -0
- package/dist/components/operations/RecentSchedules.js +180 -0
- package/dist/components/operations/RecentStationCycles.d.ts +4 -0
- package/dist/components/operations/RecentStationCycles.js +159 -0
- package/dist/components/operations/ShiftSettings.d.ts +6 -0
- package/dist/components/operations/ShiftSettings.js +134 -0
- package/dist/components/operations/SimDayUsage.d.ts +1 -0
- package/dist/components/operations/SimDayUsage.js +133 -0
- package/dist/components/operations/ToolReport.d.ts +3 -0
- package/dist/components/operations/ToolReport.js +233 -0
- package/dist/components/operations/WorkorderGantt.d.ts +1 -0
- package/dist/components/operations/WorkorderGantt.js +124 -0
- package/dist/components/quality/QualityMaterial.d.ts +2 -0
- package/dist/components/quality/QualityMaterial.js +169 -0
- package/dist/components/quality/QualityPaths.d.ts +1 -0
- package/dist/components/quality/QualityPaths.js +53 -0
- package/dist/components/quality/RecentFailedInspections.d.ts +1 -0
- package/dist/components/quality/RecentFailedInspections.js +123 -0
- package/dist/components/routes.d.ts +170 -0
- package/dist/components/routes.js +301 -0
- package/dist/components/station-monitor/BulkRawMaterial.d.ts +11 -0
- package/dist/components/station-monitor/BulkRawMaterial.js +251 -0
- package/dist/components/station-monitor/Closeout.d.ts +5 -0
- package/dist/components/station-monitor/Closeout.js +162 -0
- package/dist/components/station-monitor/CustomStationMonitorDialog.d.ts +1 -0
- package/dist/components/station-monitor/CustomStationMonitorDialog.js +55 -0
- package/dist/components/station-monitor/Inspection.d.ts +8 -0
- package/dist/components/station-monitor/Inspection.js +164 -0
- package/dist/components/station-monitor/InvalidateCycle.d.ts +33 -0
- package/dist/components/station-monitor/InvalidateCycle.js +262 -0
- package/dist/components/station-monitor/JobDetails.d.ts +7 -0
- package/dist/components/station-monitor/JobDetails.js +108 -0
- package/dist/components/station-monitor/LoadStation.d.ts +10 -0
- package/dist/components/station-monitor/LoadStation.js +450 -0
- package/dist/components/station-monitor/Material.d.ts +77 -0
- package/dist/components/station-monitor/Material.js +489 -0
- package/dist/components/station-monitor/MoveMaterialArrows.d.ts +11 -0
- package/dist/components/station-monitor/MoveMaterialArrows.js +118 -0
- package/dist/components/station-monitor/PrintedLabel.d.ts +29 -0
- package/dist/components/station-monitor/PrintedLabel.js +166 -0
- package/dist/components/station-monitor/QuarantineButton.d.ts +4 -0
- package/dist/components/station-monitor/QuarantineButton.js +184 -0
- package/dist/components/station-monitor/Queues.d.ts +23 -0
- package/dist/components/station-monitor/Queues.js +312 -0
- package/dist/components/station-monitor/QueuesAddMaterial.d.ts +30 -0
- package/dist/components/station-monitor/QueuesAddMaterial.js +248 -0
- package/dist/components/station-monitor/SelectInspType.d.ts +2 -0
- package/dist/components/station-monitor/SelectInspType.js +99 -0
- package/dist/components/station-monitor/SelectWorkorder.d.ts +4 -0
- package/dist/components/station-monitor/SelectWorkorder.js +100 -0
- package/dist/components/station-monitor/StationToolbar.d.ts +3 -0
- package/dist/components/station-monitor/StationToolbar.js +168 -0
- package/dist/components/station-monitor/SystemOverview.d.ts +46 -0
- package/dist/components/station-monitor/SystemOverview.js +439 -0
- package/dist/components/station-monitor/Whiteboard.d.ts +10 -0
- package/dist/components/station-monitor/Whiteboard.js +67 -0
- package/dist/data/all-material-bins.d.ts +45 -0
- package/dist/data/all-material-bins.js +224 -0
- package/dist/data/chart-times.d.ts +20 -0
- package/dist/data/chart-times.js +99 -0
- package/dist/data/cost-per-piece.d.ts +32 -0
- package/dist/data/cost-per-piece.js +183 -0
- package/dist/data/current-cycles.d.ts +13 -0
- package/dist/data/current-cycles.js +144 -0
- package/dist/data/inspection-sankey.d.ts +15 -0
- package/dist/data/inspection-sankey.js +147 -0
- package/dist/data/move-arrows.d.ts +48 -0
- package/dist/data/move-arrows.js +217 -0
- package/dist/data/operators.d.ts +2 -0
- package/dist/data/operators.js +44 -0
- package/dist/data/part-summary.d.ts +17 -0
- package/dist/data/part-summary.js +107 -0
- package/dist/data/path-lookup.d.ts +13 -0
- package/dist/data/path-lookup.js +107 -0
- package/dist/data/queue-material.d.ts +46 -0
- package/dist/data/queue-material.js +256 -0
- package/dist/data/results.bufferchart.d.ts +10 -0
- package/dist/data/results.bufferchart.js +90 -0
- package/dist/data/results.completed-parts.d.ts +26 -0
- package/dist/data/results.completed-parts.js +181 -0
- package/dist/data/results.cycles.d.ts +86 -0
- package/dist/data/results.cycles.js +454 -0
- package/dist/data/results.inspection.d.ts +36 -0
- package/dist/data/results.inspection.js +188 -0
- package/dist/data/results.oee.d.ts +40 -0
- package/dist/data/results.oee.js +330 -0
- package/dist/data/results.schedules.d.ts +23 -0
- package/dist/data/results.schedules.js +157 -0
- package/dist/data/tools-programs.d.ts +78 -0
- package/dist/data/tools-programs.js +376 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +60 -0
- package/dist/network/api.d.ts +1390 -0
- package/dist/network/api.js +4971 -0
- package/dist/network/backend-mock.d.ts +11 -0
- package/dist/network/backend-mock.js +512 -0
- package/dist/network/backend.d.ts +57 -0
- package/dist/network/backend.js +77 -0
- package/dist/network/load-specific-month.d.ts +13 -0
- package/dist/network/load-specific-month.js +77 -0
- package/dist/network/server-settings.d.ts +12 -0
- package/dist/network/server-settings.js +92 -0
- package/dist/network/websocket.d.ts +4 -0
- package/dist/network/websocket.js +165 -0
- package/dist/renderer.d.ts +8 -0
- package/dist/renderer.js +55 -0
- package/dist/seedtactics-logo.d.ts +2 -0
- package/dist/seedtactics-logo.js +4 -0
- package/dist/util/chart-colors.d.ts +1 -0
- package/dist/util/chart-colors.js +116 -0
- package/dist/util/chart-helpers.d.ts +3 -0
- package/dist/util/chart-helpers.js +51 -0
- package/dist/util/parseISODuration.d.ts +4 -0
- package/dist/util/parseISODuration.js +40 -0
- package/docs/client-efficiency.md +355 -0
- package/docs/client-engineering.md +54 -0
- package/docs/client-launch.md +55 -0
- package/docs/client-operations.md +88 -0
- package/docs/client-quality.md +74 -0
- package/docs/client-sales.md +61 -0
- package/docs/client-scanners.md +41 -0
- package/docs/client-station-monitor.md +149 -0
- package/docs/client-tools-programs.md +74 -0
- package/docs/improve-fms.md +141 -0
- package/docs/makino.md +40 -0
- package/docs/material-quarantine.md +82 -0
- package/docs/material-tracking.md +236 -0
- package/docs/mazak.md +115 -0
- package/docs/niigata.md +228 -0
- package/docs/operator-procedures.md +106 -0
- package/docs/part-instructions.md +63 -0
- package/docs/screenshots/insight-all-material.png +0 -0
- package/docs/screenshots/insight-analysis-pallets.png +0 -0
- package/docs/screenshots/insight-analysis-part-completed.png +0 -0
- package/docs/screenshots/insight-analysis-sankey.png +0 -0
- package/docs/screenshots/insight-analysis-station-oee.png +0 -0
- package/docs/screenshots/insight-buffer-occupancy.png +0 -0
- package/docs/screenshots/insight-choose-analysis-month.png +0 -0
- package/docs/screenshots/insight-closeout.png +0 -0
- package/docs/screenshots/insight-cost-percentages.png +0 -0
- package/docs/screenshots/insight-dashboard.png +0 -0
- package/docs/screenshots/insight-event-custom-view.jpg +0 -0
- package/docs/screenshots/insight-event-viewer.jpg +0 -0
- package/docs/screenshots/insight-inspection.png +0 -0
- package/docs/screenshots/insight-load-station-details.png +0 -0
- package/docs/screenshots/insight-load-station.png +0 -0
- package/docs/screenshots/insight-loadcycle-graph.png +0 -0
- package/docs/screenshots/insight-loadstation-small.jpg +0 -0
- package/docs/screenshots/insight-machinecycle-graph.png +0 -0
- package/docs/screenshots/insight-machinecycle-table.png +0 -0
- package/docs/screenshots/insight-machinecycles.png +0 -0
- package/docs/screenshots/insight-machinehours.png +0 -0
- package/docs/screenshots/insight-machineoutliers.png +0 -0
- package/docs/screenshots/insight-monthly-schedules.png +0 -0
- package/docs/screenshots/insight-operations-material.png +0 -0
- package/docs/screenshots/insight-operations-overview.png +0 -0
- package/docs/screenshots/insight-operations-reports.png +0 -0
- package/docs/screenshots/insight-part-cost.png +0 -0
- package/docs/screenshots/insight-program-report.png +0 -0
- package/docs/screenshots/insight-quality-material-details.png +0 -0
- package/docs/screenshots/insight-quality-material.png +0 -0
- package/docs/screenshots/insight-quality-quarantine.png +0 -0
- package/docs/screenshots/insight-quality-sankey.png +0 -0
- package/docs/screenshots/insight-quality-similar-paths.png +0 -0
- package/docs/screenshots/insight-queue-details.png +0 -0
- package/docs/screenshots/insight-queues-jobs-table.png +0 -0
- package/docs/screenshots/insight-queues.png +0 -0
- package/docs/screenshots/insight-sim-day-usage.png +0 -0
- package/docs/screenshots/insight-station-system-overview-buttons.png +0 -0
- package/docs/screenshots/insight-station-system-overview.png +0 -0
- package/docs/screenshots/insight-system-overview.png +0 -0
- package/docs/screenshots/insight-tool-replacements.png +0 -0
- package/docs/screenshots/insight-tool-report.png +0 -0
- package/docs/screenshots/insight-toolbar-btns.png +0 -0
- package/docs/screenshots/insight-workorder-gantt.png +0 -0
- package/docs/screenshots/insight-workorders.png +0 -0
- package/docs/security.md +131 -0
- package/docs/server-config.md +56 -0
- package/docs/server-errors.md +44 -0
- package/package.json +90 -0
- package/src/index.ts +65 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/* Copyright (c) 2023, 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 { ButtonBase, Stack, TextField, Tooltip } from "@mui/material";
|
|
35
|
+
import { useMemo } from "react";
|
|
36
|
+
import { Clear as ClearIcon } from "@mui/icons-material";
|
|
37
|
+
import { addDays, addMinutes } from "date-fns";
|
|
38
|
+
import { atom, useAtom, useAtomValue } from "jotai";
|
|
39
|
+
import { atomWithStorage } from "jotai/utils";
|
|
40
|
+
const shiftStartStorage = atomWithStorage("shift-starts", [
|
|
41
|
+
[1, 6 * 60],
|
|
42
|
+
[2, 14 * 60],
|
|
43
|
+
[3, 22 * 60],
|
|
44
|
+
]);
|
|
45
|
+
const shiftStartAtom = atom((get) => new Map(get(shiftStartStorage)), (get, set, update) => {
|
|
46
|
+
const shifts = new Map(get(shiftStartStorage));
|
|
47
|
+
update(shifts);
|
|
48
|
+
set(shiftStartStorage, Array.from(shifts.entries()));
|
|
49
|
+
});
|
|
50
|
+
export function useShifts(day) {
|
|
51
|
+
const shifts = useAtomValue(shiftStartAtom);
|
|
52
|
+
return useMemo(() => {
|
|
53
|
+
const starts = [];
|
|
54
|
+
for (let i = 1; i <= 3; i++) {
|
|
55
|
+
const cur = shifts.get(i);
|
|
56
|
+
if (cur === undefined)
|
|
57
|
+
continue;
|
|
58
|
+
if (starts.length >= 1 && starts[starts.length - 1] >= cur)
|
|
59
|
+
continue;
|
|
60
|
+
starts.push(cur);
|
|
61
|
+
}
|
|
62
|
+
if (starts.length === 0) {
|
|
63
|
+
return [
|
|
64
|
+
{
|
|
65
|
+
start: day,
|
|
66
|
+
end: addDays(day, 1),
|
|
67
|
+
},
|
|
68
|
+
];
|
|
69
|
+
}
|
|
70
|
+
const finalShiftEnd = addMinutes(addDays(day, 1), starts[0]);
|
|
71
|
+
const startAndEnd = [];
|
|
72
|
+
for (let i = 0; i < starts.length; i++) {
|
|
73
|
+
startAndEnd.push({
|
|
74
|
+
start: addMinutes(day, starts[i]),
|
|
75
|
+
end: i === starts.length - 1 ? finalShiftEnd : addMinutes(day, starts[i + 1]),
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return startAndEnd;
|
|
79
|
+
}, [shifts, day]);
|
|
80
|
+
}
|
|
81
|
+
function timespanToMinutes(t) {
|
|
82
|
+
const [h, m] = t.split(":");
|
|
83
|
+
return parseInt(h, 10) * 60 + parseInt(m, 10);
|
|
84
|
+
}
|
|
85
|
+
function minutesToTimespan(m) {
|
|
86
|
+
const h = Math.floor(m / 60);
|
|
87
|
+
const min = m % 60;
|
|
88
|
+
return `${h.toString().padStart(2, "0")}:${min.toString().padStart(2, "0")}`;
|
|
89
|
+
}
|
|
90
|
+
function shiftError(shiftNum, starts) {
|
|
91
|
+
const cur = starts.get(shiftNum);
|
|
92
|
+
if (cur === undefined) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
if (shiftNum === 1) {
|
|
96
|
+
const after = starts.get(2) ?? starts.get(3);
|
|
97
|
+
return after !== undefined && after <= cur;
|
|
98
|
+
}
|
|
99
|
+
else if (shiftNum === 2) {
|
|
100
|
+
const before = starts.get(1);
|
|
101
|
+
const after = starts.get(3);
|
|
102
|
+
return (before !== undefined && before >= cur) || (after !== undefined && after <= cur);
|
|
103
|
+
}
|
|
104
|
+
else if (shiftNum === 3) {
|
|
105
|
+
const before = starts.get(2) ?? starts.get(1);
|
|
106
|
+
return before !== undefined && before >= cur;
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function ShiftStartInput({ shiftNum }) {
|
|
113
|
+
const [shifts, setShifts] = useAtom(shiftStartAtom);
|
|
114
|
+
const val = shifts.get(shiftNum);
|
|
115
|
+
return (_jsx(TextField, { type: "time", label: `Shift ${shiftNum} Start`, error: shiftError(shiftNum, shifts), value: val === undefined ? "" : minutesToTimespan(val), onChange: (e) => {
|
|
116
|
+
const val = e.target.value;
|
|
117
|
+
if (val === "") {
|
|
118
|
+
setShifts((draft) => draft.delete(shiftNum));
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
setShifts((draft) => draft.set(shiftNum, timespanToMinutes(val)));
|
|
122
|
+
}
|
|
123
|
+
}, variant: "standard", size: "small", slotProps: {
|
|
124
|
+
inputLabel: { shrink: true },
|
|
125
|
+
input: shiftNum === 1
|
|
126
|
+
? undefined
|
|
127
|
+
: {
|
|
128
|
+
endAdornment: (_jsx(Tooltip, { title: "Clear/Disable Shift", children: _jsx(ButtonBase, { sx: { mb: "4px" }, onClick: () => setShifts((draft) => draft.delete(shiftNum)), children: _jsx(ClearIcon, { fontSize: "small" }) }) })),
|
|
129
|
+
},
|
|
130
|
+
} }));
|
|
131
|
+
}
|
|
132
|
+
export function ShiftStart() {
|
|
133
|
+
return (_jsxs(Stack, { direction: "row", spacing: 2, children: [_jsx(ShiftStartInput, { shiftNum: 1 }), _jsx(ShiftStartInput, { shiftNum: 2 }), _jsx(ShiftStartInput, { shiftNum: 3 })] }));
|
|
134
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function SimDayUsagePage(): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
/* Copyright (c) 2023, 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 { useMemo } from "react";
|
|
35
|
+
import { Box, Stack, Tooltip, Typography } from "@mui/material";
|
|
36
|
+
import { green, grey } from "@mui/material/colors";
|
|
37
|
+
import { LazySeq, OrderedSet } from "@seedtactics/immutable-collections";
|
|
38
|
+
import { atom, useAtomValue } from "jotai";
|
|
39
|
+
import { latestSimDayUsage } from "../../cell-status/sim-day-usage";
|
|
40
|
+
import { useSetTitle } from "../routes";
|
|
41
|
+
import { scaleLinear } from "d3-scale";
|
|
42
|
+
import { Warning as WarningIcon } from "@mui/icons-material";
|
|
43
|
+
const color1 = green[50];
|
|
44
|
+
const color2 = green[600];
|
|
45
|
+
const downtimeColor = grey[100];
|
|
46
|
+
const monthBoxSize = "3em";
|
|
47
|
+
const monthWidth = `calc(${monthBoxSize} * 7)`;
|
|
48
|
+
const groupedSimDayUsage = atom((get) => {
|
|
49
|
+
const usage = get(latestSimDayUsage);
|
|
50
|
+
if (usage === null)
|
|
51
|
+
return null;
|
|
52
|
+
return LazySeq.of(usage.usage).toLookupOrderedMap((u) => u.machineGroup, (u) => new Date(u.day.getUTCFullYear(), u.day.getUTCMonth(), u.day.getUTCDate()));
|
|
53
|
+
});
|
|
54
|
+
const machineGroups = atom((get) => {
|
|
55
|
+
const usage = get(latestSimDayUsage);
|
|
56
|
+
if (usage === null)
|
|
57
|
+
return OrderedSet.empty();
|
|
58
|
+
return OrderedSet.build(usage.usage, (u) => u.machineGroup);
|
|
59
|
+
});
|
|
60
|
+
const usageMonths = atom((get) => {
|
|
61
|
+
const usage = get(latestSimDayUsage);
|
|
62
|
+
if (usage === null)
|
|
63
|
+
return OrderedSet.empty();
|
|
64
|
+
return OrderedSet.build(usage.usage, (u) => new Date(u.day.getUTCFullYear(), u.day.getUTCMonth(), 1));
|
|
65
|
+
});
|
|
66
|
+
const maxUsage = atom((get) => {
|
|
67
|
+
const usage = get(latestSimDayUsage);
|
|
68
|
+
if (usage === null)
|
|
69
|
+
return 0;
|
|
70
|
+
return (LazySeq.of(usage.usage)
|
|
71
|
+
.map((u) => Math.ceil(u.usage))
|
|
72
|
+
.maxBy((u) => u) ?? 1);
|
|
73
|
+
});
|
|
74
|
+
const minAndMaxUsageDay = atom((get) => {
|
|
75
|
+
const usage = get(latestSimDayUsage);
|
|
76
|
+
if (usage === null)
|
|
77
|
+
return [null, null];
|
|
78
|
+
return [
|
|
79
|
+
LazySeq.of(usage.usage)
|
|
80
|
+
.map((u) => new Date(u.day.getUTCFullYear(), u.day.getUTCMonth(), u.day.getUTCDate()))
|
|
81
|
+
.minBy((d) => d) ?? null,
|
|
82
|
+
LazySeq.of(usage.usage)
|
|
83
|
+
.map((u) => new Date(u.day.getUTCFullYear(), u.day.getUTCMonth(), u.day.getUTCDate()))
|
|
84
|
+
.maxBy((d) => d) ?? null,
|
|
85
|
+
];
|
|
86
|
+
});
|
|
87
|
+
function ShowMonth({ date, dayColor, tooltip, }) {
|
|
88
|
+
const dayStart = new Date(date.getFullYear(), date.getMonth(), 1).getDay();
|
|
89
|
+
const numDaysInMonth = new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
|
|
90
|
+
return (_jsxs(Box, { width: monthWidth, children: [_jsx(Typography, { variant: "h6", textAlign: "center", children: date.toLocaleString("default", { month: "long", year: "numeric" }) }), _jsxs(Box, { display: "flex", flexWrap: "wrap", children: [LazySeq.ofRange(0, dayStart).map((_, i) => (_jsx(Box, { height: monthBoxSize, width: monthBoxSize }, i))), LazySeq.ofRange(1, numDaysInMonth + 1).map((d, i) => (_jsx("div", { style: {
|
|
91
|
+
width: monthBoxSize,
|
|
92
|
+
height: monthBoxSize,
|
|
93
|
+
cursor: "default",
|
|
94
|
+
backgroundColor: dayColor(new Date(date.getFullYear(), date.getMonth(), d)),
|
|
95
|
+
}, children: _jsx(Tooltip, { title: tooltip(new Date(date.getFullYear(), date.getMonth(), d)), children: _jsx(Box, { display: "flex", justifyContent: "center", alignItems: "center", height: "100%", children: d }) }) }, i)))] })] }));
|
|
96
|
+
}
|
|
97
|
+
function Warning() {
|
|
98
|
+
return (_jsxs(Stack, { direction: "row", spacing: 2, alignItems: "center", children: [_jsx(WarningIcon, { fontSize: "small" }), _jsx(Typography, { variant: "caption", children: "Projected dates are estimates" })] }));
|
|
99
|
+
}
|
|
100
|
+
function MonthHeatmap({ group, month }) {
|
|
101
|
+
const usage = useAtomValue(groupedSimDayUsage);
|
|
102
|
+
const [minDay, maxDay] = useAtomValue(minAndMaxUsageDay);
|
|
103
|
+
const maxUse = useAtomValue(maxUsage);
|
|
104
|
+
const dayColor = useMemo(() => {
|
|
105
|
+
const scale = scaleLinear().domain([0, maxUse]).range([color1, color2]);
|
|
106
|
+
return (d) => {
|
|
107
|
+
if (maxDay && d > maxDay) {
|
|
108
|
+
return "white";
|
|
109
|
+
}
|
|
110
|
+
if (minDay && d < minDay) {
|
|
111
|
+
return "white";
|
|
112
|
+
}
|
|
113
|
+
const u = usage?.get(group)?.get(d);
|
|
114
|
+
if (u) {
|
|
115
|
+
return scale(u.usage);
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
return downtimeColor;
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
}, [usage, maxUse, minDay, maxDay, group]);
|
|
122
|
+
function tooltip(d) {
|
|
123
|
+
const u = usage?.get(group)?.get(d);
|
|
124
|
+
return (_jsxs(_Fragment, { children: [_jsx("div", { children: d.toLocaleDateString(undefined, { month: "short", day: "numeric", year: "numeric" }) }), maxDay && d > maxDay ? (_jsx("div", { children: "Not Simulated" })) : minDay && d < minDay ? (_jsx("div", {})) : u ? (_jsxs("div", { children: ["Usage: ", u.usage.toFixed(2)] })) : (_jsx("div", { children: "Downtime" }))] }));
|
|
125
|
+
}
|
|
126
|
+
return _jsx(ShowMonth, { date: month, dayColor: dayColor, tooltip: tooltip });
|
|
127
|
+
}
|
|
128
|
+
export function SimDayUsagePage() {
|
|
129
|
+
useSetTitle("Projected Machine Usage");
|
|
130
|
+
const groups = useAtomValue(machineGroups);
|
|
131
|
+
const months = useAtomValue(usageMonths);
|
|
132
|
+
return (_jsx(Box, { component: "main", padding: "24px", children: _jsxs(Stack, { direction: "column", spacing: 5, children: [_jsx(Warning, {}), groups.toAscLazySeq().map((g) => (_jsxs("div", { children: [_jsx(Typography, { variant: "h4", children: g }), _jsx(Box, { display: "flex", flexWrap: "wrap", columnGap: "50px", children: months.toAscLazySeq().map((m) => (_jsx(MonthHeatmap, { group: g, month: m }, m.getTime()))) })] }, g)))] }) }));
|
|
133
|
+
}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
/* Copyright (c) 2022, 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 { useState, Fragment } from "react";
|
|
35
|
+
import { Fab, styled, Box, FormControl } from "@mui/material";
|
|
36
|
+
import { CircularProgress } from "@mui/material";
|
|
37
|
+
import TimeAgo from "react-timeago";
|
|
38
|
+
import { Table } from "@mui/material";
|
|
39
|
+
import { TableHead } from "@mui/material";
|
|
40
|
+
import { TableCell } from "@mui/material";
|
|
41
|
+
import { TableRow } from "@mui/material";
|
|
42
|
+
import { Tooltip } from "@mui/material";
|
|
43
|
+
import { TableBody } from "@mui/material";
|
|
44
|
+
import { IconButton } from "@mui/material";
|
|
45
|
+
import { Select } from "@mui/material";
|
|
46
|
+
import { MenuItem } from "@mui/material";
|
|
47
|
+
import { Collapse } from "@mui/material";
|
|
48
|
+
import { Refresh as RefreshIcon, ImportExport, KeyboardArrowDown as KeyboardArrowDownIcon, KeyboardArrowUp as KeyboardArrowUpIcon, } from "@mui/icons-material";
|
|
49
|
+
import { currentToolReport, toolReportRefreshTime, machinesWithTools, toolReportHasSerial, useCopyToolReportToClipboard, toolReportEstimatedToolCounts, } from "../../data/tools-programs.js";
|
|
50
|
+
import { LazySeq } from "@seedtactics/immutable-collections";
|
|
51
|
+
import { PartIdenticon } from "../station-monitor/Material.js";
|
|
52
|
+
import { useIsDemo, useSetTitle } from "../routes.js";
|
|
53
|
+
import { DisplayLoadingAndError } from "../ErrorsAndLoading.js";
|
|
54
|
+
import { atom, useAtom, useAtomValue } from "jotai";
|
|
55
|
+
const cntFormat = new Intl.NumberFormat("en-US", {
|
|
56
|
+
minimumFractionDigits: 0,
|
|
57
|
+
maximumFractionDigits: 1,
|
|
58
|
+
});
|
|
59
|
+
const ToolTableRow = styled(TableRow, { shouldForwardProp: (prop) => prop.toString()[0] !== "$" })(({ theme, $noBorderBottom, $highlightedRow, $noticeRow }) => ({
|
|
60
|
+
...($noBorderBottom && {
|
|
61
|
+
"& > *": {
|
|
62
|
+
borderBottom: "unset",
|
|
63
|
+
},
|
|
64
|
+
}),
|
|
65
|
+
[theme.breakpoints.up("lg")]: {
|
|
66
|
+
"& td:not(:last-child), & th:not(:last-child)": {
|
|
67
|
+
whiteSpace: "nowrap",
|
|
68
|
+
},
|
|
69
|
+
"& td:last-child, & th:last-child": {
|
|
70
|
+
width: "100%",
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
backgroundColor: $highlightedRow ? "#BDBDBD" : $noticeRow ? "#E0E0E0" : undefined,
|
|
74
|
+
}));
|
|
75
|
+
const MachineDetailTableRow = styled(TableRow, { shouldForwardProp: (prop) => prop.toString()[0] !== "$" })(({ $borderBottom }) => ({
|
|
76
|
+
...($borderBottom && {
|
|
77
|
+
"&:not(:last-child)": {
|
|
78
|
+
borderBottom: "2px solid black",
|
|
79
|
+
},
|
|
80
|
+
}),
|
|
81
|
+
}));
|
|
82
|
+
const VerticalSeperator = styled("span")({
|
|
83
|
+
fontSize: "x-large",
|
|
84
|
+
fontWeight: "lighter",
|
|
85
|
+
});
|
|
86
|
+
function FormatMinAndCnt({ min, cnt, showZero, extraMin, extraCnt, }) {
|
|
87
|
+
min ??= 0;
|
|
88
|
+
cnt ??= 0;
|
|
89
|
+
const minSpan = extraMin ? (_jsx(Tooltip, { title: extraMin, children: _jsxs("span", { children: [min.toFixed(1), " min"] }) })) : (min.toFixed(1) + " min");
|
|
90
|
+
const cntSpan = extraCnt ? (_jsx(Tooltip, { title: extraCnt, children: _jsxs("span", { children: [cntFormat.format(cnt), " cnt"] }) })) : (cntFormat.format(cnt) + " cnt");
|
|
91
|
+
if (min === 0 && cnt === 0) {
|
|
92
|
+
if (showZero) {
|
|
93
|
+
return "0";
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
return "";
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
else if (min > 0 && cnt === 0) {
|
|
100
|
+
return minSpan;
|
|
101
|
+
}
|
|
102
|
+
else if (min === 0 && cnt > 0) {
|
|
103
|
+
return cntSpan;
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
return (_jsxs("span", { children: [minSpan, " ", _jsx(VerticalSeperator, { children: "|" }), " ", cntSpan] }));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function PartDetailTable({ tool, machine }) {
|
|
110
|
+
const parts = machine
|
|
111
|
+
? LazySeq.of(tool.parts).filter((p) => p.machines.has(machine))
|
|
112
|
+
: LazySeq.of(tool.parts);
|
|
113
|
+
return (_jsxs(Table, { size: "small", sx: {
|
|
114
|
+
width: "auto",
|
|
115
|
+
ml: "10em",
|
|
116
|
+
mb: "1em",
|
|
117
|
+
}, children: [_jsx(TableHead, { children: _jsxs(TableRow, { children: [_jsx(TableCell, { children: "Part" }), _jsx(TableCell, { children: "Program" }), !machine ? _jsx(TableCell, { children: "Machines" }) : undefined, _jsx(TableCell, { align: "right", children: "Quantity" }), _jsx(TableCell, { align: "right", children: "Use/Cycle (min)" }), _jsx(TableCell, { align: "right", children: "Use/Cycle (cnt)" })] }) }), _jsx(TableBody, { children: parts.isEmpty() ? (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: 5, children: "No programs use this machine" }) })) : (parts.map((p, idx) => (_jsxs(TableRow, { children: [_jsx(TableCell, { children: _jsxs(Box, { sx: {
|
|
118
|
+
display: "flex",
|
|
119
|
+
alignItems: "center",
|
|
120
|
+
}, children: [_jsx(PartIdenticon, { part: p.partAndProg.part, size: 20 }), _jsx("span", { children: p.partAndProg.part })] }) }), _jsx(TableCell, { children: p.partAndProg.operation }), !machine ? (_jsx(TableCell, { children: p.machines.foldl((acc, m) => (acc.length > 0 ? acc + ", " : "") + m, "") })) : undefined, _jsx(TableCell, { align: "right", children: p.quantity }), _jsx(TableCell, { align: "right", children: p.scheduledUseMinutes > 0 ? p.scheduledUseMinutes.toFixed(1) : "" }), _jsx(TableCell, { align: "right", children: p.scheduledUseCnt > 0 ? cntFormat.format(p.scheduledUseCnt) : "" })] }, idx)))) })] }));
|
|
121
|
+
}
|
|
122
|
+
function MachineDetailTable({ machines }) {
|
|
123
|
+
const showSerial = useAtomValue(toolReportHasSerial);
|
|
124
|
+
const byMachine = LazySeq.of(machines).toLookupOrderedMap((m) => m.machineName, (m) => m.pocket);
|
|
125
|
+
const anyHasSubtotal = byMachine.size > 1 && byMachine.valuesToAscLazySeq().some((tools) => tools.size > 1);
|
|
126
|
+
return (_jsxs(Table, { size: "small", sx: {
|
|
127
|
+
width: "auto",
|
|
128
|
+
ml: "10em",
|
|
129
|
+
mb: "1em",
|
|
130
|
+
}, children: [_jsx(TableHead, { children: _jsxs(TableRow, { children: [_jsx(TableCell, { children: "Machine" }), _jsx(TableCell, { align: "right", children: "Pocket" }), showSerial ? _jsx(TableCell, { children: "Serial" }) : undefined, _jsx(TableCell, { align: "right", children: "Current Use " }), _jsx(TableCell, { align: "right", children: "Lifetime " }), _jsx(TableCell, { align: "right", children: "Remaining Use " })] }) }), _jsxs(TableBody, { children: [byMachine.toAscLazySeq().map(([mach, tools]) => (_jsxs(Fragment, { children: [tools.valuesToAscLazySeq().map((m, idx) => (_jsxs(MachineDetailTableRow, { "$borderBottom": anyHasSubtotal && tools.size === 1, children: [_jsx(TableCell, { children: m.machineName }), _jsx(TableCell, { align: "right", children: m.pocket }), showSerial ? _jsx(TableCell, { children: m.serial ?? "" }) : undefined, _jsx(TableCell, { align: "right", children: _jsx(FormatMinAndCnt, { min: m.currentUseMinutes, cnt: m.currentUseCnt }) }), _jsx(TableCell, { align: "right", children: _jsx(FormatMinAndCnt, { min: m.lifetimeMinutes, cnt: m.lifetimeCnt }) }), _jsx(TableCell, { align: "right", children: _jsx(FormatMinAndCnt, { min: m.remainingMinutes, cnt: m.remainingCnt, showZero: true }) })] }, idx))), byMachine.size > 1 && tools.size > 1 ? (_jsxs(MachineDetailTableRow, { "$borderBottom": true, children: [_jsx(TableCell, { colSpan: showSerial ? 4 : 3 }), _jsx(TableCell, { children: "Subtotal" }), _jsx(TableCell, { align: "right", children: _jsx(FormatMinAndCnt, { min: tools.valuesToAscLazySeq().sumBy((m) => m.remainingMinutes ?? 0), cnt: tools.valuesToAscLazySeq().sumBy((m) => m.remainingCnt ?? 0) }) })] })) : undefined] }, mach))), _jsxs(TableRow, { children: [_jsx(TableCell, { colSpan: showSerial ? 4 : 3 }), _jsx(TableCell, { children: "Total" }), _jsx(TableCell, { align: "right", children: _jsx(FormatMinAndCnt, { min: LazySeq.of(machines).sumBy((m) => m.remainingMinutes ?? 0), cnt: LazySeq.of(machines).sumBy((m) => m.remainingCnt ?? 0) }) })] })] })] }));
|
|
131
|
+
}
|
|
132
|
+
function ToolSummaryHeader() {
|
|
133
|
+
const cntsWereEstimated = useAtomValue(toolReportEstimatedToolCounts);
|
|
134
|
+
return (_jsxs(ToolTableRow, { children: [_jsx(TableCell, {}), _jsx(TableCell, { children: "Tool" }), _jsx(TableCell, { align: "right", children: _jsx(Tooltip, { title: "Expected use for all currently scheduled parts." +
|
|
135
|
+
(cntsWereEstimated ? " Counts are estimates." : ""), children: _jsx("span", { children: "Scheduled Use" }) }) }), _jsx(TableCell, { align: "right", children: _jsx(Tooltip, { title: "Remaining use summed over all machines which have a scheduled part." +
|
|
136
|
+
(cntsWereEstimated ? " Counts are estimates." : ""), children: _jsx("span", { children: "Total Remaining Use" }) }) }), _jsx(TableCell, { align: "right", children: _jsx(Tooltip, { title: "Machine with the least remaining use." + (cntsWereEstimated ? " Counts are estimates." : ""), children: _jsx("span", { children: "Smallest Remaining Use" }) }) }), _jsx(TableCell, {})] }));
|
|
137
|
+
}
|
|
138
|
+
function ToolSummaryRow({ tool }) {
|
|
139
|
+
const [open, setOpen] = useState(false);
|
|
140
|
+
const machinesWithSomePart = LazySeq.of(tool.machines).filter((m) => tool.parts.some((p) => p.machines.has(m.machineName)));
|
|
141
|
+
const schUseMin = LazySeq.of(tool.parts).sumBy((p) => p.scheduledUseMinutes * p.quantity);
|
|
142
|
+
const schUseCnt = LazySeq.of(tool.parts).sumBy((p) => p.scheduledUseCnt * p.quantity);
|
|
143
|
+
const totalRemainMin = machinesWithSomePart.sumBy((m) => m.remainingMinutes ?? 0);
|
|
144
|
+
const totalRemainCnt = machinesWithSomePart.sumBy((m) => m.remainingCnt ?? 0);
|
|
145
|
+
const minRemainMin = machinesWithSomePart
|
|
146
|
+
.groupBy((m) => m.machineName)
|
|
147
|
+
.map(([mach, tools]) => ({ mach, remainMin: LazySeq.of(tools).sumBy((m) => m.remainingMinutes ?? 0) }))
|
|
148
|
+
.minBy(({ remainMin }) => remainMin);
|
|
149
|
+
const minRemainCnt = machinesWithSomePart
|
|
150
|
+
.groupBy((m) => m.machineName)
|
|
151
|
+
.map(([mach, tools]) => ({ mach, remainCnt: LazySeq.of(tools).sumBy((m) => m.remainingCnt ?? 0) }))
|
|
152
|
+
.minBy(({ remainCnt }) => remainCnt);
|
|
153
|
+
const numCols = 6;
|
|
154
|
+
return (_jsxs(_Fragment, { children: [_jsxs(ToolTableRow, { "$noBorderBottom": true, "$highlightedRow": schUseMin > totalRemainMin || schUseCnt > totalRemainCnt, "$noticeRow": schUseMin <= totalRemainMin &&
|
|
155
|
+
schUseCnt <= totalRemainCnt &&
|
|
156
|
+
((minRemainMin && schUseMin > minRemainMin.remainMin) ||
|
|
157
|
+
(minRemainCnt && schUseCnt > minRemainCnt.remainCnt)), children: [_jsx(TableCell, { children: _jsx(IconButton, { size: "small", onClick: () => setOpen(!open), children: open ? _jsx(KeyboardArrowUpIcon, {}) : _jsx(KeyboardArrowDownIcon, {}) }) }), _jsx(TableCell, { children: tool.toolName }), _jsx(TableCell, { align: "right", children: _jsx(FormatMinAndCnt, { min: schUseMin, cnt: schUseCnt }) }), _jsx(TableCell, { align: "right", children: _jsx(FormatMinAndCnt, { min: totalRemainMin, cnt: totalRemainCnt }) }), _jsx(TableCell, { align: "right", children: _jsx(FormatMinAndCnt, { min: minRemainMin?.remainMin, cnt: minRemainCnt?.remainCnt, showZero: true, extraMin: minRemainMin?.mach, extraCnt: minRemainCnt?.mach }) }), _jsx(TableCell, {})] }), _jsx(TableRow, { children: _jsx(TableCell, { sx: { pb: "0", pt: "0" }, colSpan: numCols, children: _jsx(Collapse, { in: open, timeout: "auto", unmountOnExit: true, children: _jsxs(Box, { sx: {
|
|
158
|
+
display: "flex",
|
|
159
|
+
flexWrap: "wrap",
|
|
160
|
+
justifyContent: "space-around",
|
|
161
|
+
ml: "1em",
|
|
162
|
+
mr: "1em",
|
|
163
|
+
}, children: [_jsx("div", { children: _jsx(MachineDetailTable, { machines: tool.machines }) }), tool.parts.length === 0 ? undefined : (_jsx("div", { children: _jsx(PartDetailTable, { tool: tool }) }))] }) }) }) })] }));
|
|
164
|
+
}
|
|
165
|
+
function ToolMachineHeader({ machine }) {
|
|
166
|
+
const cntsWereEstimated = useAtomValue(toolReportEstimatedToolCounts);
|
|
167
|
+
const showSerial = useAtomValue(toolReportHasSerial);
|
|
168
|
+
return (_jsxs(ToolTableRow, { children: [_jsx(TableCell, {}), _jsxs(TableCell, { children: ["Tool In ", machine] }), _jsx(TableCell, { align: "right", children: "Pocket" }), showSerial ? _jsx(TableCell, { children: "Serial" }) : undefined, _jsx(TableCell, { align: "right", children: _jsx(Tooltip, { title: "Expected use for all currently scheduled parts." +
|
|
169
|
+
(cntsWereEstimated ? " Counts are estimates." : ""), children: _jsx("span", { children: "Scheduled Use" }) }) }), _jsx(TableCell, { align: "right", children: _jsx(Tooltip, { title: "Current recorded usage of this tool." + (cntsWereEstimated ? " Counts are estimates." : ""), children: _jsx("span", { children: "Current Use" }) }) }), _jsxs(TableCell, { align: "right", children: [_jsx(Tooltip, { title: "Current configured lifetime of this tool." + (cntsWereEstimated ? " Counts are estimates." : ""), children: _jsx("span", { children: "Lifetime" }) }), " "] }), _jsx(TableCell, { align: "right", children: _jsx(Tooltip, { title: "Lifetime minus current use." + (cntsWereEstimated ? " Counts are estimates." : ""), children: _jsx("span", { children: "Remaining Use" }) }) }), _jsx(TableCell, {})] }));
|
|
170
|
+
}
|
|
171
|
+
function ToolMachineRow({ report, pocket }) {
|
|
172
|
+
const [open, setOpen] = useState(false);
|
|
173
|
+
const showSerial = useAtomValue(toolReportHasSerial);
|
|
174
|
+
const numCols = showSerial ? 9 : 8;
|
|
175
|
+
const schMins = LazySeq.of(report.parts)
|
|
176
|
+
.filter((p) => p.machines.has(pocket.machineName))
|
|
177
|
+
.sumBy((p) => p.scheduledUseMinutes * p.quantity);
|
|
178
|
+
const schCnts = LazySeq.of(report.parts)
|
|
179
|
+
.filter((p) => p.machines.has(pocket.machineName))
|
|
180
|
+
.sumBy((p) => p.scheduledUseCnt * p.quantity);
|
|
181
|
+
return (_jsxs(_Fragment, { children: [_jsxs(ToolTableRow, { "$noBorderBottom": true, children: [_jsx(TableCell, { children: _jsx(IconButton, { size: "small", onClick: () => setOpen(!open), children: open ? _jsx(KeyboardArrowUpIcon, {}) : _jsx(KeyboardArrowDownIcon, {}) }) }), _jsx(TableCell, { children: report.toolName }), _jsx(TableCell, { align: "right", children: pocket.pocket }), showSerial ? _jsx(TableCell, { children: pocket.serial ?? "" }) : undefined, _jsx(TableCell, { align: "right", children: _jsx(FormatMinAndCnt, { min: schMins, cnt: schCnts }) }), _jsx(TableCell, { align: "right", children: _jsx(FormatMinAndCnt, { min: pocket.currentUseMinutes, cnt: pocket.currentUseCnt }) }), _jsx(TableCell, { align: "right", children: _jsx(FormatMinAndCnt, { min: pocket.lifetimeMinutes, cnt: pocket.lifetimeCnt }) }), _jsx(TableCell, { align: "right", children: _jsx(FormatMinAndCnt, { min: pocket.remainingMinutes, cnt: pocket.remainingCnt }) }), _jsx(TableCell, {})] }), _jsx(TableRow, { children: _jsx(TableCell, { sx: { pb: "0", pt: "0" }, colSpan: numCols, children: _jsx(Collapse, { in: open, timeout: "auto", unmountOnExit: true, children: _jsx(PartDetailTable, { tool: report, machine: pocket.machineName }) }) }) })] }));
|
|
182
|
+
}
|
|
183
|
+
const FilterAnyMachineKey = "__Insight__FilterAnyMachine__";
|
|
184
|
+
const toolReportMachineFilter = atom(null);
|
|
185
|
+
export function ToolSummaryTable() {
|
|
186
|
+
const machineFilter = useAtomValue(toolReportMachineFilter);
|
|
187
|
+
const tools = useAtomValue(currentToolReport);
|
|
188
|
+
if (tools === null) {
|
|
189
|
+
return _jsx("div", {});
|
|
190
|
+
}
|
|
191
|
+
return (_jsxs(Table, { stickyHeader: true, children: [_jsx(TableHead, { children: machineFilter === null ? _jsx(ToolSummaryHeader, {}) : _jsx(ToolMachineHeader, { machine: machineFilter }) }), _jsx(TableBody, { children: tools.map((tool) => machineFilter === null ? (_jsx(ToolSummaryRow, { tool: tool }, tool.toolName)) : (_jsx(Fragment, { children: tool.machines
|
|
192
|
+
.filter((m) => m.machineName === machineFilter)
|
|
193
|
+
.map((m) => (_jsx(ToolMachineRow, { report: tool, pocket: m }, m.pocket))) }, tool.toolName))) })] }));
|
|
194
|
+
}
|
|
195
|
+
function ToolNavHeader() {
|
|
196
|
+
const [machineFilter, setMachineFilter] = useAtom(toolReportMachineFilter);
|
|
197
|
+
const [reloadTime, refreshReport] = useAtom(toolReportRefreshTime);
|
|
198
|
+
const [loading, setLoading] = useState(false);
|
|
199
|
+
const machineNames = useAtomValue(machinesWithTools);
|
|
200
|
+
const copyToolReportToClipboard = useCopyToolReportToClipboard();
|
|
201
|
+
const demo = useIsDemo();
|
|
202
|
+
function refresh() {
|
|
203
|
+
setLoading(true);
|
|
204
|
+
refreshReport(new Date())
|
|
205
|
+
.catch(console.log)
|
|
206
|
+
.finally(() => setLoading(false));
|
|
207
|
+
}
|
|
208
|
+
if (demo) {
|
|
209
|
+
return _jsx("div", {});
|
|
210
|
+
}
|
|
211
|
+
else if (reloadTime === null) {
|
|
212
|
+
return (_jsx("main", { style: { margin: "2em", display: "flex", justifyContent: "center" }, children: _jsx(Fab, { color: "secondary", size: "large", variant: "extended", style: { margin: "2em" }, onClick: refresh, disabled: loading, children: _jsxs(_Fragment, { children: [loading ? (_jsx(CircularProgress, { size: 10, style: { marginRight: "1em" } })) : (_jsx(RefreshIcon, { fontSize: "inherit", style: { marginRight: "1em" } })), "Load Tools"] }) }) }));
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
return (_jsxs(Box, { component: "nav", sx: {
|
|
216
|
+
display: "flex",
|
|
217
|
+
minHeight: "2.5em",
|
|
218
|
+
alignItems: "center",
|
|
219
|
+
maxWidth: "calc(100vw - 24px - 24px)",
|
|
220
|
+
}, children: [_jsx(Tooltip, { title: "Refresh Tools", children: _jsx("div", { children: _jsx(IconButton, { onClick: refresh, disabled: loading, size: "small", children: loading ? _jsx(CircularProgress, { size: 10 }) : _jsx(RefreshIcon, { fontSize: "inherit" }) }) }) }), _jsxs("span", { style: { marginLeft: "1em" }, children: ["Tools from ", _jsx(TimeAgo, { date: reloadTime })] }), _jsx("div", { style: { flexGrow: "1" } }), _jsx(FormControl, { size: "small", children: _jsxs(Select, { autoWidth: true, displayEmpty: true, value: machineFilter ?? FilterAnyMachineKey, style: { marginLeft: "1em" }, onChange: (e) => {
|
|
221
|
+
if (e.target.value === FilterAnyMachineKey) {
|
|
222
|
+
setMachineFilter(null);
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
setMachineFilter(e.target.value);
|
|
226
|
+
}
|
|
227
|
+
}, children: [_jsx(MenuItem, { value: FilterAnyMachineKey, children: _jsx("em", { children: "All Machines" }) }), machineNames.map((n) => (_jsx(MenuItem, { value: n, children: _jsx("div", { style: { display: "flex", alignItems: "center" }, children: _jsx("span", { style: { marginRight: "1em" }, children: n }) }) }, n)))] }) }), _jsx(Tooltip, { title: "Copy to Clipboard", children: _jsx(IconButton, { style: { height: "25px", paddingTop: 0, paddingBottom: 0 }, onClick: copyToolReportToClipboard, size: "large", children: _jsx(ImportExport, {}) }) })] }));
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
export function ToolReportPage() {
|
|
231
|
+
useSetTitle("Tool Report");
|
|
232
|
+
return (_jsxs(Box, { paddingLeft: "24px", paddingRight: "24px", paddingTop: "10px", children: [_jsx(ToolNavHeader, {}), _jsx("main", { children: _jsx(DisplayLoadingAndError, { children: _jsx(ToolSummaryTable, {}) }) })] }));
|
|
233
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function WorkorderGantt(): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
/* Copyright (c) 2023, 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 { useRef, useCallback, useMemo } from "react";
|
|
35
|
+
import { atom, useAtomValue, useSetAtom } from "jotai";
|
|
36
|
+
import { green } from "@mui/material/colors";
|
|
37
|
+
import { LazySeq } from "@seedtactics/immutable-collections";
|
|
38
|
+
import { addDays, differenceInDays } from "date-fns";
|
|
39
|
+
import { currentStatus } from "../../cell-status/current-status";
|
|
40
|
+
import { Box, Stack } from "@mui/material";
|
|
41
|
+
import { PartIdenticon } from "../station-monitor/Material";
|
|
42
|
+
import { localPoint } from "../../util/chart-helpers.js";
|
|
43
|
+
import { AxisTop, GridCols } from "../AxisAndGrid";
|
|
44
|
+
import { scaleBand, scaleTime } from "d3-scale";
|
|
45
|
+
import { Tooltip } from "../ChartTooltip";
|
|
46
|
+
const tooltipData = atom(null);
|
|
47
|
+
const namesWidth = 150;
|
|
48
|
+
const marginTop = 40;
|
|
49
|
+
const marginBottom = 10;
|
|
50
|
+
const marginLeft = 20;
|
|
51
|
+
const marginRight = 10;
|
|
52
|
+
const rowSize = 80;
|
|
53
|
+
function workorderKey(work) {
|
|
54
|
+
return `${work.workorderId}-${work.part}`;
|
|
55
|
+
}
|
|
56
|
+
function utcDateOnlyToLocal(d) {
|
|
57
|
+
if (d) {
|
|
58
|
+
return new Date(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate());
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function useScales(workorders) {
|
|
65
|
+
const workKeys = workorders.map(workorderKey);
|
|
66
|
+
const dates = LazySeq.of(workorders)
|
|
67
|
+
.flatMap((w) => {
|
|
68
|
+
const filled = utcDateOnlyToLocal(w.simulatedFilled);
|
|
69
|
+
return [utcDateOnlyToLocal(w.simulatedStart), filled ? addDays(filled, 2) : null];
|
|
70
|
+
})
|
|
71
|
+
.collect((t) => t);
|
|
72
|
+
const start = dates.minBy((t) => t) ?? new Date();
|
|
73
|
+
const end = dates.maxBy((t) => t) ?? addDays(new Date(), 7);
|
|
74
|
+
const xMax = Math.max(differenceInDays(end, start) * 50, 30);
|
|
75
|
+
const yMax = Math.max(workKeys.length * rowSize, rowSize);
|
|
76
|
+
const xScale = scaleTime().domain([start, end]).range([0, xMax]);
|
|
77
|
+
const yScale = scaleBand().domain(workKeys).range([0, yMax]);
|
|
78
|
+
return { xScale, yScale };
|
|
79
|
+
}
|
|
80
|
+
function WorkorderTooltip({ tooltip }) {
|
|
81
|
+
return (_jsxs(Stack, { children: [_jsxs("div", { children: ["Workorder: ", tooltip.data.workorderId] }), _jsxs("div", { children: ["Part: ", tooltip.data.part] }), _jsxs("div", { children: ["Due Date: ", tooltip.data.dueDate.toLocaleDateString()] }), _jsxs("div", { children: ["Priority: ", tooltip.data.priority] }), _jsxs("div", { children: ["Planned Quantity: ", tooltip.data.plannedQuantity] }), _jsxs("div", { children: ["Completed Quantity: ", tooltip.data.completedQuantity] }), _jsxs("div", { children: ["Projected Start: ", utcDateOnlyToLocal(tooltip.data.simulatedStart)?.toLocaleDateString()] }), _jsxs("div", { children: ["Projected Filled: ", utcDateOnlyToLocal(tooltip.data.simulatedFilled)?.toLocaleDateString()] })] }));
|
|
82
|
+
}
|
|
83
|
+
function YAxis({ workorders }) {
|
|
84
|
+
return (_jsx("div", { children: workorders.map((work) => (_jsxs(Stack, { height: rowSize, direction: "column", alignItems: "flex-end", paddingRight: "10px", justifyContent: "center", children: [_jsx(Box, { children: work.workorderId }), _jsxs(Box, { display: "flex", alignItems: "center", children: [_jsx(PartIdenticon, { part: work.part, size: 18 }), _jsx(Box, { ml: "3px", children: work.part })] })] }, workorderKey(work)))) }));
|
|
85
|
+
}
|
|
86
|
+
function XAxis({ xScale, yScale }) {
|
|
87
|
+
return (_jsxs(_Fragment, { children: [_jsx(AxisTop, { scale: xScale, top: 0 }), _jsx(GridCols, { scale: xScale, height: yScale.range()[1] - yScale.range()[0] })] }));
|
|
88
|
+
}
|
|
89
|
+
function Series({ workorders, xScale, yScale, }) {
|
|
90
|
+
const setTooltip = useSetAtom(tooltipData);
|
|
91
|
+
const hideTooltipRef = useRef(null);
|
|
92
|
+
function showTooltip(w) {
|
|
93
|
+
return (e) => {
|
|
94
|
+
const pt = localPoint(e);
|
|
95
|
+
if (!pt)
|
|
96
|
+
return;
|
|
97
|
+
if (hideTooltipRef.current !== null) {
|
|
98
|
+
clearTimeout(hideTooltipRef.current);
|
|
99
|
+
hideTooltipRef.current = null;
|
|
100
|
+
}
|
|
101
|
+
setTooltip({
|
|
102
|
+
left: pt.x,
|
|
103
|
+
top: pt.y,
|
|
104
|
+
data: w,
|
|
105
|
+
});
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
const hideTooltip = useCallback(() => {
|
|
109
|
+
hideTooltipRef.current = setTimeout(() => {
|
|
110
|
+
setTooltip(null);
|
|
111
|
+
}, 300);
|
|
112
|
+
}, [hideTooltipRef, setTooltip]);
|
|
113
|
+
return (_jsx("g", { children: workorders.map((work) => {
|
|
114
|
+
const key = workorderKey(work);
|
|
115
|
+
return (_jsx("g", { onMouseOver: showTooltip(work), onMouseLeave: hideTooltip, children: work.simulatedStart && work.simulatedFilled ? (_jsx("rect", { x: xScale(utcDateOnlyToLocal(work.simulatedStart)), y: (yScale(key) ?? 0) + 20, width: xScale(addDays(utcDateOnlyToLocal(work.simulatedFilled), 1)) -
|
|
116
|
+
xScale(utcDateOnlyToLocal(work.simulatedStart)), height: rowSize - 40, fill: green[600] })) : undefined }, key));
|
|
117
|
+
}) }));
|
|
118
|
+
}
|
|
119
|
+
export function WorkorderGantt() {
|
|
120
|
+
const currentSt = useAtomValue(currentStatus);
|
|
121
|
+
const sortedWorkorders = useMemo(() => LazySeq.of(currentSt.workorders ?? []).toSortedArray((w) => w.simulatedFilled?.getTime() ?? null, (w) => w.simulatedStart?.getTime() ?? null, (w) => w.workorderId, (w) => w.part), [currentSt.workorders]);
|
|
122
|
+
const { xScale, yScale } = useScales(sortedWorkorders);
|
|
123
|
+
return (_jsxs(Box, { display: "flex", children: [_jsx(Box, { height: yScale.range()[1] + marginTop + marginBottom, width: namesWidth, paddingTop: `${marginTop}px`, children: _jsx(YAxis, { workorders: sortedWorkorders }) }), _jsxs(Box, { flexGrow: 1, width: 0, position: "relative", children: [_jsx("div", { style: { overflowX: "visible" }, children: _jsx("svg", { height: yScale.range()[1] + marginTop + marginBottom, width: xScale.range()[1] + marginRight + marginLeft, children: _jsxs("g", { transform: `translate(${marginLeft}, ${marginTop})`, children: [_jsx(XAxis, { yScale: yScale, xScale: xScale }), _jsx(Series, { workorders: sortedWorkorders, xScale: xScale, yScale: yScale })] }) }) }), _jsx(Tooltip, { atom: tooltipData, TooltipContent: WorkorderTooltip, chartHeight: yScale.range()[1] + marginTop + marginBottom, chartWidth: xScale.range()[1] + marginRight + marginLeft })] })] }));
|
|
124
|
+
}
|