@optifye/dashboard-core 6.11.16 → 6.11.18
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/index.d.mts +22 -1
- package/dist/index.d.ts +22 -1
- package/dist/index.js +679 -431
- package/dist/index.mjs +680 -433
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3295,11 +3295,9 @@ var normalizeShiftDefinitions = (timezone, shiftConfig) => {
|
|
|
3295
3295
|
}
|
|
3296
3296
|
return { shifts: legacyShifts, timezone: fallbackTimezone };
|
|
3297
3297
|
};
|
|
3298
|
-
var
|
|
3298
|
+
var determineActiveShiftFromDefinitions = (timezone, shifts, now4 = /* @__PURE__ */ new Date()) => {
|
|
3299
3299
|
const zonedNow = dateFnsTz.toZonedTime(now4, timezone);
|
|
3300
3300
|
const currentMinutes = zonedNow.getHours() * 60 + zonedNow.getMinutes();
|
|
3301
|
-
let chosen;
|
|
3302
|
-
let operationalDate = getOperationalDate(timezone, now4, shifts[0].startTime);
|
|
3303
3301
|
for (const shift of shifts) {
|
|
3304
3302
|
const start = parseTimeToMinutes(shift.startTime);
|
|
3305
3303
|
const endRaw = parseTimeToMinutes(shift.endTime);
|
|
@@ -3307,32 +3305,47 @@ var determineShiftFromDefinitions = (timezone, shifts, now4 = /* @__PURE__ */ ne
|
|
|
3307
3305
|
const wraps = end <= start;
|
|
3308
3306
|
if (wraps) end += 1440;
|
|
3309
3307
|
if (start <= currentMinutes && currentMinutes < end) {
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3308
|
+
return {
|
|
3309
|
+
shiftId: shift.shiftId,
|
|
3310
|
+
shiftName: shift.shiftName,
|
|
3311
|
+
startTime: shift.startTime,
|
|
3312
|
+
endTime: shift.endTime,
|
|
3313
|
+
timezone,
|
|
3314
|
+
date: getOperationalDate(timezone, now4, shift.startTime)
|
|
3315
|
+
};
|
|
3313
3316
|
}
|
|
3314
3317
|
if (start <= currentMinutes + 1440 && currentMinutes + 1440 < end) {
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
+
return {
|
|
3319
|
+
shiftId: shift.shiftId,
|
|
3320
|
+
shiftName: shift.shiftName,
|
|
3321
|
+
startTime: shift.startTime,
|
|
3322
|
+
endTime: shift.endTime,
|
|
3323
|
+
timezone,
|
|
3324
|
+
date: getOperationalDate(timezone, now4, shift.startTime)
|
|
3325
|
+
};
|
|
3318
3326
|
}
|
|
3319
3327
|
}
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3328
|
+
return null;
|
|
3329
|
+
};
|
|
3330
|
+
var getCurrentShift = (timezone, shiftConfig, now4 = /* @__PURE__ */ new Date()) => {
|
|
3331
|
+
const { shifts, timezone: effectiveTz } = normalizeShiftDefinitions(timezone, shiftConfig);
|
|
3332
|
+
const activeShift = determineActiveShiftFromDefinitions(effectiveTz, shifts, now4);
|
|
3333
|
+
if (activeShift) {
|
|
3334
|
+
return activeShift;
|
|
3323
3335
|
}
|
|
3336
|
+
const fallbackShift = shifts[0];
|
|
3324
3337
|
return {
|
|
3325
|
-
shiftId:
|
|
3326
|
-
shiftName:
|
|
3327
|
-
startTime:
|
|
3328
|
-
endTime:
|
|
3329
|
-
timezone,
|
|
3330
|
-
date:
|
|
3338
|
+
shiftId: fallbackShift.shiftId,
|
|
3339
|
+
shiftName: fallbackShift.shiftName,
|
|
3340
|
+
startTime: fallbackShift.startTime,
|
|
3341
|
+
endTime: fallbackShift.endTime,
|
|
3342
|
+
timezone: effectiveTz,
|
|
3343
|
+
date: getOperationalDate(effectiveTz, now4, fallbackShift.startTime)
|
|
3331
3344
|
};
|
|
3332
3345
|
};
|
|
3333
|
-
var
|
|
3346
|
+
var getActiveShift = (timezone, shiftConfig, now4 = /* @__PURE__ */ new Date()) => {
|
|
3334
3347
|
const { shifts, timezone: effectiveTz } = normalizeShiftDefinitions(timezone, shiftConfig);
|
|
3335
|
-
return
|
|
3348
|
+
return determineActiveShiftFromDefinitions(effectiveTz, shifts, now4);
|
|
3336
3349
|
};
|
|
3337
3350
|
var isTransitionPeriod = (timezone, shiftConfig, now4 = /* @__PURE__ */ new Date()) => {
|
|
3338
3351
|
const transitionMinutes = shiftConfig?.transitionPeriodMinutes ?? DEFAULT_TRANSITION_MINUTES;
|
|
@@ -3997,7 +4010,8 @@ var dashboardService = {
|
|
|
3997
4010
|
enable,
|
|
3998
4011
|
monitoring_mode,
|
|
3999
4012
|
assembly,
|
|
4000
|
-
video_grid_metric_mode
|
|
4013
|
+
video_grid_metric_mode,
|
|
4014
|
+
recent_flow_window_minutes
|
|
4001
4015
|
`).eq("enable", true);
|
|
4002
4016
|
if (companyId) {
|
|
4003
4017
|
query = query.eq("company_id", companyId);
|
|
@@ -4022,7 +4036,8 @@ var dashboardService = {
|
|
|
4022
4036
|
video_grid_metric_mode: normalizeVideoGridMetricMode(
|
|
4023
4037
|
line.video_grid_metric_mode,
|
|
4024
4038
|
line.assembly ?? false
|
|
4025
|
-
)
|
|
4039
|
+
),
|
|
4040
|
+
recent_flow_window_minutes: line.recent_flow_window_minutes ?? 7
|
|
4026
4041
|
}));
|
|
4027
4042
|
return transformedLines;
|
|
4028
4043
|
} catch (err) {
|
|
@@ -4059,7 +4074,7 @@ var dashboardService = {
|
|
|
4059
4074
|
}
|
|
4060
4075
|
const lineIdsToQuery = configuredLineIds;
|
|
4061
4076
|
const [line1Result, metricsResult2] = await Promise.all([
|
|
4062
|
-
supabase.from(linesTable).select("id, line_name, factory_id, monitoring_mode, assembly, video_grid_metric_mode, factories!lines_factory_id_fkey(factory_name), company_id, companies!lines_company_id_fkey(company_name:name)").eq("id", defaultLineId).single(),
|
|
4077
|
+
supabase.from(linesTable).select("id, line_name, factory_id, monitoring_mode, assembly, video_grid_metric_mode, recent_flow_window_minutes, factories!lines_factory_id_fkey(factory_name), company_id, companies!lines_company_id_fkey(company_name:name)").eq("id", defaultLineId).single(),
|
|
4063
4078
|
supabase.from(lineMetricsTable).select("*").in("line_id", lineIdsToQuery).eq("shift_id", queryShiftId).eq("date", queryDate)
|
|
4064
4079
|
]);
|
|
4065
4080
|
if (line1Result.error) throw line1Result.error;
|
|
@@ -4115,6 +4130,7 @@ var dashboardService = {
|
|
|
4115
4130
|
date: queryDate,
|
|
4116
4131
|
shift_id: queryShiftId,
|
|
4117
4132
|
monitoring_mode: line1Data.monitoring_mode ?? void 0,
|
|
4133
|
+
recent_flow_window_minutes: line1Data.recent_flow_window_minutes ?? 7,
|
|
4118
4134
|
metrics: {
|
|
4119
4135
|
avg_efficiency: avgEfficiency,
|
|
4120
4136
|
avg_cycle_time: combinedMetricsData.avg_cycle_time / numLines,
|
|
@@ -4138,7 +4154,7 @@ var dashboardService = {
|
|
|
4138
4154
|
throw new Error("Company ID must be configured for detailed line requests.");
|
|
4139
4155
|
}
|
|
4140
4156
|
const [lineResult, metricsResult] = await Promise.all([
|
|
4141
|
-
supabase.from(linesTable).select("id, line_name, factory_id, monitoring_mode, assembly, video_grid_metric_mode, factories!lines_factory_id_fkey(factory_name), company_id, companies!lines_company_id_fkey(company_name:name)").eq("id", lineIdToQuery).single(),
|
|
4157
|
+
supabase.from(linesTable).select("id, line_name, factory_id, monitoring_mode, assembly, video_grid_metric_mode, recent_flow_window_minutes, factories!lines_factory_id_fkey(factory_name), company_id, companies!lines_company_id_fkey(company_name:name)").eq("id", lineIdToQuery).single(),
|
|
4142
4158
|
supabase.from(lineMetricsTable).select("*").eq("line_id", lineIdToQuery).eq("shift_id", queryShiftId).eq("date", queryDate)
|
|
4143
4159
|
]);
|
|
4144
4160
|
if (lineResult.error) throw lineResult.error;
|
|
@@ -4163,6 +4179,7 @@ var dashboardService = {
|
|
|
4163
4179
|
date: queryDate,
|
|
4164
4180
|
shift_id: queryShiftId,
|
|
4165
4181
|
monitoring_mode: lineData.monitoring_mode ?? void 0,
|
|
4182
|
+
recent_flow_window_minutes: lineData.recent_flow_window_minutes ?? 7,
|
|
4166
4183
|
metrics: {
|
|
4167
4184
|
avg_efficiency: metrics2?.avg_efficiency ?? 0,
|
|
4168
4185
|
avg_cycle_time: metrics2?.avg_cycle_time || 0,
|
|
@@ -8496,7 +8513,8 @@ var LinesService = class {
|
|
|
8496
8513
|
videoGridMetricMode: normalizeVideoGridMetricMode(
|
|
8497
8514
|
line.video_grid_metric_mode,
|
|
8498
8515
|
line.assembly ?? false
|
|
8499
|
-
)
|
|
8516
|
+
),
|
|
8517
|
+
recentFlowWindowMinutes: line.recent_flow_window_minutes ?? 7
|
|
8500
8518
|
}));
|
|
8501
8519
|
} catch (error) {
|
|
8502
8520
|
console.error("Error fetching lines:", error);
|
|
@@ -8544,7 +8562,8 @@ var LinesService = class {
|
|
|
8544
8562
|
videoGridMetricMode: normalizeVideoGridMetricMode(
|
|
8545
8563
|
line.video_grid_metric_mode,
|
|
8546
8564
|
line.assembly ?? false
|
|
8547
|
-
)
|
|
8565
|
+
),
|
|
8566
|
+
recentFlowWindowMinutes: line.recent_flow_window_minutes ?? 7
|
|
8548
8567
|
}));
|
|
8549
8568
|
} catch (error) {
|
|
8550
8569
|
console.error("Error fetching all lines:", error);
|
|
@@ -8601,7 +8620,8 @@ var LinesService = class {
|
|
|
8601
8620
|
videoGridMetricMode: normalizeVideoGridMetricMode(
|
|
8602
8621
|
data.video_grid_metric_mode,
|
|
8603
8622
|
data.assembly ?? false
|
|
8604
|
-
)
|
|
8623
|
+
),
|
|
8624
|
+
recentFlowWindowMinutes: data.recent_flow_window_minutes ?? 7
|
|
8605
8625
|
};
|
|
8606
8626
|
} catch (error) {
|
|
8607
8627
|
console.error("Error fetching line:", error);
|
|
@@ -13355,6 +13375,7 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
|
|
|
13355
13375
|
actionName: item.action_name
|
|
13356
13376
|
}),
|
|
13357
13377
|
recent_flow_percent: item.recent_flow_percent ?? null,
|
|
13378
|
+
recent_flow_window_minutes: item.recent_flow_window_minutes ?? null,
|
|
13358
13379
|
recent_flow_effective_end_at: item.recent_flow_effective_end_at ?? null,
|
|
13359
13380
|
recent_flow_computed_at: item.recent_flow_computed_at ?? null,
|
|
13360
13381
|
incoming_wip_current: item.incoming_wip_current ?? null,
|
|
@@ -32203,6 +32224,7 @@ var LineChartComponent = ({
|
|
|
32203
32224
|
xAxisLabel,
|
|
32204
32225
|
xAxisTickFormatter,
|
|
32205
32226
|
// Pass through for X-axis tick formatting
|
|
32227
|
+
xAxisInterval,
|
|
32206
32228
|
yAxisLabel,
|
|
32207
32229
|
yAxisUnit,
|
|
32208
32230
|
yAxisDomain,
|
|
@@ -32285,6 +32307,7 @@ var LineChartComponent = ({
|
|
|
32285
32307
|
dataKey: xAxisDataKey,
|
|
32286
32308
|
label: xAxisLabel ? { value: xAxisLabel, position: "insideBottom", offset: -10 } : void 0,
|
|
32287
32309
|
tickFormatter: xAxisTickFormatter,
|
|
32310
|
+
interval: xAxisInterval,
|
|
32288
32311
|
tick: { fontSize: 12, fill: axisTickFillColor },
|
|
32289
32312
|
stroke: axisStrokeColor
|
|
32290
32313
|
}
|
|
@@ -32609,6 +32632,212 @@ var CycleTimeChart = React141__namespace.default.memo(CycleTimeChartComponent, (
|
|
|
32609
32632
|
});
|
|
32610
32633
|
});
|
|
32611
32634
|
CycleTimeChart.displayName = "CycleTimeChart";
|
|
32635
|
+
|
|
32636
|
+
// src/lib/utils/hourlyIdle.ts
|
|
32637
|
+
var DEFAULT_SHIFT_DURATION = 11;
|
|
32638
|
+
var normalizeMinuteSeries = (idleTimeHourly) => {
|
|
32639
|
+
if (!idleTimeHourly || typeof idleTimeHourly !== "object") {
|
|
32640
|
+
return {};
|
|
32641
|
+
}
|
|
32642
|
+
return Object.fromEntries(
|
|
32643
|
+
Object.entries(idleTimeHourly).map(([key, value]) => {
|
|
32644
|
+
if (Array.isArray(value)) {
|
|
32645
|
+
return [key, value];
|
|
32646
|
+
}
|
|
32647
|
+
if (value && Array.isArray(value.values)) {
|
|
32648
|
+
return [key, value.values];
|
|
32649
|
+
}
|
|
32650
|
+
return [key, []];
|
|
32651
|
+
})
|
|
32652
|
+
);
|
|
32653
|
+
};
|
|
32654
|
+
var parseTimeString = (timeValue) => {
|
|
32655
|
+
const [hoursPart, minutesPart] = timeValue.split(":");
|
|
32656
|
+
const hour = Number.parseInt(hoursPart, 10);
|
|
32657
|
+
const minute = Number.parseInt(minutesPart ?? "0", 10);
|
|
32658
|
+
const safeHour = Number.isFinite(hour) ? hour : 0;
|
|
32659
|
+
const safeMinute = Number.isFinite(minute) ? minute : 0;
|
|
32660
|
+
return {
|
|
32661
|
+
hour: safeHour,
|
|
32662
|
+
minute: safeMinute,
|
|
32663
|
+
decimalHour: safeHour + safeMinute / 60
|
|
32664
|
+
};
|
|
32665
|
+
};
|
|
32666
|
+
var buildShiftLayout = ({
|
|
32667
|
+
shiftStart,
|
|
32668
|
+
shiftEnd
|
|
32669
|
+
}) => {
|
|
32670
|
+
const shiftStartTime = parseTimeString(shiftStart);
|
|
32671
|
+
if (!shiftEnd) {
|
|
32672
|
+
return {
|
|
32673
|
+
shiftDuration: DEFAULT_SHIFT_DURATION,
|
|
32674
|
+
shiftStartTime,
|
|
32675
|
+
shiftEndTime: null,
|
|
32676
|
+
hasPartialLastHour: false
|
|
32677
|
+
};
|
|
32678
|
+
}
|
|
32679
|
+
const shiftEndTime = parseTimeString(shiftEnd);
|
|
32680
|
+
let duration = shiftEndTime.decimalHour - shiftStartTime.decimalHour;
|
|
32681
|
+
if (duration <= 0) {
|
|
32682
|
+
duration += 24;
|
|
32683
|
+
}
|
|
32684
|
+
const hasPartialLastHour = shiftEndTime.minute > 0 && shiftEndTime.minute < 60;
|
|
32685
|
+
const shiftDuration = hasPartialLastHour ? Math.ceil(duration) : Math.round(duration);
|
|
32686
|
+
return {
|
|
32687
|
+
shiftDuration,
|
|
32688
|
+
shiftStartTime,
|
|
32689
|
+
shiftEndTime,
|
|
32690
|
+
hasPartialLastHour
|
|
32691
|
+
};
|
|
32692
|
+
};
|
|
32693
|
+
var formatCompactTime = (hour, minute) => {
|
|
32694
|
+
const period = hour >= 12 ? "PM" : "AM";
|
|
32695
|
+
const hour12 = hour === 0 ? 12 : hour > 12 ? hour - 12 : hour;
|
|
32696
|
+
if (minute === 0) {
|
|
32697
|
+
return `${hour12}${period}`;
|
|
32698
|
+
}
|
|
32699
|
+
return `${hour12}:${minute.toString().padStart(2, "0")}${period}`;
|
|
32700
|
+
};
|
|
32701
|
+
var formatFullTime = (hour, minute) => {
|
|
32702
|
+
const period = hour >= 12 ? "PM" : "AM";
|
|
32703
|
+
const hour12 = hour === 0 ? 12 : hour > 12 ? hour - 12 : hour;
|
|
32704
|
+
return `${hour12}:${minute.toString().padStart(2, "0")} ${period}`;
|
|
32705
|
+
};
|
|
32706
|
+
var getSlotTimeBounds = (hourIndex, layout2) => {
|
|
32707
|
+
const isLastHour = hourIndex === layout2.shiftDuration - 1;
|
|
32708
|
+
const startDecimalHour = layout2.shiftStartTime.decimalHour + hourIndex;
|
|
32709
|
+
const startHour = Math.floor(startDecimalHour) % 24;
|
|
32710
|
+
const startMinute = Math.round(startDecimalHour % 1 * 60);
|
|
32711
|
+
let endHour;
|
|
32712
|
+
let endMinute;
|
|
32713
|
+
if (isLastHour && layout2.shiftEndTime) {
|
|
32714
|
+
endHour = layout2.shiftEndTime.hour;
|
|
32715
|
+
endMinute = layout2.shiftEndTime.minute;
|
|
32716
|
+
} else {
|
|
32717
|
+
const endDecimalHour = startDecimalHour + 1;
|
|
32718
|
+
endHour = Math.floor(endDecimalHour) % 24;
|
|
32719
|
+
endMinute = Math.round(endDecimalHour % 1 * 60);
|
|
32720
|
+
}
|
|
32721
|
+
return {
|
|
32722
|
+
startHour,
|
|
32723
|
+
startMinute,
|
|
32724
|
+
endHour,
|
|
32725
|
+
endMinute
|
|
32726
|
+
};
|
|
32727
|
+
};
|
|
32728
|
+
var getIdleArrayForHour = (hourIndex, idleTimeHourly, layout2) => {
|
|
32729
|
+
const actualHour = (layout2.shiftStartTime.hour + hourIndex) % 24;
|
|
32730
|
+
const startMinute = layout2.shiftStartTime.minute;
|
|
32731
|
+
if (startMinute > 0) {
|
|
32732
|
+
if (hourIndex === 0) {
|
|
32733
|
+
const firstHourData = idleTimeHourly[actualHour.toString()] || [];
|
|
32734
|
+
const nextHourData2 = idleTimeHourly[((actualHour + 1) % 24).toString()] || [];
|
|
32735
|
+
return [
|
|
32736
|
+
...firstHourData.slice(startMinute),
|
|
32737
|
+
...nextHourData2.slice(0, startMinute)
|
|
32738
|
+
];
|
|
32739
|
+
}
|
|
32740
|
+
if (hourIndex < layout2.shiftDuration - 1) {
|
|
32741
|
+
const currentHourData3 = idleTimeHourly[actualHour.toString()] || [];
|
|
32742
|
+
const nextHourData2 = idleTimeHourly[((actualHour + 1) % 24).toString()] || [];
|
|
32743
|
+
return [
|
|
32744
|
+
...currentHourData3.slice(startMinute),
|
|
32745
|
+
...nextHourData2.slice(0, startMinute)
|
|
32746
|
+
];
|
|
32747
|
+
}
|
|
32748
|
+
if (layout2.hasPartialLastHour && layout2.shiftEndTime) {
|
|
32749
|
+
const currentHourData3 = idleTimeHourly[actualHour.toString()] || [];
|
|
32750
|
+
const nextHourData2 = idleTimeHourly[((actualHour + 1) % 24).toString()] || [];
|
|
32751
|
+
return [
|
|
32752
|
+
...currentHourData3.slice(startMinute),
|
|
32753
|
+
...nextHourData2.slice(0, layout2.shiftEndTime.minute)
|
|
32754
|
+
];
|
|
32755
|
+
}
|
|
32756
|
+
const currentHourData2 = idleTimeHourly[actualHour.toString()] || [];
|
|
32757
|
+
const nextHourData = idleTimeHourly[((actualHour + 1) % 24).toString()] || [];
|
|
32758
|
+
return [
|
|
32759
|
+
...currentHourData2.slice(startMinute),
|
|
32760
|
+
...nextHourData.slice(0, startMinute)
|
|
32761
|
+
];
|
|
32762
|
+
}
|
|
32763
|
+
const currentHourData = idleTimeHourly[actualHour.toString()] || [];
|
|
32764
|
+
if (hourIndex === layout2.shiftDuration - 1 && layout2.hasPartialLastHour && layout2.shiftEndTime) {
|
|
32765
|
+
return currentHourData.slice(0, layout2.shiftEndTime.minute);
|
|
32766
|
+
}
|
|
32767
|
+
return currentHourData;
|
|
32768
|
+
};
|
|
32769
|
+
var buildHourlyIdleSlots = ({
|
|
32770
|
+
idleTimeHourly,
|
|
32771
|
+
shiftStart,
|
|
32772
|
+
shiftEnd
|
|
32773
|
+
}) => {
|
|
32774
|
+
const normalizedIdleTimeHourly = normalizeMinuteSeries(idleTimeHourly);
|
|
32775
|
+
const layout2 = buildShiftLayout({ shiftStart, shiftEnd });
|
|
32776
|
+
return Array.from({ length: layout2.shiftDuration }, (_, hourIndex) => {
|
|
32777
|
+
const { startHour, startMinute, endHour, endMinute } = getSlotTimeBounds(hourIndex, layout2);
|
|
32778
|
+
const idleArray = getIdleArrayForHour(hourIndex, normalizedIdleTimeHourly, layout2);
|
|
32779
|
+
const idleMinutes = idleArray.filter((value) => value === 1 || value === "1").length;
|
|
32780
|
+
return {
|
|
32781
|
+
hourIndex,
|
|
32782
|
+
hour: `${formatCompactTime(startHour, startMinute)}-${formatCompactTime(endHour, endMinute)}`,
|
|
32783
|
+
timeRange: `${formatFullTime(startHour, startMinute)} - ${formatFullTime(endHour, endMinute)}`,
|
|
32784
|
+
idleMinutes,
|
|
32785
|
+
idleArray
|
|
32786
|
+
};
|
|
32787
|
+
});
|
|
32788
|
+
};
|
|
32789
|
+
var formatIdleTimestamp = ({
|
|
32790
|
+
shiftStart,
|
|
32791
|
+
hourIndex,
|
|
32792
|
+
minuteIndex
|
|
32793
|
+
}) => {
|
|
32794
|
+
const shiftStartTime = parseTimeString(shiftStart);
|
|
32795
|
+
const totalMinutes = (shiftStartTime.hour + hourIndex) * 60 + shiftStartTime.minute + minuteIndex;
|
|
32796
|
+
const hour = Math.floor(totalMinutes / 60) % 24;
|
|
32797
|
+
const minute = totalMinutes % 60;
|
|
32798
|
+
return formatFullTime(hour, minute);
|
|
32799
|
+
};
|
|
32800
|
+
var getHourlyIdlePeriods = ({
|
|
32801
|
+
idleArray,
|
|
32802
|
+
shiftStart,
|
|
32803
|
+
hourIndex
|
|
32804
|
+
}) => {
|
|
32805
|
+
if (!Array.isArray(idleArray) || idleArray.length === 0) {
|
|
32806
|
+
return [];
|
|
32807
|
+
}
|
|
32808
|
+
const periods = [];
|
|
32809
|
+
let currentRange = null;
|
|
32810
|
+
idleArray.forEach((value, minuteIndex) => {
|
|
32811
|
+
if (value === 1 || value === "1") {
|
|
32812
|
+
if (!currentRange) {
|
|
32813
|
+
currentRange = { start: minuteIndex, end: minuteIndex };
|
|
32814
|
+
} else {
|
|
32815
|
+
currentRange.end = minuteIndex;
|
|
32816
|
+
}
|
|
32817
|
+
} else if (value !== "x" && currentRange) {
|
|
32818
|
+
periods.push(currentRange);
|
|
32819
|
+
currentRange = null;
|
|
32820
|
+
}
|
|
32821
|
+
});
|
|
32822
|
+
if (currentRange) {
|
|
32823
|
+
periods.push(currentRange);
|
|
32824
|
+
}
|
|
32825
|
+
return periods.map((period) => ({
|
|
32826
|
+
start: period.start,
|
|
32827
|
+
end: period.end,
|
|
32828
|
+
duration: period.end - period.start + 1,
|
|
32829
|
+
startTime: formatIdleTimestamp({
|
|
32830
|
+
shiftStart,
|
|
32831
|
+
hourIndex,
|
|
32832
|
+
minuteIndex: period.start
|
|
32833
|
+
}),
|
|
32834
|
+
endTime: formatIdleTimestamp({
|
|
32835
|
+
shiftStart,
|
|
32836
|
+
hourIndex,
|
|
32837
|
+
minuteIndex: period.end + 1
|
|
32838
|
+
})
|
|
32839
|
+
}));
|
|
32840
|
+
};
|
|
32612
32841
|
var CycleTimeOverTimeChart = ({
|
|
32613
32842
|
data,
|
|
32614
32843
|
idealCycleTime,
|
|
@@ -32618,7 +32847,8 @@ var CycleTimeOverTimeChart = ({
|
|
|
32618
32847
|
datasetKey,
|
|
32619
32848
|
className = "",
|
|
32620
32849
|
showIdleTime = false,
|
|
32621
|
-
idleTimeData = []
|
|
32850
|
+
idleTimeData = [],
|
|
32851
|
+
idleTimeSlots = []
|
|
32622
32852
|
}) => {
|
|
32623
32853
|
const MAX_DATA_POINTS = 40;
|
|
32624
32854
|
const containerRef = React141__namespace.default.useRef(null);
|
|
@@ -32721,50 +32951,60 @@ var CycleTimeOverTimeChart = ({
|
|
|
32721
32951
|
if (!visibleEntries.length) {
|
|
32722
32952
|
return null;
|
|
32723
32953
|
}
|
|
32724
|
-
|
|
32725
|
-
|
|
32726
|
-
|
|
32727
|
-
|
|
32728
|
-
|
|
32729
|
-
|
|
32730
|
-
|
|
32731
|
-
|
|
32732
|
-
|
|
32733
|
-
|
|
32734
|
-
|
|
32735
|
-
|
|
32736
|
-
|
|
32737
|
-
|
|
32738
|
-
|
|
32739
|
-
|
|
32740
|
-
|
|
32741
|
-
|
|
32742
|
-
|
|
32743
|
-
|
|
32744
|
-
|
|
32745
|
-
|
|
32746
|
-
|
|
32747
|
-
|
|
32748
|
-
|
|
32749
|
-
|
|
32750
|
-
|
|
32751
|
-
|
|
32752
|
-
|
|
32753
|
-
|
|
32754
|
-
|
|
32755
|
-
|
|
32756
|
-
|
|
32757
|
-
|
|
32758
|
-
|
|
32759
|
-
|
|
32760
|
-
|
|
32761
|
-
|
|
32762
|
-
|
|
32763
|
-
|
|
32764
|
-
|
|
32765
|
-
|
|
32766
|
-
|
|
32767
|
-
|
|
32954
|
+
const dataPoint = payload[0]?.payload || {};
|
|
32955
|
+
const idlePeriods = showIdleTime && typeof dataPoint.idleMinutes === "number" && dataPoint.idleMinutes > 0 ? getHourlyIdlePeriods({
|
|
32956
|
+
idleArray: dataPoint.idleArray,
|
|
32957
|
+
shiftStart,
|
|
32958
|
+
hourIndex: Number.isFinite(dataPoint.hourIndex) ? dataPoint.hourIndex : 0
|
|
32959
|
+
}) : [];
|
|
32960
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-xl shadow-xl border border-gray-100 p-4 min-w-[220px]", children: [
|
|
32961
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between mb-3", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-semibold text-gray-900 text-sm", children: dataPoint.timeRange || dataPoint.tooltip || "" }) }),
|
|
32962
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
32963
|
+
visibleEntries.map((entry) => {
|
|
32964
|
+
const numericValue = getNumericValue(entry.value);
|
|
32965
|
+
if (numericValue === null) {
|
|
32966
|
+
return null;
|
|
32967
|
+
}
|
|
32968
|
+
if (entry.name === "idleMinutes") {
|
|
32969
|
+
if (!showIdleTime) return null;
|
|
32970
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t border-gray-100 pt-2", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
32971
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-gray-500", children: "Idle Time" }),
|
|
32972
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-semibold text-orange-600 text-sm", children: [
|
|
32973
|
+
numericValue.toFixed(0),
|
|
32974
|
+
" minutes"
|
|
32975
|
+
] })
|
|
32976
|
+
] }) }, `${entry.name}-${numericValue}`);
|
|
32977
|
+
}
|
|
32978
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
32979
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-gray-500", children: "Cycle Time" }),
|
|
32980
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-semibold text-gray-900 text-sm", children: [
|
|
32981
|
+
numericValue.toFixed(1),
|
|
32982
|
+
" seconds"
|
|
32983
|
+
] })
|
|
32984
|
+
] }, `${entry.name}-${numericValue}`);
|
|
32985
|
+
}),
|
|
32986
|
+
idlePeriods.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3 bg-gray-50 rounded-lg p-2.5", children: [
|
|
32987
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-medium text-gray-700 text-xs mb-2", children: "Idle periods:" }),
|
|
32988
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-1 max-h-32 overflow-y-auto pr-1", children: idlePeriods.map((period, index) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-gray-600 flex items-center gap-2 text-xs", children: [
|
|
32989
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block w-1.5 h-1.5 bg-orange-400 rounded-full flex-shrink-0" }),
|
|
32990
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: period.duration === 1 ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
32991
|
+
period.startTime,
|
|
32992
|
+
" (",
|
|
32993
|
+
period.duration,
|
|
32994
|
+
" min)"
|
|
32995
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
32996
|
+
period.startTime,
|
|
32997
|
+
" - ",
|
|
32998
|
+
period.endTime,
|
|
32999
|
+
" (",
|
|
33000
|
+
period.duration,
|
|
33001
|
+
" mins)"
|
|
33002
|
+
] }) })
|
|
33003
|
+
] }, index)) })
|
|
33004
|
+
] })
|
|
33005
|
+
] })
|
|
33006
|
+
] });
|
|
33007
|
+
}, [getNumericValue, shiftStart, showIdleTime]);
|
|
32768
33008
|
const renderCycleDot = React141__namespace.default.useCallback((props) => {
|
|
32769
33009
|
const { cx: cx2, cy, payload } = props;
|
|
32770
33010
|
const cycleTime = getNumericValue(payload?.cycleTime);
|
|
@@ -32833,14 +33073,18 @@ var CycleTimeOverTimeChart = ({
|
|
|
32833
33073
|
r: 4,
|
|
32834
33074
|
fill: "#f59e0b",
|
|
32835
33075
|
stroke: "#fff",
|
|
32836
|
-
strokeWidth: 1
|
|
33076
|
+
strokeWidth: 1,
|
|
33077
|
+
style: {
|
|
33078
|
+
opacity: showIdleTime ? 1 : 0,
|
|
33079
|
+
transition: "opacity 0.3s ease-in-out"
|
|
33080
|
+
}
|
|
32837
33081
|
}
|
|
32838
33082
|
);
|
|
32839
|
-
}, [getNumericValue]);
|
|
33083
|
+
}, [getNumericValue, showIdleTime]);
|
|
32840
33084
|
const renderIdleActiveDot = React141__namespace.default.useCallback((props) => {
|
|
32841
33085
|
const { cx: cx2, cy, payload } = props;
|
|
32842
33086
|
const idleMinutes = getNumericValue(payload?.idleMinutes);
|
|
32843
|
-
if (idleMinutes === null) {
|
|
33087
|
+
if (idleMinutes === null || !showIdleTime) {
|
|
32844
33088
|
return /* @__PURE__ */ jsxRuntime.jsx("g", {});
|
|
32845
33089
|
}
|
|
32846
33090
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -32854,25 +33098,40 @@ var CycleTimeOverTimeChart = ({
|
|
|
32854
33098
|
strokeWidth: 2
|
|
32855
33099
|
}
|
|
32856
33100
|
);
|
|
32857
|
-
}, [getNumericValue]);
|
|
33101
|
+
}, [getNumericValue, showIdleTime]);
|
|
32858
33102
|
const chartData = React141__namespace.default.useMemo(() => Array.from({ length: DURATION }, (_, i) => {
|
|
32859
33103
|
const cycleTime = getNumericValue(finalData[i]);
|
|
32860
|
-
const
|
|
33104
|
+
const useIdleSlots = idleTimeSlots.length > 0;
|
|
33105
|
+
const idleSlot = useIdleSlots ? idleTimeSlots[i] ?? null : null;
|
|
33106
|
+
const idleMinutes = useIdleSlots ? idleSlot?.idleMinutes ?? null : getNumericValue(idleTimeData[i]);
|
|
32861
33107
|
return {
|
|
32862
33108
|
timeIndex: i,
|
|
32863
33109
|
label: formatTimeLabel(i),
|
|
32864
|
-
tooltip: formatTooltipTime(i),
|
|
33110
|
+
tooltip: idleSlot?.timeRange || formatTooltipTime(i),
|
|
33111
|
+
timeRange: idleSlot?.timeRange || formatTooltipTime(i),
|
|
33112
|
+
hourIndex: idleSlot?.hourIndex ?? i,
|
|
32865
33113
|
cycleTime,
|
|
32866
33114
|
idleMinutes,
|
|
33115
|
+
idleArray: idleSlot?.idleArray || [],
|
|
32867
33116
|
color: cycleTime !== null && cycleTime <= idealCycleTime ? "#00AB45" : "#E34329"
|
|
32868
33117
|
};
|
|
32869
|
-
}), [DURATION, finalData,
|
|
33118
|
+
}), [DURATION, finalData, idleTimeData, idleTimeSlots, idealCycleTime, getNumericValue]);
|
|
32870
33119
|
const renderLegend = () => {
|
|
32871
|
-
|
|
32872
|
-
|
|
32873
|
-
|
|
32874
|
-
|
|
32875
|
-
|
|
33120
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
33121
|
+
"div",
|
|
33122
|
+
{
|
|
33123
|
+
className: "flex items-center justify-start text-[10px] font-bold text-gray-500 mb-6 tracking-[0.05em] gap-5",
|
|
33124
|
+
style: {
|
|
33125
|
+
opacity: showIdleTime ? 1 : 0,
|
|
33126
|
+
pointerEvents: showIdleTime ? "auto" : "none",
|
|
33127
|
+
transition: "opacity 0.3s ease-in-out"
|
|
33128
|
+
},
|
|
33129
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
33130
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-2.5 h-2.5 rounded-full bg-[#f59e0b]" }),
|
|
33131
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Idle Time (min)" })
|
|
33132
|
+
] })
|
|
33133
|
+
}
|
|
33134
|
+
);
|
|
32876
33135
|
};
|
|
32877
33136
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
32878
33137
|
"div",
|
|
@@ -32943,7 +33202,7 @@ var CycleTimeOverTimeChart = ({
|
|
|
32943
33202
|
}
|
|
32944
33203
|
}
|
|
32945
33204
|
),
|
|
32946
|
-
|
|
33205
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
32947
33206
|
recharts.YAxis,
|
|
32948
33207
|
{
|
|
32949
33208
|
yAxisId: "idle",
|
|
@@ -32952,7 +33211,11 @@ var CycleTimeOverTimeChart = ({
|
|
|
32952
33211
|
width: 35,
|
|
32953
33212
|
domain: [0, 60],
|
|
32954
33213
|
tickFormatter: (value) => `${value}m`,
|
|
32955
|
-
tick: {
|
|
33214
|
+
tick: {
|
|
33215
|
+
fontSize: 11,
|
|
33216
|
+
fill: showIdleTime ? "#f59e0b" : "transparent",
|
|
33217
|
+
style: { transition: "fill 0.3s ease-in-out" }
|
|
33218
|
+
},
|
|
32956
33219
|
axisLine: false,
|
|
32957
33220
|
tickLine: false
|
|
32958
33221
|
}
|
|
@@ -33000,7 +33263,7 @@ var CycleTimeOverTimeChart = ({
|
|
|
33000
33263
|
},
|
|
33001
33264
|
`${effectiveDatasetKey}:cycle`
|
|
33002
33265
|
),
|
|
33003
|
-
|
|
33266
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
33004
33267
|
recharts.Line,
|
|
33005
33268
|
{
|
|
33006
33269
|
type: "monotone",
|
|
@@ -33015,7 +33278,11 @@ var CycleTimeOverTimeChart = ({
|
|
|
33015
33278
|
isAnimationActive: true,
|
|
33016
33279
|
animationBegin: 300,
|
|
33017
33280
|
animationDuration: 1500,
|
|
33018
|
-
animationEasing: "ease-out"
|
|
33281
|
+
animationEasing: "ease-out",
|
|
33282
|
+
style: {
|
|
33283
|
+
strokeOpacity: showIdleTime ? 1 : 0,
|
|
33284
|
+
transition: "stroke-opacity 0.3s ease-in-out"
|
|
33285
|
+
}
|
|
33019
33286
|
},
|
|
33020
33287
|
`${effectiveDatasetKey}:idle`
|
|
33021
33288
|
)
|
|
@@ -33170,89 +33437,20 @@ var HourlyOutputChartComponent = ({
|
|
|
33170
33437
|
shiftEnd,
|
|
33171
33438
|
showIdleTime = false,
|
|
33172
33439
|
idleTimeHourly,
|
|
33173
|
-
idleTimeClips,
|
|
33174
|
-
idleTimeClipClassifications,
|
|
33175
|
-
shiftDate,
|
|
33176
|
-
timezone,
|
|
33177
33440
|
className = ""
|
|
33178
33441
|
}) => {
|
|
33179
33442
|
const containerRef = React141__namespace.default.useRef(null);
|
|
33180
33443
|
const [containerReady, setContainerReady] = React141__namespace.default.useState(false);
|
|
33181
33444
|
const [containerWidth, setContainerWidth] = React141__namespace.default.useState(0);
|
|
33182
|
-
const
|
|
33183
|
-
|
|
33184
|
-
|
|
33185
|
-
const minute = parseInt(minutes || "0");
|
|
33186
|
-
const decimalHour = hour + minute / 60;
|
|
33187
|
-
return { hour, minute, decimalHour };
|
|
33188
|
-
};
|
|
33189
|
-
const shiftStartTime = getTimeFromTimeString2(shiftStart);
|
|
33190
|
-
React141__namespace.default.useMemo(() => {
|
|
33191
|
-
if (!shiftDate || !timezone) return null;
|
|
33192
|
-
const hour = shiftStartTime.hour.toString().padStart(2, "0");
|
|
33193
|
-
const minute = shiftStartTime.minute.toString().padStart(2, "0");
|
|
33194
|
-
return dateFnsTz.fromZonedTime(`${shiftDate}T${hour}:${minute}:00`, timezone);
|
|
33195
|
-
}, [shiftDate, timezone, shiftStartTime.hour, shiftStartTime.minute]);
|
|
33196
|
-
const idleClipRanges = React141__namespace.default.useMemo(() => {
|
|
33197
|
-
if (!idleTimeClips || idleTimeClips.length === 0) return [];
|
|
33198
|
-
return idleTimeClips.map((clip) => ({
|
|
33199
|
-
id: clip.id,
|
|
33200
|
-
start: clip.idle_start_time ? new Date(clip.idle_start_time) : null,
|
|
33201
|
-
end: clip.idle_end_time ? new Date(clip.idle_end_time) : null
|
|
33202
|
-
})).filter((clip) => clip.start && clip.end);
|
|
33203
|
-
}, [idleTimeClips]);
|
|
33204
|
-
React141__namespace.default.useCallback((rangeStart, rangeEnd) => {
|
|
33205
|
-
if (!rangeStart || !rangeEnd || idleClipRanges.length === 0) {
|
|
33206
|
-
return "Reason unavailable";
|
|
33207
|
-
}
|
|
33208
|
-
const matchingClip = idleClipRanges.find((clip) => rangeStart >= clip.start && rangeEnd <= clip.end) || idleClipRanges.find((clip) => rangeStart < clip.end && rangeEnd > clip.start);
|
|
33209
|
-
if (!matchingClip) {
|
|
33210
|
-
return "Reason unavailable";
|
|
33211
|
-
}
|
|
33212
|
-
const classification = idleTimeClipClassifications?.[matchingClip.id];
|
|
33213
|
-
if (!classification || classification.status === "processing") {
|
|
33214
|
-
return "Analyzing...";
|
|
33215
|
-
}
|
|
33216
|
-
if (!classification.label) {
|
|
33217
|
-
return "Reason unavailable";
|
|
33218
|
-
}
|
|
33219
|
-
return classification.displayName || classification.label.replace(/_/g, " ");
|
|
33220
|
-
}, [idleClipRanges, idleTimeClipClassifications]);
|
|
33221
|
-
const { shiftDuration, shiftEndTime, hasPartialLastHour } = React141__namespace.default.useMemo(() => {
|
|
33222
|
-
console.log("[HourlyOutputChart] Calculating shift duration with:", {
|
|
33445
|
+
const idleSlots = React141__namespace.default.useMemo(
|
|
33446
|
+
() => buildHourlyIdleSlots({
|
|
33447
|
+
idleTimeHourly,
|
|
33223
33448
|
shiftStart,
|
|
33224
|
-
shiftEnd
|
|
33225
|
-
|
|
33226
|
-
|
|
33227
|
-
|
|
33228
|
-
|
|
33229
|
-
return {
|
|
33230
|
-
shiftDuration: 11,
|
|
33231
|
-
shiftEndTime: null,
|
|
33232
|
-
hasPartialLastHour: false
|
|
33233
|
-
};
|
|
33234
|
-
}
|
|
33235
|
-
const endTime = getTimeFromTimeString2(shiftEnd);
|
|
33236
|
-
let duration = endTime.decimalHour - shiftStartTime.decimalHour;
|
|
33237
|
-
if (duration <= 0) {
|
|
33238
|
-
duration += 24;
|
|
33239
|
-
}
|
|
33240
|
-
const hasPartial = endTime.minute > 0 && endTime.minute < 60;
|
|
33241
|
-
const hourCount = hasPartial ? Math.ceil(duration) : Math.round(duration);
|
|
33242
|
-
console.log("[HourlyOutputChart] Shift calculation results:", {
|
|
33243
|
-
endTime,
|
|
33244
|
-
duration,
|
|
33245
|
-
hasPartial,
|
|
33246
|
-
hourCount
|
|
33247
|
-
});
|
|
33248
|
-
return {
|
|
33249
|
-
shiftDuration: hourCount,
|
|
33250
|
-
shiftEndTime: endTime,
|
|
33251
|
-
hasPartialLastHour: hasPartial
|
|
33252
|
-
};
|
|
33253
|
-
}, [shiftEnd, shiftStartTime.decimalHour]);
|
|
33254
|
-
const SHIFT_DURATION = shiftDuration;
|
|
33255
|
-
shiftEndTime ? shiftEndTime.hour : (shiftStartTime.hour + SHIFT_DURATION) % 24;
|
|
33449
|
+
shiftEnd
|
|
33450
|
+
}),
|
|
33451
|
+
[idleTimeHourly, shiftStart, shiftEnd]
|
|
33452
|
+
);
|
|
33453
|
+
const SHIFT_DURATION = idleSlots.length;
|
|
33256
33454
|
const [animatedData, setAnimatedData] = React141__namespace.default.useState(
|
|
33257
33455
|
() => Array(SHIFT_DURATION).fill(0)
|
|
33258
33456
|
);
|
|
@@ -33375,121 +33573,22 @@ var HourlyOutputChartComponent = ({
|
|
|
33375
33573
|
}
|
|
33376
33574
|
return { interval: 0, angle: -30, height: 64, tickFont: 9, tickMargin: 6 };
|
|
33377
33575
|
}, [containerWidth]);
|
|
33378
|
-
const formatHour = React141__namespace.default.useCallback((hourIndex) => {
|
|
33379
|
-
const isLastHour = hourIndex === SHIFT_DURATION - 1;
|
|
33380
|
-
const startDecimalHour = shiftStartTime.decimalHour + hourIndex;
|
|
33381
|
-
const startHour = Math.floor(startDecimalHour) % 24;
|
|
33382
|
-
const startMinute = Math.round(startDecimalHour % 1 * 60);
|
|
33383
|
-
let endHour, endMinute;
|
|
33384
|
-
if (isLastHour && shiftEndTime) {
|
|
33385
|
-
endHour = shiftEndTime.hour;
|
|
33386
|
-
endMinute = shiftEndTime.minute;
|
|
33387
|
-
} else {
|
|
33388
|
-
const endDecimalHour = startDecimalHour + 1;
|
|
33389
|
-
endHour = Math.floor(endDecimalHour) % 24;
|
|
33390
|
-
endMinute = Math.round(endDecimalHour % 1 * 60);
|
|
33391
|
-
}
|
|
33392
|
-
const formatTime5 = (h, m) => {
|
|
33393
|
-
const period = h >= 12 ? "PM" : "AM";
|
|
33394
|
-
const hour12 = h === 0 ? 12 : h > 12 ? h - 12 : h;
|
|
33395
|
-
if (m === 0) {
|
|
33396
|
-
return `${hour12}${period}`;
|
|
33397
|
-
}
|
|
33398
|
-
return `${hour12}:${m.toString().padStart(2, "0")}${period}`;
|
|
33399
|
-
};
|
|
33400
|
-
return `${formatTime5(startHour, startMinute)}-${formatTime5(endHour, endMinute)}`;
|
|
33401
|
-
}, [shiftStartTime.decimalHour, SHIFT_DURATION, shiftEndTime]);
|
|
33402
|
-
const formatTimeRange2 = React141__namespace.default.useCallback((hourIndex) => {
|
|
33403
|
-
const isLastHour = hourIndex === SHIFT_DURATION - 1;
|
|
33404
|
-
const startDecimalHour = shiftStartTime.decimalHour + hourIndex;
|
|
33405
|
-
const startHour = Math.floor(startDecimalHour) % 24;
|
|
33406
|
-
const startMinute = Math.round(startDecimalHour % 1 * 60);
|
|
33407
|
-
let endHour, endMinute;
|
|
33408
|
-
if (isLastHour && shiftEndTime) {
|
|
33409
|
-
endHour = shiftEndTime.hour;
|
|
33410
|
-
endMinute = shiftEndTime.minute;
|
|
33411
|
-
} else {
|
|
33412
|
-
const endDecimalHour = startDecimalHour + 1;
|
|
33413
|
-
endHour = Math.floor(endDecimalHour) % 24;
|
|
33414
|
-
endMinute = Math.round(endDecimalHour % 1 * 60);
|
|
33415
|
-
}
|
|
33416
|
-
const formatTime5 = (h, m) => {
|
|
33417
|
-
const period = h >= 12 ? "PM" : "AM";
|
|
33418
|
-
const hour12 = h === 0 ? 12 : h > 12 ? h - 12 : h;
|
|
33419
|
-
return `${hour12}:${m.toString().padStart(2, "0")} ${period}`;
|
|
33420
|
-
};
|
|
33421
|
-
return `${formatTime5(startHour, startMinute)} - ${formatTime5(endHour, endMinute)}`;
|
|
33422
|
-
}, [shiftStartTime.decimalHour, SHIFT_DURATION, shiftEndTime]);
|
|
33423
33576
|
const chartData = React141__namespace.default.useMemo(() => {
|
|
33424
33577
|
return Array.from({ length: SHIFT_DURATION }, (_, i) => {
|
|
33425
|
-
const
|
|
33426
|
-
const startMinute = shiftStartTime.minute;
|
|
33427
|
-
let idleArray = [];
|
|
33428
|
-
let idleMinutes = 0;
|
|
33429
|
-
if (idleTimeHourly) {
|
|
33430
|
-
if (startMinute > 0) {
|
|
33431
|
-
if (i === 0) {
|
|
33432
|
-
const firstHourData = idleTimeHourly[actualHour.toString()] || [];
|
|
33433
|
-
const nextHour = (actualHour + 1) % 24;
|
|
33434
|
-
const nextHourData = idleTimeHourly[nextHour.toString()] || [];
|
|
33435
|
-
idleArray = [
|
|
33436
|
-
...firstHourData.slice(startMinute) || [],
|
|
33437
|
-
...nextHourData.slice(0, startMinute) || []
|
|
33438
|
-
];
|
|
33439
|
-
} else if (i < SHIFT_DURATION - 1) {
|
|
33440
|
-
const currentHourData = idleTimeHourly[actualHour.toString()] || [];
|
|
33441
|
-
const nextHour = (actualHour + 1) % 24;
|
|
33442
|
-
const nextHourData = idleTimeHourly[nextHour.toString()] || [];
|
|
33443
|
-
idleArray = [
|
|
33444
|
-
...currentHourData.slice(startMinute) || [],
|
|
33445
|
-
...nextHourData.slice(0, startMinute) || []
|
|
33446
|
-
];
|
|
33447
|
-
} else {
|
|
33448
|
-
const hasPartialLastHour2 = shiftEndTime && shiftEndTime.minute > 0 && shiftEndTime.minute < 60;
|
|
33449
|
-
if (hasPartialLastHour2) {
|
|
33450
|
-
const currentHourData = idleTimeHourly[actualHour.toString()] || [];
|
|
33451
|
-
const nextHour = (actualHour + 1) % 24;
|
|
33452
|
-
const nextHourData = idleTimeHourly[nextHour.toString()] || [];
|
|
33453
|
-
if (startMinute > 0) {
|
|
33454
|
-
const firstPart = currentHourData.slice(startMinute) || [];
|
|
33455
|
-
const secondPart = nextHourData.slice(0, shiftEndTime.minute) || [];
|
|
33456
|
-
idleArray = [...firstPart, ...secondPart];
|
|
33457
|
-
} else {
|
|
33458
|
-
idleArray = currentHourData.slice(0, shiftEndTime.minute) || [];
|
|
33459
|
-
}
|
|
33460
|
-
} else {
|
|
33461
|
-
const currentHourData = idleTimeHourly[actualHour.toString()] || [];
|
|
33462
|
-
const nextHour = (actualHour + 1) % 24;
|
|
33463
|
-
const nextHourData = idleTimeHourly[nextHour.toString()] || [];
|
|
33464
|
-
idleArray = [
|
|
33465
|
-
...currentHourData.slice(startMinute) || [],
|
|
33466
|
-
...nextHourData.slice(0, startMinute) || []
|
|
33467
|
-
];
|
|
33468
|
-
}
|
|
33469
|
-
}
|
|
33470
|
-
} else {
|
|
33471
|
-
const currentHourData = idleTimeHourly[actualHour.toString()] || [];
|
|
33472
|
-
if (i === SHIFT_DURATION - 1 && shiftEndTime && shiftEndTime.minute > 0 && shiftEndTime.minute < 60) {
|
|
33473
|
-
idleArray = currentHourData.slice(0, shiftEndTime.minute) || [];
|
|
33474
|
-
} else {
|
|
33475
|
-
idleArray = currentHourData || [];
|
|
33476
|
-
}
|
|
33477
|
-
}
|
|
33478
|
-
}
|
|
33479
|
-
idleMinutes = idleArray.filter((val) => val === "1" || val === 1).length;
|
|
33578
|
+
const idleSlot = idleSlots[i];
|
|
33480
33579
|
return {
|
|
33481
|
-
hourIndex: i,
|
|
33482
|
-
hour:
|
|
33483
|
-
timeRange:
|
|
33580
|
+
hourIndex: idleSlot?.hourIndex ?? i,
|
|
33581
|
+
hour: idleSlot?.hour || "",
|
|
33582
|
+
timeRange: idleSlot?.timeRange || "",
|
|
33484
33583
|
output: animatedData[i] || 0,
|
|
33485
33584
|
originalOutput: data[i] || 0,
|
|
33486
33585
|
// Keep original data for labels
|
|
33487
33586
|
color: (animatedData[i] || 0) >= Math.round(pphThreshold) ? "#00AB45" : "#E34329",
|
|
33488
|
-
idleMinutes,
|
|
33489
|
-
idleArray
|
|
33587
|
+
idleMinutes: idleSlot?.idleMinutes || 0,
|
|
33588
|
+
idleArray: idleSlot?.idleArray || []
|
|
33490
33589
|
};
|
|
33491
33590
|
});
|
|
33492
|
-
}, [animatedData, data, pphThreshold,
|
|
33591
|
+
}, [animatedData, data, pphThreshold, idleSlots, SHIFT_DURATION]);
|
|
33493
33592
|
const IdleBar = React141__namespace.default.useMemo(() => {
|
|
33494
33593
|
if (!idleBarState.visible) return null;
|
|
33495
33594
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -33651,36 +33750,11 @@ var HourlyOutputChartComponent = ({
|
|
|
33651
33750
|
content: (props) => {
|
|
33652
33751
|
if (!props.active || !props.payload || props.payload.length === 0) return null;
|
|
33653
33752
|
const data2 = props.payload[0].payload;
|
|
33654
|
-
const
|
|
33655
|
-
|
|
33656
|
-
|
|
33657
|
-
data2.
|
|
33658
|
-
|
|
33659
|
-
if (!currentRange) {
|
|
33660
|
-
currentRange = { start: idx, end: idx };
|
|
33661
|
-
} else {
|
|
33662
|
-
currentRange.end = idx;
|
|
33663
|
-
}
|
|
33664
|
-
} else if (val !== "x" && currentRange) {
|
|
33665
|
-
idleRanges.push(currentRange);
|
|
33666
|
-
currentRange = null;
|
|
33667
|
-
}
|
|
33668
|
-
});
|
|
33669
|
-
if (currentRange) {
|
|
33670
|
-
idleRanges.push(currentRange);
|
|
33671
|
-
}
|
|
33672
|
-
}
|
|
33673
|
-
const formatIdleTimestamp = (minuteIdx) => {
|
|
33674
|
-
const fallbackIndex = chartData.findIndex((item) => item.timeRange === data2.timeRange);
|
|
33675
|
-
const hourOffset = Number.isFinite(data2.hourIndex) ? data2.hourIndex : fallbackIndex;
|
|
33676
|
-
const safeHourOffset = hourOffset >= 0 ? hourOffset : 0;
|
|
33677
|
-
const totalMinutes = (shiftStartTime.hour + safeHourOffset) * 60 + shiftStartTime.minute + minuteIdx;
|
|
33678
|
-
const hour = Math.floor(totalMinutes / 60) % 24;
|
|
33679
|
-
const minute = totalMinutes % 60;
|
|
33680
|
-
const period = hour >= 12 ? "PM" : "AM";
|
|
33681
|
-
const hour12 = hour === 0 ? 12 : hour > 12 ? hour - 12 : hour;
|
|
33682
|
-
return `${hour12}:${minute.toString().padStart(2, "0")} ${period}`;
|
|
33683
|
-
};
|
|
33753
|
+
const idlePeriods = showIdleTime ? getHourlyIdlePeriods({
|
|
33754
|
+
idleArray: data2.idleArray,
|
|
33755
|
+
shiftStart,
|
|
33756
|
+
hourIndex: Number.isFinite(data2.hourIndex) ? data2.hourIndex : 0
|
|
33757
|
+
}) : [];
|
|
33684
33758
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-xl shadow-xl border border-gray-100 p-4 min-w-[220px]", children: [
|
|
33685
33759
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between mb-3", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-semibold text-gray-900 text-sm", children: data2.timeRange }) }),
|
|
33686
33760
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
@@ -33699,27 +33773,22 @@ var HourlyOutputChartComponent = ({
|
|
|
33699
33773
|
" minutes"
|
|
33700
33774
|
] })
|
|
33701
33775
|
] }) }),
|
|
33702
|
-
|
|
33776
|
+
idlePeriods.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3 bg-gray-50 rounded-lg p-2.5", children: [
|
|
33703
33777
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-medium text-gray-700 text-xs mb-2", children: "Idle periods:" }),
|
|
33704
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-1 max-h-32 overflow-y-auto pr-1", children:
|
|
33705
|
-
const duration = range.end - range.start + 1;
|
|
33706
|
-
const startTime = formatIdleTimestamp(range.start);
|
|
33707
|
-
const endTime = formatIdleTimestamp(range.end + 1);
|
|
33708
|
-
const fallbackIndex = chartData.findIndex((item) => item.timeRange === data2.timeRange);
|
|
33709
|
-
Number.isFinite(data2.hourIndex) ? data2.hourIndex : fallbackIndex;
|
|
33778
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-1 max-h-32 overflow-y-auto pr-1", children: idlePeriods.map((period, index) => {
|
|
33710
33779
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-gray-600 flex items-center gap-2 text-xs", children: [
|
|
33711
33780
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block w-1.5 h-1.5 bg-orange-400 rounded-full flex-shrink-0" }),
|
|
33712
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: duration === 1 ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
33713
|
-
startTime,
|
|
33781
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: period.duration === 1 ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
33782
|
+
period.startTime,
|
|
33714
33783
|
" (",
|
|
33715
|
-
duration,
|
|
33784
|
+
period.duration,
|
|
33716
33785
|
" min)"
|
|
33717
33786
|
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
33718
|
-
startTime,
|
|
33787
|
+
period.startTime,
|
|
33719
33788
|
" - ",
|
|
33720
|
-
endTime,
|
|
33789
|
+
period.endTime,
|
|
33721
33790
|
" (",
|
|
33722
|
-
duration,
|
|
33791
|
+
period.duration,
|
|
33723
33792
|
" mins)"
|
|
33724
33793
|
] }) })
|
|
33725
33794
|
] }, index);
|
|
@@ -33874,9 +33943,8 @@ var HourlyOutputChart = React141__namespace.default.memo(HourlyOutputChartCompon
|
|
|
33874
33943
|
HourlyOutputChart.displayName = "HourlyOutputChart";
|
|
33875
33944
|
|
|
33876
33945
|
// src/components/dashboard/grid/videoGridMetricUtils.ts
|
|
33877
|
-
var VIDEO_GRID_LEGEND_LABEL = "7 Minute Efficiency";
|
|
33878
33946
|
var MAP_GRID_LEGEND_LABEL = "Efficiency";
|
|
33879
|
-
var MIXED_VIDEO_GRID_LEGEND_LABEL = "Efficiency";
|
|
33947
|
+
var MIXED_VIDEO_GRID_LEGEND_LABEL = "Flow Efficiency";
|
|
33880
33948
|
var isFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value);
|
|
33881
33949
|
var isVideoGridRecentFlowEnabled = (workspace) => isRecentFlowVideoGridMetricMode(
|
|
33882
33950
|
workspace.video_grid_metric_mode,
|
|
@@ -33975,8 +34043,15 @@ var getVideoGridLegendLabel = (workspaces) => {
|
|
|
33975
34043
|
if (recentFlowEnabledCount === 0) {
|
|
33976
34044
|
return MAP_GRID_LEGEND_LABEL;
|
|
33977
34045
|
}
|
|
34046
|
+
const recentFlowWindows = new Set(
|
|
34047
|
+
visibleWorkspaces.filter(isVideoGridRecentFlowEnabled).map((workspace) => workspace.recent_flow_window_minutes ?? 7).filter((value) => typeof value === "number" && Number.isFinite(value))
|
|
34048
|
+
);
|
|
33978
34049
|
if (recentFlowEnabledCount === visibleWorkspaces.length) {
|
|
33979
|
-
|
|
34050
|
+
if (recentFlowWindows.size === 1) {
|
|
34051
|
+
const [windowMinutes] = Array.from(recentFlowWindows);
|
|
34052
|
+
return `${windowMinutes} Minute Efficiency`;
|
|
34053
|
+
}
|
|
34054
|
+
return MIXED_VIDEO_GRID_LEGEND_LABEL;
|
|
33980
34055
|
}
|
|
33981
34056
|
return MIXED_VIDEO_GRID_LEGEND_LABEL;
|
|
33982
34057
|
};
|
|
@@ -34028,8 +34103,9 @@ var VideoCard = React141__namespace.default.memo(({
|
|
|
34028
34103
|
const isRecentFlowCard = isVideoGridRecentFlowEnabled(workspace);
|
|
34029
34104
|
const hasDisplayMetric = typeof videoGridDisplayValue === "number" && Number.isFinite(videoGridDisplayValue);
|
|
34030
34105
|
const hasBarMetric = typeof videoGridMetricValue === "number" && Number.isFinite(videoGridMetricValue);
|
|
34106
|
+
const shouldRenderMetricBadge = hasDisplayMetric;
|
|
34031
34107
|
const badgeTitle = hasVideoGridRecentFlow(workspace) ? `Flow ${Math.round(videoGridDisplayValue ?? 0)}%` : isRecentFlowCard ? "Flow unavailable" : `Efficiency ${Math.round(videoGridDisplayValue ?? 0)}%`;
|
|
34032
|
-
const badgeLabel =
|
|
34108
|
+
const badgeLabel = `${Math.round(videoGridDisplayValue ?? 0)}%`;
|
|
34033
34109
|
const efficiencyOverlayClass = videoGridColorState === "green" ? "bg-[#00D654]/25" : videoGridColorState === "yellow" ? "bg-[#FFD700]/30" : videoGridColorState === "red" ? "bg-[#FF2D0A]/30" : "bg-transparent";
|
|
34034
34110
|
const efficiencyBarClass = videoGridColorState === "green" ? "bg-[#00AB45]" : videoGridColorState === "yellow" ? "bg-[#FFB020]" : videoGridColorState === "red" ? "bg-[#E34329]" : "bg-gray-500/70";
|
|
34035
34111
|
const efficiencyStatus = videoGridColorState === "green" ? "High" : videoGridColorState === "yellow" ? "Medium" : videoGridColorState === "red" ? "Low" : "Neutral";
|
|
@@ -34105,7 +34181,7 @@ var VideoCard = React141__namespace.default.memo(({
|
|
|
34105
34181
|
lastSeenText
|
|
34106
34182
|
] })
|
|
34107
34183
|
] }) }),
|
|
34108
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: `absolute ${compact ? "top-1 right-1" : "top-2 right-2"} z-30`, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
34184
|
+
shouldRenderMetricBadge && /* @__PURE__ */ jsxRuntime.jsx("div", { className: `absolute ${compact ? "top-1 right-1" : "top-2 right-2"} z-30`, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
34109
34185
|
"div",
|
|
34110
34186
|
{
|
|
34111
34187
|
"data-testid": "video-card-metric-badge",
|
|
@@ -35922,7 +35998,7 @@ var HourlyUptimeChartComponent = ({
|
|
|
35922
35998
|
idleRanges.push(currentRange);
|
|
35923
35999
|
}
|
|
35924
36000
|
}
|
|
35925
|
-
const
|
|
36001
|
+
const formatIdleTimestamp2 = (minuteIdx) => {
|
|
35926
36002
|
const totalMinutes = (shiftStartTime.hour + data.hourIndex) * 60 + shiftStartTime.minute + minuteIdx;
|
|
35927
36003
|
const hour = Math.floor(totalMinutes / 60) % 24;
|
|
35928
36004
|
const minute = totalMinutes % 60;
|
|
@@ -35952,8 +36028,8 @@ var HourlyUptimeChartComponent = ({
|
|
|
35952
36028
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-medium text-gray-700 text-xs mb-2", children: "Idle periods:" }),
|
|
35953
36029
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-1 max-h-32 overflow-y-auto pr-1", children: idleRanges.map((range, index) => {
|
|
35954
36030
|
const duration = range.end - range.start + 1;
|
|
35955
|
-
const startTime =
|
|
35956
|
-
const endTime =
|
|
36031
|
+
const startTime = formatIdleTimestamp2(range.start);
|
|
36032
|
+
const endTime = formatIdleTimestamp2(range.end + 1);
|
|
35957
36033
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-gray-600 flex items-center gap-2 text-xs", children: [
|
|
35958
36034
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block w-1.5 h-1.5 bg-orange-400 rounded-full flex-shrink-0" }),
|
|
35959
36035
|
/* @__PURE__ */ jsxRuntime.jsx("span", { children: duration === 1 ? `${startTime} (${duration} min)` : `${startTime} - ${endTime} (${duration} mins)` })
|
|
@@ -49832,8 +49908,7 @@ var WorkspaceCycleTimeMetricCards = ({
|
|
|
49832
49908
|
{
|
|
49833
49909
|
data: idleTimeData.chartData,
|
|
49834
49910
|
isLoading: idleTimeData.isLoading,
|
|
49835
|
-
error: idleTimeData.error
|
|
49836
|
-
variant: "bar"
|
|
49911
|
+
error: idleTimeData.error
|
|
49837
49912
|
}
|
|
49838
49913
|
) })
|
|
49839
49914
|
] })
|
|
@@ -69201,56 +69276,43 @@ var WorkspaceDetailView = ({
|
|
|
69201
69276
|
const canToggleChartIdleTime = !isUptimeMode && !shouldShowCycleTimeLoadingState && !shouldShowCycleTimeUnavailableState;
|
|
69202
69277
|
const idleClipDate = date || workspace?.date || calculatedOperationalDate || getOperationalDate(timezone);
|
|
69203
69278
|
const idleClipShiftId = parsedShiftId ?? workspace?.shift_id;
|
|
69204
|
-
const
|
|
69205
|
-
|
|
69206
|
-
|
|
69207
|
-
|
|
69208
|
-
|
|
69209
|
-
|
|
69210
|
-
|
|
69211
|
-
|
|
69212
|
-
|
|
69213
|
-
|
|
69214
|
-
|
|
69215
|
-
|
|
69216
|
-
|
|
69217
|
-
|
|
69218
|
-
|
|
69219
|
-
|
|
69220
|
-
|
|
69221
|
-
if (endSlotMins > endTotal) endSlotMins = endTotal;
|
|
69222
|
-
let idleCount = 0;
|
|
69223
|
-
for (let m = startSlotMins; m < endSlotMins; m++) {
|
|
69224
|
-
const hourKey = Math.floor(m / 60) % 24;
|
|
69225
|
-
const minuteKey = m % 60;
|
|
69226
|
-
const hourData = idleTimeHourlyObj[hourKey.toString()] || [];
|
|
69227
|
-
if (Array.isArray(hourData)) {
|
|
69228
|
-
const val = hourData[minuteKey];
|
|
69229
|
-
if (val === 1 || val === "1") {
|
|
69230
|
-
idleCount++;
|
|
69231
|
-
}
|
|
69232
|
-
}
|
|
69233
|
-
}
|
|
69234
|
-
result[i] = idleCount;
|
|
69235
|
-
}
|
|
69236
|
-
return result;
|
|
69237
|
-
}, [shouldShowCycleTimeChart, workspace?.idle_time_hourly, workspace?.shift_start, workspace?.shift_end]);
|
|
69279
|
+
const rawHourlyIdleSlots = React141.useMemo(
|
|
69280
|
+
() => !shouldShowCycleTimeChart || !authoritativeCycleMetrics?.shift_start ? [] : buildHourlyIdleSlots({
|
|
69281
|
+
idleTimeHourly: authoritativeCycleMetrics.idle_time_hourly,
|
|
69282
|
+
shiftStart: authoritativeCycleMetrics.shift_start,
|
|
69283
|
+
shiftEnd: authoritativeCycleMetrics.shift_end
|
|
69284
|
+
}),
|
|
69285
|
+
[
|
|
69286
|
+
shouldShowCycleTimeChart,
|
|
69287
|
+
authoritativeCycleMetrics?.idle_time_hourly,
|
|
69288
|
+
authoritativeCycleMetrics?.shift_start,
|
|
69289
|
+
authoritativeCycleMetrics?.shift_end
|
|
69290
|
+
]
|
|
69291
|
+
);
|
|
69292
|
+
const rawHourlyIdleMinutes = React141.useMemo(
|
|
69293
|
+
() => rawHourlyIdleSlots.map((slot) => slot.idleMinutes),
|
|
69294
|
+
[rawHourlyIdleSlots]
|
|
69295
|
+
);
|
|
69238
69296
|
const hourlyIdleMinutes = React141.useMemo(
|
|
69239
69297
|
() => maskFutureHourlySeries({
|
|
69240
69298
|
data: rawHourlyIdleMinutes,
|
|
69241
|
-
shiftStart:
|
|
69242
|
-
shiftEnd:
|
|
69299
|
+
shiftStart: authoritativeCycleMetrics?.shift_start,
|
|
69300
|
+
shiftEnd: authoritativeCycleMetrics?.shift_end,
|
|
69243
69301
|
shiftDate: idleClipDate,
|
|
69244
69302
|
timezone: effectiveCycleTimeTimezone
|
|
69245
69303
|
}),
|
|
69246
69304
|
[
|
|
69247
69305
|
rawHourlyIdleMinutes,
|
|
69248
|
-
|
|
69249
|
-
|
|
69306
|
+
authoritativeCycleMetrics?.shift_start,
|
|
69307
|
+
authoritativeCycleMetrics?.shift_end,
|
|
69250
69308
|
idleClipDate,
|
|
69251
69309
|
effectiveCycleTimeTimezone
|
|
69252
69310
|
]
|
|
69253
69311
|
);
|
|
69312
|
+
const hourlyIdleSlots = React141.useMemo(
|
|
69313
|
+
() => rawHourlyIdleSlots.map((slot, index) => hourlyIdleMinutes[index] === null ? null : slot),
|
|
69314
|
+
[rawHourlyIdleSlots, hourlyIdleMinutes]
|
|
69315
|
+
);
|
|
69254
69316
|
const cycleTimeUnavailableView = React141.useMemo(() => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full h-full rounded-lg border border-amber-200 bg-amber-50/70 px-6 py-5 text-center flex flex-col items-center justify-center", children: [
|
|
69255
69317
|
/* @__PURE__ */ jsxRuntime.jsx("h4", { className: "text-base font-semibold text-amber-900", children: "Cycle data unavailable" }),
|
|
69256
69318
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 max-w-md text-sm text-amber-800", children: "This workstation has cycle-time metrics for the selected shift, but no matching `cycle_completion` clips were found for the chart." }),
|
|
@@ -69806,7 +69868,8 @@ var WorkspaceDetailView = ({
|
|
|
69806
69868
|
xAxisMode: "hourly",
|
|
69807
69869
|
datasetKey: cycleTimeDatasetKey,
|
|
69808
69870
|
showIdleTime: showChartIdleTime,
|
|
69809
|
-
idleTimeData: hourlyIdleMinutes
|
|
69871
|
+
idleTimeData: hourlyIdleMinutes,
|
|
69872
|
+
idleTimeSlots: hourlyIdleSlots
|
|
69810
69873
|
}
|
|
69811
69874
|
) : null : /* @__PURE__ */ jsxRuntime.jsx(
|
|
69812
69875
|
HourlyOutputChart2,
|
|
@@ -69935,7 +69998,8 @@ var WorkspaceDetailView = ({
|
|
|
69935
69998
|
xAxisMode: "hourly",
|
|
69936
69999
|
datasetKey: cycleTimeDatasetKey,
|
|
69937
70000
|
showIdleTime: showChartIdleTime,
|
|
69938
|
-
idleTimeData: hourlyIdleMinutes
|
|
70001
|
+
idleTimeData: hourlyIdleMinutes,
|
|
70002
|
+
idleTimeSlots: hourlyIdleSlots
|
|
69939
70003
|
}
|
|
69940
70004
|
) : null : /* @__PURE__ */ jsxRuntime.jsx(
|
|
69941
70005
|
HourlyOutputChart2,
|
|
@@ -76453,7 +76517,7 @@ var formatComparisonWindow = ({
|
|
|
76453
76517
|
shiftMode
|
|
76454
76518
|
}) => {
|
|
76455
76519
|
if (comparisonStrategy === "previous_full_week") return "last week";
|
|
76456
|
-
if (comparisonStrategy === "matched_range" &&
|
|
76520
|
+
if (comparisonStrategy === "matched_range" && currentDayCount === 1 && previousDayCount === 1) {
|
|
76457
76521
|
return "previous day";
|
|
76458
76522
|
}
|
|
76459
76523
|
if (!previousDayCount || !Number.isFinite(previousDayCount)) return "previous range";
|
|
@@ -76476,8 +76540,14 @@ var buildDeltaBadge = (delta, options) => {
|
|
|
76476
76540
|
};
|
|
76477
76541
|
};
|
|
76478
76542
|
var normalizeShiftLabel = (shiftName, shiftMode) => {
|
|
76543
|
+
if (shiftMode === "all") {
|
|
76544
|
+
return "All Shifts";
|
|
76545
|
+
}
|
|
76479
76546
|
const trimmedName = shiftName?.trim();
|
|
76480
76547
|
if (trimmedName) {
|
|
76548
|
+
const normalizedName = trimmedName.toLowerCase();
|
|
76549
|
+
if (normalizedName === "day") return "Day Shift";
|
|
76550
|
+
if (normalizedName === "night") return "Night Shift";
|
|
76481
76551
|
return /shift/i.test(trimmedName) ? trimmedName : `${trimmedName} Shift`;
|
|
76482
76552
|
}
|
|
76483
76553
|
if (shiftMode === "night") return "Night Shift";
|
|
@@ -76486,6 +76556,9 @@ var normalizeShiftLabel = (shiftName, shiftMode) => {
|
|
|
76486
76556
|
var getShiftIcon = (shiftName, shiftMode) => {
|
|
76487
76557
|
const normalizedName = (shiftName || "").toLowerCase();
|
|
76488
76558
|
const normalizedMode = shiftMode || "day";
|
|
76559
|
+
if (normalizedMode === "all") {
|
|
76560
|
+
return /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" }) });
|
|
76561
|
+
}
|
|
76489
76562
|
if (normalizedName.includes("day") || normalizedName.includes("morning") || normalizedMode === "day") {
|
|
76490
76563
|
return /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" }) });
|
|
76491
76564
|
}
|
|
@@ -76564,6 +76637,7 @@ var OperationsOverviewHeader = React141__namespace.default.memo(({
|
|
|
76564
76637
|
}) => {
|
|
76565
76638
|
bumpRenderCounter();
|
|
76566
76639
|
const subtitleRange = displayDateRange || dateRange;
|
|
76640
|
+
const showLiveShiftMeta = isLiveScope && trendMode !== "all";
|
|
76567
76641
|
const liveShiftLabel = React141__namespace.default.useMemo(
|
|
76568
76642
|
() => normalizeShiftLabel(liveShiftName, trendMode),
|
|
76569
76643
|
[liveShiftName, trendMode]
|
|
@@ -76701,8 +76775,8 @@ var OperationsOverviewHeader = React141__namespace.default.memo(({
|
|
|
76701
76775
|
] }),
|
|
76702
76776
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1 flex flex-wrap items-center justify-center gap-x-2 gap-y-1 text-[11px] font-medium text-slate-500 min-w-0", children: [
|
|
76703
76777
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-center", children: mobileSubtitle }),
|
|
76704
|
-
|
|
76705
|
-
|
|
76778
|
+
showLiveShiftMeta ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-slate-300", children: "|" }) : null,
|
|
76779
|
+
showLiveShiftMeta ? /* @__PURE__ */ jsxRuntime.jsx("span", { "data-testid": "operations-overview-live-meta", className: "inline-flex items-center gap-1.5 text-gray-600 min-w-0", children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1 justify-center truncate", children: [
|
|
76706
76780
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "opacity-75 shrink-0", children: liveShiftIcon }),
|
|
76707
76781
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: liveShiftLabel })
|
|
76708
76782
|
] }) }) : null
|
|
@@ -76749,7 +76823,7 @@ var OperationsOverviewHeader = React141__namespace.default.memo(({
|
|
|
76749
76823
|
] }),
|
|
76750
76824
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 flex flex-wrap items-center justify-center gap-x-3 gap-y-1 text-xs sm:text-sm font-medium text-slate-500 text-center", children: [
|
|
76751
76825
|
/* @__PURE__ */ jsxRuntime.jsx("span", { children: desktopSubtitle }),
|
|
76752
|
-
|
|
76826
|
+
showLiveShiftMeta ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
76753
76827
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-slate-300", children: "|" }),
|
|
76754
76828
|
/* @__PURE__ */ jsxRuntime.jsx("span", { "data-testid": "operations-overview-live-meta", className: "inline-flex items-center gap-1.5 text-gray-600", children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1.5 justify-center", children: [
|
|
76755
76829
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "opacity-75", children: liveShiftIcon }),
|
|
@@ -77323,18 +77397,18 @@ var EfficiencyTrendCard = React141__namespace.default.memo(({
|
|
|
77323
77397
|
return (trend.data.points || []).map((point, index) => ({
|
|
77324
77398
|
name: (() => {
|
|
77325
77399
|
const rawLabel = point.label?.trim() || "";
|
|
77326
|
-
if (
|
|
77327
|
-
return rawLabel || `Hour ${index + 1}`;
|
|
77328
|
-
}
|
|
77329
|
-
if (rawLabel && !/^Hour\s+\d+$/i.test(rawLabel)) {
|
|
77400
|
+
if (rawLabel) {
|
|
77330
77401
|
return rawLabel;
|
|
77331
77402
|
}
|
|
77403
|
+
if (!hourlyLabelStartTime) {
|
|
77404
|
+
return "";
|
|
77405
|
+
}
|
|
77332
77406
|
const hourIndex = typeof point.hour_index === "number" ? point.hour_index : index;
|
|
77333
77407
|
const [hoursPart, minutesPart] = hourlyLabelStartTime.split(":");
|
|
77334
77408
|
const startHours = Number(hoursPart);
|
|
77335
77409
|
const startMinutes = Number(minutesPart);
|
|
77336
77410
|
if (!Number.isFinite(startHours) || !Number.isFinite(startMinutes)) {
|
|
77337
|
-
return
|
|
77411
|
+
return "";
|
|
77338
77412
|
}
|
|
77339
77413
|
const totalMinutes = startHours * 60 + startMinutes + hourIndex * 60;
|
|
77340
77414
|
const hour24 = Math.floor(totalMinutes / 60) % 24;
|
|
@@ -77396,6 +77470,12 @@ var EfficiencyTrendCard = React141__namespace.default.memo(({
|
|
|
77396
77470
|
if (!dayOfWeek || typeof label !== "string") return label;
|
|
77397
77471
|
return `${label} (${dayOfWeek})`;
|
|
77398
77472
|
}, [isHourlyTrend]);
|
|
77473
|
+
const trendXAxisTickFormatter = React141__namespace.default.useCallback((value, index) => {
|
|
77474
|
+
if (!isHourlyTrend) {
|
|
77475
|
+
return typeof value === "string" ? value : String(value ?? "");
|
|
77476
|
+
}
|
|
77477
|
+
return index % 2 === 0 ? typeof value === "string" ? value : String(value ?? "") : "";
|
|
77478
|
+
}, [isHourlyTrend]);
|
|
77399
77479
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-xl shadow-[0_2px_10px_-3px_rgba(6,81,237,0.1)] border border-slate-100 flex flex-col overflow-hidden text-left", children: [
|
|
77400
77480
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-5 flex-none flex justify-between items-center border-b border-slate-50/50", children: /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-semibold text-gray-700", children: "Efficiency Trend" }) }),
|
|
77401
77481
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-[250px] w-full p-4 pt-4 relative", children: showInitialSkeleton ? /* @__PURE__ */ jsxRuntime.jsx(OverviewChartSkeleton, {}) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 pb-2 pr-4 pl-1", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -77404,6 +77484,8 @@ var EfficiencyTrendCard = React141__namespace.default.memo(({
|
|
|
77404
77484
|
data: trendData,
|
|
77405
77485
|
lines: efficiencyLineConfig,
|
|
77406
77486
|
xAxisDataKey: "name",
|
|
77487
|
+
xAxisInterval: isHourlyTrend ? 0 : void 0,
|
|
77488
|
+
xAxisTickFormatter: trendXAxisTickFormatter,
|
|
77407
77489
|
yAxisUnit: "%",
|
|
77408
77490
|
yAxisDomain: [0, 100],
|
|
77409
77491
|
showLegend: false,
|
|
@@ -77909,6 +77991,62 @@ var classifyShiftBucket = ({
|
|
|
77909
77991
|
if (normalizedShiftId === 1) return "night";
|
|
77910
77992
|
return null;
|
|
77911
77993
|
};
|
|
77994
|
+
var getShiftWindowsForConfig = (shiftConfig, timezone) => {
|
|
77995
|
+
if (shiftConfig?.shifts && shiftConfig.shifts.length > 0) {
|
|
77996
|
+
return shiftConfig.shifts.map((shift) => ({
|
|
77997
|
+
shiftId: shift.shiftId,
|
|
77998
|
+
shiftName: shift.shiftName,
|
|
77999
|
+
startTime: shift.startTime,
|
|
78000
|
+
endTime: shift.endTime
|
|
78001
|
+
}));
|
|
78002
|
+
}
|
|
78003
|
+
const windows = [];
|
|
78004
|
+
if (shiftConfig?.dayShift) {
|
|
78005
|
+
windows.push({
|
|
78006
|
+
shiftId: shiftConfig.dayShift.id ?? 0,
|
|
78007
|
+
shiftName: shiftConfig.dayShift.name || "Day Shift",
|
|
78008
|
+
startTime: shiftConfig.dayShift.startTime || "06:00",
|
|
78009
|
+
endTime: shiftConfig.dayShift.endTime || "18:00"
|
|
78010
|
+
});
|
|
78011
|
+
}
|
|
78012
|
+
if (shiftConfig?.nightShift) {
|
|
78013
|
+
windows.push({
|
|
78014
|
+
shiftId: shiftConfig.nightShift.id ?? 1,
|
|
78015
|
+
shiftName: shiftConfig.nightShift.name || "Night Shift",
|
|
78016
|
+
startTime: shiftConfig.nightShift.startTime || "18:00",
|
|
78017
|
+
endTime: shiftConfig.nightShift.endTime || "06:00"
|
|
78018
|
+
});
|
|
78019
|
+
}
|
|
78020
|
+
if (windows.length > 0) {
|
|
78021
|
+
return windows;
|
|
78022
|
+
}
|
|
78023
|
+
return [
|
|
78024
|
+
{
|
|
78025
|
+
shiftId: 0,
|
|
78026
|
+
shiftName: "Day Shift",
|
|
78027
|
+
startTime: "06:00",
|
|
78028
|
+
endTime: "18:00"
|
|
78029
|
+
},
|
|
78030
|
+
{
|
|
78031
|
+
shiftId: 1,
|
|
78032
|
+
shiftName: "Night Shift",
|
|
78033
|
+
startTime: "18:00",
|
|
78034
|
+
endTime: "06:00"
|
|
78035
|
+
}
|
|
78036
|
+
];
|
|
78037
|
+
};
|
|
78038
|
+
var normalizeShiftWindowMinutes = (startTime, endTime) => {
|
|
78039
|
+
const startMinutes = parseTimeToMinutes3(startTime);
|
|
78040
|
+
const endMinutesRaw = parseTimeToMinutes3(endTime);
|
|
78041
|
+
if (startMinutes === null || endMinutesRaw === null) {
|
|
78042
|
+
return null;
|
|
78043
|
+
}
|
|
78044
|
+
let endMinutes = endMinutesRaw;
|
|
78045
|
+
if (endMinutes <= startMinutes) {
|
|
78046
|
+
endMinutes += 24 * 60;
|
|
78047
|
+
}
|
|
78048
|
+
return { startMinutes, endMinutes };
|
|
78049
|
+
};
|
|
77912
78050
|
var PlantHeadView = () => {
|
|
77913
78051
|
const supabase = useSupabase();
|
|
77914
78052
|
const entityConfig = useEntityConfig();
|
|
@@ -77934,6 +78072,7 @@ var PlantHeadView = () => {
|
|
|
77934
78072
|
const [selectedSupervisorId, setSelectedSupervisorId] = React141__namespace.default.useState("all");
|
|
77935
78073
|
const [selectedLineIds, setSelectedLineIds] = React141__namespace.default.useState([]);
|
|
77936
78074
|
const [isInitialScopeReady, setIsInitialScopeReady] = React141__namespace.default.useState(false);
|
|
78075
|
+
const [shiftResolutionTick, setShiftResolutionTick] = React141__namespace.default.useState(0);
|
|
77937
78076
|
const hasAutoInitializedScopeRef = React141__namespace.default.useRef(false);
|
|
77938
78077
|
const hasUserAdjustedScopeRef = React141__namespace.default.useRef(false);
|
|
77939
78078
|
React141__namespace.default.useEffect(() => {
|
|
@@ -78038,34 +78177,151 @@ var PlantHeadView = () => {
|
|
|
78038
78177
|
shiftConfigMap,
|
|
78039
78178
|
isLoading: isShiftConfigLoading
|
|
78040
78179
|
} = useMultiLineShiftConfigs(scopedLineIds, staticShiftConfig);
|
|
78041
|
-
|
|
78042
|
-
|
|
78043
|
-
|
|
78044
|
-
|
|
78045
|
-
|
|
78046
|
-
|
|
78047
|
-
|
|
78048
|
-
|
|
78049
|
-
|
|
78050
|
-
const referenceShiftConfig = referenceLineId ? shiftConfigMap.get(referenceLineId) : null;
|
|
78051
|
-
const normalizedGroupShiftId = normalizeShiftId(group.shiftId);
|
|
78052
|
-
const shiftDefinition = referenceShiftConfig?.shifts?.find((shift) => normalizeShiftId(shift.shiftId) === normalizedGroupShiftId);
|
|
78053
|
-
const resolvedMode = classifyShiftBucket({
|
|
78054
|
-
shiftName: group.shiftName,
|
|
78055
|
-
shiftId: normalizedGroupShiftId,
|
|
78056
|
-
startTime: shiftDefinition?.startTime,
|
|
78057
|
-
endTime: shiftDefinition?.endTime
|
|
78058
|
-
});
|
|
78059
|
-
if (!resolvedMode || resolvedMode === "all") return null;
|
|
78060
|
-
return {
|
|
78061
|
-
date: group.date,
|
|
78062
|
-
trendMode: resolvedMode,
|
|
78063
|
-
shiftName: shiftDefinition?.shiftName || group.shiftName || null
|
|
78180
|
+
React141__namespace.default.useEffect(() => {
|
|
78181
|
+
if (scopedLineIds.length === 0 || isShiftConfigLoading) {
|
|
78182
|
+
return;
|
|
78183
|
+
}
|
|
78184
|
+
const intervalId = window.setInterval(() => {
|
|
78185
|
+
setShiftResolutionTick((previous) => previous + 1);
|
|
78186
|
+
}, 6e4);
|
|
78187
|
+
return () => {
|
|
78188
|
+
clearInterval(intervalId);
|
|
78064
78189
|
};
|
|
78065
|
-
}, [
|
|
78190
|
+
}, [isShiftConfigLoading, scopedLineIds.length]);
|
|
78191
|
+
const shiftResolutionNow = React141__namespace.default.useMemo(
|
|
78192
|
+
() => /* @__PURE__ */ new Date(),
|
|
78193
|
+
[shiftResolutionTick]
|
|
78194
|
+
);
|
|
78195
|
+
const earliestDayShiftStartTime = React141__namespace.default.useMemo(() => {
|
|
78196
|
+
const candidateStarts = [];
|
|
78197
|
+
scopedLineIds.forEach((lineId) => {
|
|
78198
|
+
const shiftConfig = shiftConfigMap.get(lineId) || staticShiftConfig;
|
|
78199
|
+
getShiftWindowsForConfig(shiftConfig).forEach((shift) => {
|
|
78200
|
+
const bucket = classifyShiftBucket({
|
|
78201
|
+
shiftName: shift.shiftName,
|
|
78202
|
+
shiftId: shift.shiftId,
|
|
78203
|
+
startTime: shift.startTime,
|
|
78204
|
+
endTime: shift.endTime
|
|
78205
|
+
});
|
|
78206
|
+
const startMinutes = parseTimeToMinutes3(shift.startTime);
|
|
78207
|
+
if (bucket === "day" && startMinutes !== null) {
|
|
78208
|
+
candidateStarts.push(startMinutes);
|
|
78209
|
+
}
|
|
78210
|
+
});
|
|
78211
|
+
});
|
|
78212
|
+
if (candidateStarts.length === 0) {
|
|
78213
|
+
scopedLineIds.forEach((lineId) => {
|
|
78214
|
+
const shiftConfig = shiftConfigMap.get(lineId) || staticShiftConfig;
|
|
78215
|
+
getShiftWindowsForConfig(shiftConfig).forEach((shift) => {
|
|
78216
|
+
const startMinutes = parseTimeToMinutes3(shift.startTime);
|
|
78217
|
+
if (startMinutes !== null) {
|
|
78218
|
+
candidateStarts.push(startMinutes);
|
|
78219
|
+
}
|
|
78220
|
+
});
|
|
78221
|
+
});
|
|
78222
|
+
}
|
|
78223
|
+
if (candidateStarts.length === 0) {
|
|
78224
|
+
return "06:00";
|
|
78225
|
+
}
|
|
78226
|
+
const earliestMinutes = Math.min(...candidateStarts);
|
|
78227
|
+
const hours = Math.floor(earliestMinutes / 60);
|
|
78228
|
+
const minutes = earliestMinutes % 60;
|
|
78229
|
+
return `${hours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}`;
|
|
78230
|
+
}, [appTimezone, scopedLineIds, shiftConfigMap, staticShiftConfig]);
|
|
78231
|
+
const resolvedOperationalToday = React141__namespace.default.useMemo(
|
|
78232
|
+
() => getOperationalDate(appTimezone, shiftResolutionNow, earliestDayShiftStartTime),
|
|
78233
|
+
[appTimezone, earliestDayShiftStartTime, shiftResolutionNow]
|
|
78234
|
+
);
|
|
78235
|
+
const activeLineShiftStates = React141__namespace.default.useMemo(() => {
|
|
78236
|
+
return scopedLineIds.flatMap((lineId) => {
|
|
78237
|
+
const shiftConfig = shiftConfigMap.get(lineId) || staticShiftConfig;
|
|
78238
|
+
const activeShift = getActiveShift(appTimezone, shiftConfig, shiftResolutionNow);
|
|
78239
|
+
if (!activeShift) {
|
|
78240
|
+
return [];
|
|
78241
|
+
}
|
|
78242
|
+
const trendBucket = classifyShiftBucket({
|
|
78243
|
+
shiftName: activeShift.shiftName,
|
|
78244
|
+
shiftId: activeShift.shiftId,
|
|
78245
|
+
startTime: activeShift.startTime,
|
|
78246
|
+
endTime: activeShift.endTime
|
|
78247
|
+
});
|
|
78248
|
+
if (!trendBucket || trendBucket === "all") {
|
|
78249
|
+
return [];
|
|
78250
|
+
}
|
|
78251
|
+
return [{
|
|
78252
|
+
lineId,
|
|
78253
|
+
trendMode: trendBucket,
|
|
78254
|
+
shiftId: activeShift.shiftId,
|
|
78255
|
+
shiftName: activeShift.shiftName || null,
|
|
78256
|
+
startTime: activeShift.startTime || null,
|
|
78257
|
+
endTime: activeShift.endTime || null,
|
|
78258
|
+
date: activeShift.date
|
|
78259
|
+
}];
|
|
78260
|
+
});
|
|
78261
|
+
}, [appTimezone, scopedLineIds, shiftConfigMap, shiftResolutionNow, staticShiftConfig]);
|
|
78262
|
+
const hasActiveDayShiftLine = React141__namespace.default.useMemo(
|
|
78263
|
+
() => activeLineShiftStates.some((shift) => shift.trendMode === "day" && shift.date === resolvedOperationalToday),
|
|
78264
|
+
[activeLineShiftStates, resolvedOperationalToday]
|
|
78265
|
+
);
|
|
78266
|
+
const hasActiveNightShiftLine = React141__namespace.default.useMemo(
|
|
78267
|
+
() => activeLineShiftStates.some((shift) => shift.trendMode === "night" && shift.date === resolvedOperationalToday),
|
|
78268
|
+
[activeLineShiftStates, resolvedOperationalToday]
|
|
78269
|
+
);
|
|
78270
|
+
const resolvedTrendMode = isInitialScopeReady ? trendMode : "all";
|
|
78271
|
+
const hourlyWindowStartTime = React141__namespace.default.useMemo(() => {
|
|
78272
|
+
if (scopedLineIds.length === 0) {
|
|
78273
|
+
return null;
|
|
78274
|
+
}
|
|
78275
|
+
const startCandidates = [];
|
|
78276
|
+
const endCandidates = [];
|
|
78277
|
+
scopedLineIds.forEach((lineId) => {
|
|
78278
|
+
const shiftConfig = shiftConfigMap.get(lineId) || staticShiftConfig;
|
|
78279
|
+
getShiftWindowsForConfig(shiftConfig).forEach((shift) => {
|
|
78280
|
+
const bucket = classifyShiftBucket({
|
|
78281
|
+
shiftName: shift.shiftName,
|
|
78282
|
+
shiftId: shift.shiftId,
|
|
78283
|
+
startTime: shift.startTime,
|
|
78284
|
+
endTime: shift.endTime
|
|
78285
|
+
});
|
|
78286
|
+
if (resolvedTrendMode !== "all" && bucket !== resolvedTrendMode) {
|
|
78287
|
+
return;
|
|
78288
|
+
}
|
|
78289
|
+
const normalizedWindow = normalizeShiftWindowMinutes(shift.startTime, shift.endTime);
|
|
78290
|
+
if (!normalizedWindow) {
|
|
78291
|
+
return;
|
|
78292
|
+
}
|
|
78293
|
+
startCandidates.push(normalizedWindow.startMinutes);
|
|
78294
|
+
endCandidates.push(normalizedWindow.endMinutes);
|
|
78295
|
+
});
|
|
78296
|
+
});
|
|
78297
|
+
if (resolvedTrendMode === "all") {
|
|
78298
|
+
const dayStartCandidates = startCandidates.length > 0 ? scopedLineIds.flatMap((lineId) => {
|
|
78299
|
+
const shiftConfig = shiftConfigMap.get(lineId) || staticShiftConfig;
|
|
78300
|
+
return getShiftWindowsForConfig(shiftConfig).map((shift) => {
|
|
78301
|
+
const bucket = classifyShiftBucket({
|
|
78302
|
+
shiftName: shift.shiftName,
|
|
78303
|
+
shiftId: shift.shiftId,
|
|
78304
|
+
startTime: shift.startTime,
|
|
78305
|
+
endTime: shift.endTime
|
|
78306
|
+
});
|
|
78307
|
+
return bucket === "day" ? parseTimeToMinutes3(shift.startTime) : null;
|
|
78308
|
+
}).filter((value) => value !== null);
|
|
78309
|
+
}) : [];
|
|
78310
|
+
if (dayStartCandidates.length > 0) {
|
|
78311
|
+
startCandidates.splice(0, startCandidates.length, ...dayStartCandidates);
|
|
78312
|
+
}
|
|
78313
|
+
}
|
|
78314
|
+
if (startCandidates.length === 0 || endCandidates.length === 0) {
|
|
78315
|
+
return null;
|
|
78316
|
+
}
|
|
78317
|
+
const earliestMinutes = Math.min(...startCandidates);
|
|
78318
|
+
const hours = Math.floor(earliestMinutes / 60);
|
|
78319
|
+
const minutes = earliestMinutes % 60;
|
|
78320
|
+
return `${hours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}`;
|
|
78321
|
+
}, [appTimezone, resolvedTrendMode, scopedLineIds, shiftConfigMap, staticShiftConfig]);
|
|
78066
78322
|
const isShiftScopeResolved = React141__namespace.default.useMemo(
|
|
78067
|
-
() => !isShiftConfigLoading
|
|
78068
|
-
[
|
|
78323
|
+
() => !isShiftConfigLoading,
|
|
78324
|
+
[isShiftConfigLoading]
|
|
78069
78325
|
);
|
|
78070
78326
|
const initializedTimezoneRef = React141__namespace.default.useRef(appTimezone);
|
|
78071
78327
|
React141__namespace.default.useEffect(() => {
|
|
@@ -78092,7 +78348,7 @@ var PlantHeadView = () => {
|
|
|
78092
78348
|
return;
|
|
78093
78349
|
}
|
|
78094
78350
|
setDateRange((previous) => {
|
|
78095
|
-
const nextStartKey =
|
|
78351
|
+
const nextStartKey = resolvedOperationalToday || fallbackOperationalDate;
|
|
78096
78352
|
if (previous.startKey === nextStartKey && previous.endKey === nextStartKey) {
|
|
78097
78353
|
return previous;
|
|
78098
78354
|
}
|
|
@@ -78101,11 +78357,11 @@ var PlantHeadView = () => {
|
|
|
78101
78357
|
endKey: nextStartKey
|
|
78102
78358
|
};
|
|
78103
78359
|
});
|
|
78104
|
-
setTrendMode(
|
|
78360
|
+
setTrendMode("all");
|
|
78105
78361
|
setUsesThisWeekComparison(false);
|
|
78106
78362
|
hasAutoInitializedScopeRef.current = true;
|
|
78107
78363
|
setIsInitialScopeReady(true);
|
|
78108
|
-
}, [
|
|
78364
|
+
}, [fallbackOperationalDate, isShiftScopeResolved, resolvedOperationalToday, scopedLineIds.length]);
|
|
78109
78365
|
const handleDateRangeChange = React141__namespace.default.useCallback((range, meta) => {
|
|
78110
78366
|
hasUserAdjustedScopeRef.current = true;
|
|
78111
78367
|
setIsInitialScopeReady(true);
|
|
@@ -78182,45 +78438,36 @@ var PlantHeadView = () => {
|
|
|
78182
78438
|
if (isInitialScopeReady) {
|
|
78183
78439
|
return dateRange;
|
|
78184
78440
|
}
|
|
78185
|
-
const nextStartKey =
|
|
78441
|
+
const nextStartKey = resolvedOperationalToday || fallbackOperationalDate;
|
|
78186
78442
|
return {
|
|
78187
78443
|
startKey: nextStartKey,
|
|
78188
78444
|
endKey: nextStartKey
|
|
78189
78445
|
};
|
|
78190
|
-
}, [
|
|
78446
|
+
}, [dateRange, fallbackOperationalDate, isInitialScopeReady, resolvedOperationalToday]);
|
|
78191
78447
|
const effectiveTrendMode = React141__namespace.default.useMemo(
|
|
78192
|
-
() =>
|
|
78193
|
-
[
|
|
78448
|
+
() => resolvedTrendMode,
|
|
78449
|
+
[resolvedTrendMode]
|
|
78194
78450
|
);
|
|
78195
78451
|
const hourlyLabelStartTime = React141__namespace.default.useMemo(() => {
|
|
78196
|
-
if (
|
|
78197
|
-
return null;
|
|
78198
|
-
}
|
|
78199
|
-
const shiftStartTimes = /* @__PURE__ */ new Set();
|
|
78200
|
-
scopedLineIds.forEach((lineId) => {
|
|
78201
|
-
const shiftConfig = shiftConfigMap.get(lineId);
|
|
78202
|
-
const matchingShift = shiftConfig?.shifts?.find((shift) => classifyShiftBucket({
|
|
78203
|
-
shiftName: shift.shiftName,
|
|
78204
|
-
shiftId: shift.shiftId,
|
|
78205
|
-
startTime: shift.startTime,
|
|
78206
|
-
endTime: shift.endTime
|
|
78207
|
-
}) === effectiveTrendMode);
|
|
78208
|
-
if (matchingShift?.startTime) {
|
|
78209
|
-
shiftStartTimes.add(matchingShift.startTime);
|
|
78210
|
-
}
|
|
78211
|
-
});
|
|
78212
|
-
if (shiftStartTimes.size !== 1) {
|
|
78452
|
+
if (scopedLineIds.length === 0) {
|
|
78213
78453
|
return null;
|
|
78214
78454
|
}
|
|
78215
|
-
return
|
|
78216
|
-
}, [
|
|
78217
|
-
const
|
|
78218
|
-
() => effectiveDateRange.startKey === effectiveDateRange.endKey
|
|
78219
|
-
[effectiveDateRange.endKey, effectiveDateRange.startKey
|
|
78455
|
+
return hourlyWindowStartTime;
|
|
78456
|
+
}, [hourlyWindowStartTime, scopedLineIds.length]);
|
|
78457
|
+
const isSingleDayScope = React141__namespace.default.useMemo(
|
|
78458
|
+
() => effectiveDateRange.startKey === effectiveDateRange.endKey,
|
|
78459
|
+
[effectiveDateRange.endKey, effectiveDateRange.startKey]
|
|
78220
78460
|
);
|
|
78221
78461
|
const isLiveScope = React141__namespace.default.useMemo(
|
|
78222
|
-
() =>
|
|
78223
|
-
[
|
|
78462
|
+
() => isSingleDayScope && effectiveDateRange.startKey === resolvedOperationalToday && (effectiveTrendMode === "all" || effectiveTrendMode === "day" && hasActiveDayShiftLine || effectiveTrendMode === "night" && hasActiveNightShiftLine),
|
|
78463
|
+
[
|
|
78464
|
+
effectiveDateRange.startKey,
|
|
78465
|
+
effectiveTrendMode,
|
|
78466
|
+
hasActiveDayShiftLine,
|
|
78467
|
+
hasActiveNightShiftLine,
|
|
78468
|
+
isSingleDayScope,
|
|
78469
|
+
resolvedOperationalToday
|
|
78470
|
+
]
|
|
78224
78471
|
);
|
|
78225
78472
|
const handleOpenLineDetails = React141__namespace.default.useCallback((lineId, lineName) => {
|
|
78226
78473
|
trackCoreEvent("Operations Overview Line Clicked", {
|
|
@@ -78255,7 +78502,7 @@ var PlantHeadView = () => {
|
|
|
78255
78502
|
displayDateRange: headerDateRange,
|
|
78256
78503
|
trendMode,
|
|
78257
78504
|
isLiveScope,
|
|
78258
|
-
liveShiftName:
|
|
78505
|
+
liveShiftName: isLiveScope && trendMode !== "all" ? trendMode : null,
|
|
78259
78506
|
lineOptions,
|
|
78260
78507
|
supervisorOptions,
|
|
78261
78508
|
selectedSupervisorId,
|
|
@@ -79086,6 +79333,7 @@ exports.formatRelativeTime = formatRelativeTime;
|
|
|
79086
79333
|
exports.formatTimeInZone = formatTimeInZone;
|
|
79087
79334
|
exports.fromUrlFriendlyName = fromUrlFriendlyName;
|
|
79088
79335
|
exports.getActionDisplayName = getActionDisplayName;
|
|
79336
|
+
exports.getActiveShift = getActiveShift;
|
|
79089
79337
|
exports.getAllLineDisplayNames = getAllLineDisplayNames;
|
|
79090
79338
|
exports.getAllThreadMessages = getAllThreadMessages;
|
|
79091
79339
|
exports.getAllWorkspaceDisplayNamesAsync = getAllWorkspaceDisplayNamesAsync;
|