@optifye/dashboard-core 6.12.0 → 6.12.2
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.css +53 -3
- package/dist/index.d.mts +18 -6
- package/dist/index.d.ts +18 -6
- package/dist/index.js +1298 -848
- package/dist/index.mjs +1298 -848
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4234,6 +4234,22 @@ var isDummyRow = (row, dummySet) => {
|
|
|
4234
4234
|
if (typeof skuId !== "string" || skuId.length === 0) return false;
|
|
4235
4235
|
return dummySet.has(skuId);
|
|
4236
4236
|
};
|
|
4237
|
+
var isActiveOutputRow = (row) => {
|
|
4238
|
+
const currentOutput = coerceOptionalNumber(row.current_output) ?? 0;
|
|
4239
|
+
const idealOutput = coerceOptionalNumber(row.ideal_output) ?? 0;
|
|
4240
|
+
return currentOutput > 0 || idealOutput > 0;
|
|
4241
|
+
};
|
|
4242
|
+
var roundHalfUpInt = (value) => Math.floor(value + 0.5);
|
|
4243
|
+
var dedupeStringsPreserveOrder = (values) => {
|
|
4244
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4245
|
+
const ordered = [];
|
|
4246
|
+
for (const value of values) {
|
|
4247
|
+
if (!value || seen.has(value)) continue;
|
|
4248
|
+
seen.add(value);
|
|
4249
|
+
ordered.push(value);
|
|
4250
|
+
}
|
|
4251
|
+
return ordered;
|
|
4252
|
+
};
|
|
4237
4253
|
var emptyAggregate = () => ({
|
|
4238
4254
|
current_output: 0,
|
|
4239
4255
|
ideal_output: 0,
|
|
@@ -4352,10 +4368,7 @@ var combineLineMetricsRows = (rows, dummySkuId) => {
|
|
|
4352
4368
|
);
|
|
4353
4369
|
const dummyLineMetricsRow = rows.find((row) => isDummyRow(row, dummySet));
|
|
4354
4370
|
const lineThresholdValue = dummyLineMetricsRow ? safeFloat(dummyLineMetricsRow.line_threshold) : safeFloat(rowsForAggregation[0]?.line_threshold ?? 0);
|
|
4355
|
-
const
|
|
4356
|
-
(acc, row) => acc + safeInt(row.underperforming_workspaces),
|
|
4357
|
-
0
|
|
4358
|
-
);
|
|
4371
|
+
const activeRealRows = rowsForAggregation.filter(isActiveOutputRow);
|
|
4359
4372
|
const weighted = (field) => {
|
|
4360
4373
|
const pairs = [];
|
|
4361
4374
|
rowsForAggregation.forEach((row, idx) => {
|
|
@@ -4377,6 +4390,12 @@ var combineLineMetricsRows = (rows, dummySkuId) => {
|
|
|
4377
4390
|
const avgEfficiency = weighted("avg_efficiency");
|
|
4378
4391
|
const avgCycleTime = weighted("avg_cycle_time");
|
|
4379
4392
|
const thresholdPph = weighted("threshold_pph");
|
|
4393
|
+
const underperformingWorkspaces = activeRealRows.length > 0 ? roundHalfUpInt(
|
|
4394
|
+
activeRealRows.reduce(
|
|
4395
|
+
(acc, row) => acc + safeInt(row.underperforming_workspaces),
|
|
4396
|
+
0
|
|
4397
|
+
) / activeRealRows.length
|
|
4398
|
+
) : 0;
|
|
4380
4399
|
const merged = mergeHourlyFields(rows);
|
|
4381
4400
|
const outputArrays = [];
|
|
4382
4401
|
for (const row of rowsForAggregation) {
|
|
@@ -4399,7 +4418,7 @@ var combineLineMetricsRows = (rows, dummySkuId) => {
|
|
|
4399
4418
|
}
|
|
4400
4419
|
const underperformingNames = [];
|
|
4401
4420
|
const underperformingUuids = [];
|
|
4402
|
-
for (const row of
|
|
4421
|
+
for (const row of activeRealRows) {
|
|
4403
4422
|
const names = row.underperforming_workspace_names;
|
|
4404
4423
|
if (Array.isArray(names)) {
|
|
4405
4424
|
for (const n of names) {
|
|
@@ -4413,6 +4432,8 @@ var combineLineMetricsRows = (rows, dummySkuId) => {
|
|
|
4413
4432
|
}
|
|
4414
4433
|
}
|
|
4415
4434
|
}
|
|
4435
|
+
const dedupedUnderperformingNames = dedupeStringsPreserveOrder(underperformingNames);
|
|
4436
|
+
const dedupedUnderperformingUuids = dedupeStringsPreserveOrder(underperformingUuids);
|
|
4416
4437
|
let totalWorkspacesValue = null;
|
|
4417
4438
|
const primaryTotal = coerceOptionalNumber(primary.total_workspaces);
|
|
4418
4439
|
if (primaryTotal !== null) {
|
|
@@ -4434,9 +4455,9 @@ var combineLineMetricsRows = (rows, dummySkuId) => {
|
|
|
4434
4455
|
line_threshold: lineThresholdValue,
|
|
4435
4456
|
threshold_pph: thresholdPph,
|
|
4436
4457
|
total_workspaces: safeInt(totalWorkspacesValue ?? 0),
|
|
4437
|
-
underperforming_workspaces:
|
|
4438
|
-
underperforming_workspace_names:
|
|
4439
|
-
underperforming_workspace_uuids:
|
|
4458
|
+
underperforming_workspaces: underperformingWorkspaces,
|
|
4459
|
+
underperforming_workspace_names: dedupedUnderperformingNames,
|
|
4460
|
+
underperforming_workspace_uuids: dedupedUnderperformingUuids,
|
|
4440
4461
|
output_array: outputArray,
|
|
4441
4462
|
output_hourly: merged.output_hourly,
|
|
4442
4463
|
idle_time_hourly: merged.idle_time_hourly,
|
|
@@ -5561,11 +5582,11 @@ var getDaysDifferenceInZone = (compareDate, timezone) => {
|
|
|
5561
5582
|
todayInZone.setHours(0, 0, 0, 0);
|
|
5562
5583
|
compareDateInZone.setHours(0, 0, 0, 0);
|
|
5563
5584
|
const diffTime = todayInZone.getTime() - compareDateInZone.getTime();
|
|
5564
|
-
const
|
|
5565
|
-
if (
|
|
5566
|
-
if (
|
|
5567
|
-
if (
|
|
5568
|
-
return `${
|
|
5585
|
+
const diffDays2 = Math.ceil(diffTime / (1e3 * 60 * 60 * 24));
|
|
5586
|
+
if (diffDays2 < 0) return "In the future";
|
|
5587
|
+
if (diffDays2 === 0) return "Today";
|
|
5588
|
+
if (diffDays2 === 1) return "Yesterday";
|
|
5589
|
+
return `${diffDays2} days ago`;
|
|
5569
5590
|
};
|
|
5570
5591
|
var getDashboardHeaderTimeInZone = (date = /* @__PURE__ */ new Date(), timezone, timeOptions, locale = DEFAULT_LOCALE) => {
|
|
5571
5592
|
const defaultOptions = {
|
|
@@ -13012,6 +13033,8 @@ var toWorkspaceDetailedMetrics = ({
|
|
|
13012
13033
|
const targetOutput = coerceNumber(data.target_output ?? data.total_day_output, 0);
|
|
13013
13034
|
const idealOutput = coerceNumber(data.ideal_output ?? data.ideal_output_until_now, 0);
|
|
13014
13035
|
const outputDifference = totalActions - idealOutput;
|
|
13036
|
+
const hasHourlyTargetOutputField = Object.prototype.hasOwnProperty.call(data, "hourly_target_output");
|
|
13037
|
+
const hourlyTargetOutput = Array.isArray(data.hourly_target_output) ? data.hourly_target_output.map((value) => value === null || value === void 0 ? null : coerceNumber(value, 0)) : hasHourlyTargetOutputField ? null : void 0;
|
|
13015
13038
|
const hourlyCycleTimes = Array.isArray(data.hourly_cycle_times) ? data.hourly_cycle_times.map((value) => coerceNumber(value, 0)) : [];
|
|
13016
13039
|
const cycleCompletionClipCount = data.cycle_completion_clip_count === null || data.cycle_completion_clip_count === void 0 ? null : coerceNumber(data.cycle_completion_clip_count, 0);
|
|
13017
13040
|
const cycleTimeDataStatus = data.cycle_time_data_status === "missing_clips" ? "missing_clips" : data.cycle_time_data_status === "available" ? "available" : null;
|
|
@@ -13063,6 +13086,7 @@ var toWorkspaceDetailedMetrics = ({
|
|
|
13063
13086
|
avg_efficiency: coerceNumber(data.efficiency ?? data.avg_efficiency, 0),
|
|
13064
13087
|
total_actions: totalActions,
|
|
13065
13088
|
hourly_action_counts: hourlyActionCounts,
|
|
13089
|
+
hourly_target_output: hourlyTargetOutput,
|
|
13066
13090
|
hourly_cycle_times: hourlyCycleTimes,
|
|
13067
13091
|
cycle_completion_clip_count: cycleCompletionClipCount,
|
|
13068
13092
|
cycle_time_data_status: cycleTimeDataStatus,
|
|
@@ -19679,7 +19703,7 @@ function formatRelativeTime(timestamp) {
|
|
|
19679
19703
|
const diffSeconds = Math.floor(diffMs / 1e3);
|
|
19680
19704
|
const diffMinutes = Math.floor(diffSeconds / 60);
|
|
19681
19705
|
const diffHours = Math.floor(diffMinutes / 60);
|
|
19682
|
-
const
|
|
19706
|
+
const diffDays2 = Math.floor(diffHours / 24);
|
|
19683
19707
|
if (diffSeconds < 60) {
|
|
19684
19708
|
return "Less than a minute ago";
|
|
19685
19709
|
}
|
|
@@ -19691,8 +19715,8 @@ function formatRelativeTime(timestamp) {
|
|
|
19691
19715
|
const hourLabel = diffHours === 1 ? "hour" : "hours";
|
|
19692
19716
|
return `${diffHours} ${hourLabel} ago`;
|
|
19693
19717
|
}
|
|
19694
|
-
const dayLabel =
|
|
19695
|
-
return `${
|
|
19718
|
+
const dayLabel = diffDays2 === 1 ? "day" : "days";
|
|
19719
|
+
return `${diffDays2} ${dayLabel} ago`;
|
|
19696
19720
|
} catch (error) {
|
|
19697
19721
|
console.error("[formatRelativeTime] Error formatting timestamp:", error);
|
|
19698
19722
|
return "Unknown";
|
|
@@ -35192,6 +35216,478 @@ var Button = React144__namespace.forwardRef(
|
|
|
35192
35216
|
);
|
|
35193
35217
|
Button.displayName = "Button";
|
|
35194
35218
|
|
|
35219
|
+
// src/lib/utils/hourlyTargets.ts
|
|
35220
|
+
var stripSeconds2 = (timeStr) => timeStr ? timeStr.slice(0, 5) : timeStr;
|
|
35221
|
+
var MINUTES_PER_DAY = 24 * 60;
|
|
35222
|
+
var parseTimeToMinutes2 = (timeString) => {
|
|
35223
|
+
const normalized = stripSeconds2(timeString || "");
|
|
35224
|
+
if (!normalized || !/^[0-2]\d:[0-5]\d$/.test(normalized)) return Number.NaN;
|
|
35225
|
+
const [hours, minutes] = normalized.split(":").map(Number);
|
|
35226
|
+
return hours * 60 + minutes;
|
|
35227
|
+
};
|
|
35228
|
+
var normalizeBreaksOnShiftTimeline = (shiftStart, breaks) => {
|
|
35229
|
+
const shiftStartMinutes = parseTimeToMinutes2(shiftStart);
|
|
35230
|
+
if (!Number.isFinite(shiftStartMinutes)) return [];
|
|
35231
|
+
const normalizedBreaks = [];
|
|
35232
|
+
for (const entry of breaks) {
|
|
35233
|
+
const startRaw = parseTimeToMinutes2(entry.startTime);
|
|
35234
|
+
const endRaw = parseTimeToMinutes2(entry.endTime);
|
|
35235
|
+
if (!Number.isFinite(startRaw) || !Number.isFinite(endRaw)) continue;
|
|
35236
|
+
let start = startRaw;
|
|
35237
|
+
let end = endRaw;
|
|
35238
|
+
if (end <= start) {
|
|
35239
|
+
end += 24 * 60;
|
|
35240
|
+
}
|
|
35241
|
+
if (start < shiftStartMinutes) {
|
|
35242
|
+
start += 24 * 60;
|
|
35243
|
+
end += 24 * 60;
|
|
35244
|
+
}
|
|
35245
|
+
const label = entry.remarks?.trim() || "Break";
|
|
35246
|
+
normalizedBreaks.push({ start, end, label });
|
|
35247
|
+
}
|
|
35248
|
+
return normalizedBreaks;
|
|
35249
|
+
};
|
|
35250
|
+
var roundTarget = (value, mode) => {
|
|
35251
|
+
if (!Number.isFinite(value)) return 0;
|
|
35252
|
+
switch (mode) {
|
|
35253
|
+
case "floor":
|
|
35254
|
+
return Math.floor(value);
|
|
35255
|
+
case "ceil":
|
|
35256
|
+
return Math.ceil(value);
|
|
35257
|
+
case "round":
|
|
35258
|
+
default:
|
|
35259
|
+
return Math.round(value);
|
|
35260
|
+
}
|
|
35261
|
+
};
|
|
35262
|
+
var formatDateKey = (date) => {
|
|
35263
|
+
const year = date.getUTCFullYear();
|
|
35264
|
+
const month = `${date.getUTCMonth() + 1}`.padStart(2, "0");
|
|
35265
|
+
const day = `${date.getUTCDate()}`.padStart(2, "0");
|
|
35266
|
+
return `${year}-${month}-${day}`;
|
|
35267
|
+
};
|
|
35268
|
+
var shiftDateKey = (dateKey, deltaDays) => {
|
|
35269
|
+
const [yearPart, monthPart, dayPart] = dateKey.split("-").map(Number);
|
|
35270
|
+
const year = Number.isFinite(yearPart) ? yearPart : 1970;
|
|
35271
|
+
const month = Number.isFinite(monthPart) ? monthPart : 1;
|
|
35272
|
+
const day = Number.isFinite(dayPart) ? dayPart : 1;
|
|
35273
|
+
const date = new Date(Date.UTC(year, month - 1, day));
|
|
35274
|
+
date.setUTCDate(date.getUTCDate() + deltaDays);
|
|
35275
|
+
return formatDateKey(date);
|
|
35276
|
+
};
|
|
35277
|
+
var getZonedNowSnapshot = (timeZone, now4) => {
|
|
35278
|
+
const formatter = new Intl.DateTimeFormat("en-US", {
|
|
35279
|
+
timeZone,
|
|
35280
|
+
year: "numeric",
|
|
35281
|
+
month: "2-digit",
|
|
35282
|
+
day: "2-digit",
|
|
35283
|
+
hour: "2-digit",
|
|
35284
|
+
minute: "2-digit",
|
|
35285
|
+
hourCycle: "h23"
|
|
35286
|
+
});
|
|
35287
|
+
const parts = formatter.formatToParts(now4).reduce((acc, part) => {
|
|
35288
|
+
if (part.type !== "literal") {
|
|
35289
|
+
acc[part.type] = part.value;
|
|
35290
|
+
}
|
|
35291
|
+
return acc;
|
|
35292
|
+
}, {});
|
|
35293
|
+
const year = Number(parts.year);
|
|
35294
|
+
const month = Number(parts.month);
|
|
35295
|
+
const day = Number(parts.day);
|
|
35296
|
+
const hour = Number(parts.hour);
|
|
35297
|
+
const minute = Number(parts.minute);
|
|
35298
|
+
return {
|
|
35299
|
+
dateKey: `${String(year).padStart(4, "0")}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`,
|
|
35300
|
+
minutesOfDay: (Number.isFinite(hour) ? hour : 0) * 60 + (Number.isFinite(minute) ? minute : 0)
|
|
35301
|
+
};
|
|
35302
|
+
};
|
|
35303
|
+
var getDateKeyInTimeZone = (timeZone, now4 = /* @__PURE__ */ new Date()) => getZonedNowSnapshot(timeZone, now4).dateKey;
|
|
35304
|
+
var buildHourlyIntervals = ({
|
|
35305
|
+
shiftStart,
|
|
35306
|
+
shiftEnd,
|
|
35307
|
+
bucketMinutes = 60,
|
|
35308
|
+
fallbackHours = 11
|
|
35309
|
+
}) => {
|
|
35310
|
+
const startMinutes = parseTimeToMinutes2(shiftStart);
|
|
35311
|
+
if (!Number.isFinite(startMinutes)) return [];
|
|
35312
|
+
const bucket = Number.isFinite(bucketMinutes) && bucketMinutes > 0 ? Math.floor(bucketMinutes) : 60;
|
|
35313
|
+
let totalMinutes;
|
|
35314
|
+
const endRaw = shiftEnd ? parseTimeToMinutes2(shiftEnd) : Number.NaN;
|
|
35315
|
+
if (!Number.isFinite(endRaw)) {
|
|
35316
|
+
totalMinutes = Math.max(0, Math.round(fallbackHours * 60));
|
|
35317
|
+
} else {
|
|
35318
|
+
let endMinutes = endRaw;
|
|
35319
|
+
if (endMinutes <= startMinutes) {
|
|
35320
|
+
endMinutes += 24 * 60;
|
|
35321
|
+
}
|
|
35322
|
+
totalMinutes = endMinutes - startMinutes;
|
|
35323
|
+
}
|
|
35324
|
+
if (!Number.isFinite(totalMinutes) || totalMinutes <= 0) return [];
|
|
35325
|
+
const count = Math.ceil(totalMinutes / bucket);
|
|
35326
|
+
const shiftEndMinutes = startMinutes + totalMinutes;
|
|
35327
|
+
const intervals = [];
|
|
35328
|
+
for (let i = 0; i < count; i += 1) {
|
|
35329
|
+
const start = startMinutes + i * bucket;
|
|
35330
|
+
const end = Math.min(start + bucket, shiftEndMinutes);
|
|
35331
|
+
const minutes = Math.max(0, end - start);
|
|
35332
|
+
if (minutes <= 0) continue;
|
|
35333
|
+
intervals.push({ start, end, minutes });
|
|
35334
|
+
}
|
|
35335
|
+
return intervals;
|
|
35336
|
+
};
|
|
35337
|
+
var computeBreakMinutesByInterval = ({
|
|
35338
|
+
intervals,
|
|
35339
|
+
shiftStart,
|
|
35340
|
+
breaks
|
|
35341
|
+
}) => {
|
|
35342
|
+
if (!intervals.length || !breaks.length) return intervals.map(() => 0);
|
|
35343
|
+
const normalizedBreaks = normalizeBreaksOnShiftTimeline(shiftStart, breaks);
|
|
35344
|
+
return intervals.map((interval) => {
|
|
35345
|
+
if (!normalizedBreaks.length) return 0;
|
|
35346
|
+
let total = 0;
|
|
35347
|
+
for (const brk of normalizedBreaks) {
|
|
35348
|
+
const overlap = Math.max(0, Math.min(interval.end, brk.end) - Math.max(interval.start, brk.start));
|
|
35349
|
+
total += overlap;
|
|
35350
|
+
if (total >= interval.minutes) return interval.minutes;
|
|
35351
|
+
}
|
|
35352
|
+
return Math.min(interval.minutes, total);
|
|
35353
|
+
});
|
|
35354
|
+
};
|
|
35355
|
+
var computeBreakRemarksByInterval = ({
|
|
35356
|
+
intervals,
|
|
35357
|
+
shiftStart,
|
|
35358
|
+
breaks
|
|
35359
|
+
}) => {
|
|
35360
|
+
if (!intervals.length || !breaks.length) return intervals.map(() => "");
|
|
35361
|
+
const normalizedBreaks = normalizeBreaksOnShiftTimeline(shiftStart, breaks);
|
|
35362
|
+
return intervals.map((interval) => {
|
|
35363
|
+
const labels = normalizedBreaks.filter((brk) => Math.max(0, Math.min(interval.end, brk.end) - Math.max(interval.start, brk.start)) > 0).map((brk) => brk.label).filter((label, index, values) => label && values.indexOf(label) === index);
|
|
35364
|
+
return labels.join(", ");
|
|
35365
|
+
});
|
|
35366
|
+
};
|
|
35367
|
+
var computeEffectiveTargets = ({
|
|
35368
|
+
intervals,
|
|
35369
|
+
breakMinutes,
|
|
35370
|
+
pphThreshold,
|
|
35371
|
+
rounding = "round"
|
|
35372
|
+
}) => {
|
|
35373
|
+
return intervals.map((interval, idx) => {
|
|
35374
|
+
const intervalMinutes = Number(interval?.minutes) || 0;
|
|
35375
|
+
const breakMins = Number(breakMinutes?.[idx]) || 0;
|
|
35376
|
+
const plannedWorkMinutes = Math.max(0, intervalMinutes - breakMins);
|
|
35377
|
+
if (!Number.isFinite(pphThreshold) || pphThreshold <= 0) return 0;
|
|
35378
|
+
if (plannedWorkMinutes <= 0) return 0;
|
|
35379
|
+
return roundTarget(pphThreshold * plannedWorkMinutes / 60, rounding);
|
|
35380
|
+
});
|
|
35381
|
+
};
|
|
35382
|
+
var buildHourlyTargetPlan = ({
|
|
35383
|
+
shiftStart,
|
|
35384
|
+
shiftEnd,
|
|
35385
|
+
breaks = [],
|
|
35386
|
+
pphThreshold,
|
|
35387
|
+
bucketMinutes = 60,
|
|
35388
|
+
fallbackHours = 11,
|
|
35389
|
+
rounding = "round"
|
|
35390
|
+
}) => {
|
|
35391
|
+
const intervals = buildHourlyIntervals({
|
|
35392
|
+
shiftStart,
|
|
35393
|
+
shiftEnd,
|
|
35394
|
+
bucketMinutes,
|
|
35395
|
+
fallbackHours
|
|
35396
|
+
});
|
|
35397
|
+
const breakMinutes = computeBreakMinutesByInterval({
|
|
35398
|
+
intervals,
|
|
35399
|
+
shiftStart,
|
|
35400
|
+
breaks
|
|
35401
|
+
});
|
|
35402
|
+
const breakRemarks = computeBreakRemarksByInterval({
|
|
35403
|
+
intervals,
|
|
35404
|
+
shiftStart,
|
|
35405
|
+
breaks
|
|
35406
|
+
});
|
|
35407
|
+
const productiveMinutes = intervals.map((interval, idx) => Math.max(0, (Number(interval?.minutes) || 0) - (Number(breakMinutes[idx]) || 0)));
|
|
35408
|
+
const targets = computeEffectiveTargets({
|
|
35409
|
+
intervals,
|
|
35410
|
+
breakMinutes,
|
|
35411
|
+
pphThreshold,
|
|
35412
|
+
rounding
|
|
35413
|
+
});
|
|
35414
|
+
return {
|
|
35415
|
+
intervals,
|
|
35416
|
+
breakMinutes,
|
|
35417
|
+
breakRemarks,
|
|
35418
|
+
productiveMinutes,
|
|
35419
|
+
targets
|
|
35420
|
+
};
|
|
35421
|
+
};
|
|
35422
|
+
var isHourlyIntervalComplete = ({
|
|
35423
|
+
reportDate,
|
|
35424
|
+
shiftStart,
|
|
35425
|
+
shiftEnd,
|
|
35426
|
+
interval,
|
|
35427
|
+
timeZone = "Asia/Kolkata",
|
|
35428
|
+
now: now4 = /* @__PURE__ */ new Date()
|
|
35429
|
+
}) => {
|
|
35430
|
+
if (!reportDate) return true;
|
|
35431
|
+
const snapshot = getZonedNowSnapshot(timeZone, now4);
|
|
35432
|
+
const shiftStartMinutes = parseTimeToMinutes2(shiftStart);
|
|
35433
|
+
const shiftEndMinutes = shiftEnd ? parseTimeToMinutes2(shiftEnd) : Number.NaN;
|
|
35434
|
+
const wrapsMidnight = Number.isFinite(shiftStartMinutes) && Number.isFinite(shiftEndMinutes) && shiftEndMinutes <= shiftStartMinutes;
|
|
35435
|
+
if (reportDate === snapshot.dateKey) {
|
|
35436
|
+
return interval.end <= snapshot.minutesOfDay;
|
|
35437
|
+
}
|
|
35438
|
+
if (wrapsMidnight && reportDate === shiftDateKey(snapshot.dateKey, -1)) {
|
|
35439
|
+
return interval.end <= snapshot.minutesOfDay + MINUTES_PER_DAY;
|
|
35440
|
+
}
|
|
35441
|
+
return reportDate < snapshot.dateKey;
|
|
35442
|
+
};
|
|
35443
|
+
var isShiftInProgressForReportDate = ({
|
|
35444
|
+
reportDate,
|
|
35445
|
+
shiftStart,
|
|
35446
|
+
shiftEnd,
|
|
35447
|
+
timeZone = "Asia/Kolkata",
|
|
35448
|
+
now: now4 = /* @__PURE__ */ new Date()
|
|
35449
|
+
}) => {
|
|
35450
|
+
if (!reportDate || !shiftStart || !shiftEnd) return false;
|
|
35451
|
+
const shiftStartMinutes = parseTimeToMinutes2(shiftStart);
|
|
35452
|
+
const shiftEndMinutesRaw = parseTimeToMinutes2(shiftEnd);
|
|
35453
|
+
if (!Number.isFinite(shiftStartMinutes) || !Number.isFinite(shiftEndMinutesRaw)) {
|
|
35454
|
+
return false;
|
|
35455
|
+
}
|
|
35456
|
+
let shiftEndMinutes = shiftEndMinutesRaw;
|
|
35457
|
+
const wrapsMidnight = shiftEndMinutes <= shiftStartMinutes;
|
|
35458
|
+
if (wrapsMidnight) {
|
|
35459
|
+
shiftEndMinutes += MINUTES_PER_DAY;
|
|
35460
|
+
}
|
|
35461
|
+
const snapshot = getZonedNowSnapshot(timeZone, now4);
|
|
35462
|
+
let currentMinutes = null;
|
|
35463
|
+
if (reportDate === snapshot.dateKey) {
|
|
35464
|
+
currentMinutes = snapshot.minutesOfDay;
|
|
35465
|
+
} else if (wrapsMidnight && reportDate === shiftDateKey(snapshot.dateKey, -1)) {
|
|
35466
|
+
currentMinutes = snapshot.minutesOfDay + MINUTES_PER_DAY;
|
|
35467
|
+
}
|
|
35468
|
+
if (currentMinutes === null) {
|
|
35469
|
+
return false;
|
|
35470
|
+
}
|
|
35471
|
+
return shiftStartMinutes <= currentMinutes && currentMinutes < shiftEndMinutes;
|
|
35472
|
+
};
|
|
35473
|
+
var padTime = (value) => value.toString().padStart(2, "0");
|
|
35474
|
+
var parseTime = (timeValue) => {
|
|
35475
|
+
if (!timeValue) return null;
|
|
35476
|
+
const [hourPart, minutePart] = timeValue.split(":");
|
|
35477
|
+
const hour = Number.parseInt(hourPart, 10);
|
|
35478
|
+
const minute = Number.parseInt(minutePart ?? "0", 10);
|
|
35479
|
+
if (!Number.isFinite(hour) || !Number.isFinite(minute)) return null;
|
|
35480
|
+
return { hour, minute };
|
|
35481
|
+
};
|
|
35482
|
+
var normalizeIdleTimeHourly = (idleTimeHourly) => {
|
|
35483
|
+
if (!idleTimeHourly || typeof idleTimeHourly !== "object") {
|
|
35484
|
+
return {};
|
|
35485
|
+
}
|
|
35486
|
+
return Object.fromEntries(
|
|
35487
|
+
Object.entries(idleTimeHourly).map(([key, value]) => {
|
|
35488
|
+
if (Array.isArray(value)) return [key, value];
|
|
35489
|
+
if (value && Array.isArray(value.values)) {
|
|
35490
|
+
return [key, value.values];
|
|
35491
|
+
}
|
|
35492
|
+
return [key, []];
|
|
35493
|
+
})
|
|
35494
|
+
);
|
|
35495
|
+
};
|
|
35496
|
+
var interpretIdleValue = (value) => {
|
|
35497
|
+
if (value === 1 || value === "1") return "idle";
|
|
35498
|
+
if (value === 0 || value === "0") return "active";
|
|
35499
|
+
if (value === "x" || value === null || value === void 0) return "unknown";
|
|
35500
|
+
return "unknown";
|
|
35501
|
+
};
|
|
35502
|
+
var getShiftDurationMinutes = (shiftStart, shiftEnd) => {
|
|
35503
|
+
const start = parseTime(shiftStart);
|
|
35504
|
+
const end = parseTime(shiftEnd);
|
|
35505
|
+
if (!start || !end) return null;
|
|
35506
|
+
let duration = end.hour * 60 + end.minute - (start.hour * 60 + start.minute);
|
|
35507
|
+
if (duration <= 0) {
|
|
35508
|
+
duration += 24 * 60;
|
|
35509
|
+
}
|
|
35510
|
+
return duration > 0 ? duration : null;
|
|
35511
|
+
};
|
|
35512
|
+
var getShiftElapsedMinutes = ({
|
|
35513
|
+
shiftStart,
|
|
35514
|
+
shiftEnd,
|
|
35515
|
+
shiftDate,
|
|
35516
|
+
timezone,
|
|
35517
|
+
now: now4 = /* @__PURE__ */ new Date()
|
|
35518
|
+
}) => {
|
|
35519
|
+
if (!shiftDate || !timezone) return null;
|
|
35520
|
+
const startTime = parseTime(shiftStart);
|
|
35521
|
+
const endTime = parseTime(shiftEnd);
|
|
35522
|
+
if (!startTime || !endTime) return null;
|
|
35523
|
+
const shiftStartDate = dateFnsTz.fromZonedTime(
|
|
35524
|
+
`${shiftDate}T${padTime(startTime.hour)}:${padTime(startTime.minute)}:00`,
|
|
35525
|
+
timezone
|
|
35526
|
+
);
|
|
35527
|
+
let shiftEndDate = dateFnsTz.fromZonedTime(
|
|
35528
|
+
`${shiftDate}T${padTime(endTime.hour)}:${padTime(endTime.minute)}:00`,
|
|
35529
|
+
timezone
|
|
35530
|
+
);
|
|
35531
|
+
if (shiftEndDate <= shiftStartDate) {
|
|
35532
|
+
shiftEndDate = dateFns.addDays(shiftEndDate, 1);
|
|
35533
|
+
}
|
|
35534
|
+
const shiftMinutes = Math.max(dateFns.differenceInMinutes(shiftEndDate, shiftStartDate), 0);
|
|
35535
|
+
if (shiftMinutes <= 0) return null;
|
|
35536
|
+
const elapsed = dateFns.differenceInMinutes(now4, shiftStartDate);
|
|
35537
|
+
return Math.min(Math.max(elapsed, 0), shiftMinutes);
|
|
35538
|
+
};
|
|
35539
|
+
var maskFutureHourlySeries = ({
|
|
35540
|
+
data,
|
|
35541
|
+
shiftStart,
|
|
35542
|
+
shiftEnd,
|
|
35543
|
+
shiftDate,
|
|
35544
|
+
timezone,
|
|
35545
|
+
now: now4 = /* @__PURE__ */ new Date()
|
|
35546
|
+
}) => {
|
|
35547
|
+
if (!Array.isArray(data)) {
|
|
35548
|
+
return [];
|
|
35549
|
+
}
|
|
35550
|
+
const normalizedData = data.map((value) => typeof value === "number" && Number.isFinite(value) ? value : null);
|
|
35551
|
+
if (!normalizedData.length) {
|
|
35552
|
+
return normalizedData;
|
|
35553
|
+
}
|
|
35554
|
+
const shiftMinutes = getShiftDurationMinutes(shiftStart, shiftEnd);
|
|
35555
|
+
const elapsedMinutes = getShiftElapsedMinutes({
|
|
35556
|
+
shiftStart,
|
|
35557
|
+
shiftEnd,
|
|
35558
|
+
shiftDate,
|
|
35559
|
+
timezone,
|
|
35560
|
+
now: now4
|
|
35561
|
+
});
|
|
35562
|
+
if (shiftMinutes === null || elapsedMinutes === null || elapsedMinutes >= shiftMinutes) {
|
|
35563
|
+
return normalizedData;
|
|
35564
|
+
}
|
|
35565
|
+
return normalizedData.map((value, index) => {
|
|
35566
|
+
const slotStartMinutes = index * 60;
|
|
35567
|
+
return slotStartMinutes > elapsedMinutes ? null : value;
|
|
35568
|
+
});
|
|
35569
|
+
};
|
|
35570
|
+
var buildUptimeSeries = ({
|
|
35571
|
+
idleTimeHourly,
|
|
35572
|
+
shiftStart,
|
|
35573
|
+
shiftEnd,
|
|
35574
|
+
shiftDate,
|
|
35575
|
+
timezone,
|
|
35576
|
+
elapsedMinutes
|
|
35577
|
+
}) => {
|
|
35578
|
+
const normalizedIdle = normalizeIdleTimeHourly(idleTimeHourly || {});
|
|
35579
|
+
const hasIdleData = Object.keys(normalizedIdle).length > 0;
|
|
35580
|
+
if (!hasIdleData || !shiftDate || !timezone) {
|
|
35581
|
+
return {
|
|
35582
|
+
points: [],
|
|
35583
|
+
activeMinutes: 0,
|
|
35584
|
+
idleMinutes: 0,
|
|
35585
|
+
availableMinutes: 0,
|
|
35586
|
+
shiftMinutes: 0,
|
|
35587
|
+
elapsedMinutes: 0,
|
|
35588
|
+
hasData: false
|
|
35589
|
+
};
|
|
35590
|
+
}
|
|
35591
|
+
const startTime = parseTime(shiftStart);
|
|
35592
|
+
const endTime = parseTime(shiftEnd);
|
|
35593
|
+
if (!startTime || !endTime) {
|
|
35594
|
+
return {
|
|
35595
|
+
points: [],
|
|
35596
|
+
activeMinutes: 0,
|
|
35597
|
+
idleMinutes: 0,
|
|
35598
|
+
availableMinutes: 0,
|
|
35599
|
+
shiftMinutes: 0,
|
|
35600
|
+
elapsedMinutes: 0,
|
|
35601
|
+
hasData: false
|
|
35602
|
+
};
|
|
35603
|
+
}
|
|
35604
|
+
const shiftStartDate = dateFnsTz.fromZonedTime(
|
|
35605
|
+
`${shiftDate}T${padTime(startTime.hour)}:${padTime(startTime.minute)}:00`,
|
|
35606
|
+
timezone
|
|
35607
|
+
);
|
|
35608
|
+
let shiftEndDate = dateFnsTz.fromZonedTime(
|
|
35609
|
+
`${shiftDate}T${padTime(endTime.hour)}:${padTime(endTime.minute)}:00`,
|
|
35610
|
+
timezone
|
|
35611
|
+
);
|
|
35612
|
+
if (shiftEndDate <= shiftStartDate) {
|
|
35613
|
+
shiftEndDate = dateFns.addDays(shiftEndDate, 1);
|
|
35614
|
+
}
|
|
35615
|
+
const shiftMinutes = Math.max(dateFns.differenceInMinutes(shiftEndDate, shiftStartDate), 0);
|
|
35616
|
+
if (shiftMinutes <= 0) {
|
|
35617
|
+
return {
|
|
35618
|
+
points: [],
|
|
35619
|
+
activeMinutes: 0,
|
|
35620
|
+
idleMinutes: 0,
|
|
35621
|
+
availableMinutes: 0,
|
|
35622
|
+
shiftMinutes: 0,
|
|
35623
|
+
elapsedMinutes: 0,
|
|
35624
|
+
hasData: false
|
|
35625
|
+
};
|
|
35626
|
+
}
|
|
35627
|
+
const elapsedMinutesClamped = Number.isFinite(elapsedMinutes) ? Math.min(Math.max(Math.floor(elapsedMinutes ?? 0), 0), shiftMinutes) : shiftMinutes;
|
|
35628
|
+
const points = [];
|
|
35629
|
+
let activeMinutes = 0;
|
|
35630
|
+
let idleMinutes = 0;
|
|
35631
|
+
for (let minuteIndex = 0; minuteIndex < shiftMinutes; minuteIndex += 1) {
|
|
35632
|
+
const minuteDate = dateFns.addMinutes(shiftStartDate, minuteIndex);
|
|
35633
|
+
const timeLabel = dateFnsTz.formatInTimeZone(minuteDate, timezone, "h:mm a");
|
|
35634
|
+
if (minuteIndex >= elapsedMinutesClamped) {
|
|
35635
|
+
points.push({
|
|
35636
|
+
minuteIndex,
|
|
35637
|
+
timeLabel,
|
|
35638
|
+
uptime: null,
|
|
35639
|
+
status: "unknown"
|
|
35640
|
+
});
|
|
35641
|
+
continue;
|
|
35642
|
+
}
|
|
35643
|
+
const hourKey = dateFnsTz.formatInTimeZone(minuteDate, timezone, "H");
|
|
35644
|
+
const minuteKey = Number.parseInt(dateFnsTz.formatInTimeZone(minuteDate, timezone, "m"), 10);
|
|
35645
|
+
const hourBucket = normalizedIdle[hourKey] || [];
|
|
35646
|
+
const value = Array.isArray(hourBucket) ? hourBucket[minuteKey] : void 0;
|
|
35647
|
+
const status = interpretIdleValue(value);
|
|
35648
|
+
if (status === "active") activeMinutes += 1;
|
|
35649
|
+
if (status === "idle") idleMinutes += 1;
|
|
35650
|
+
points.push({
|
|
35651
|
+
minuteIndex,
|
|
35652
|
+
timeLabel,
|
|
35653
|
+
uptime: status === "active" ? 1 : status === "idle" ? 0 : null,
|
|
35654
|
+
status
|
|
35655
|
+
});
|
|
35656
|
+
}
|
|
35657
|
+
return {
|
|
35658
|
+
points,
|
|
35659
|
+
activeMinutes,
|
|
35660
|
+
idleMinutes,
|
|
35661
|
+
availableMinutes: activeMinutes + idleMinutes,
|
|
35662
|
+
shiftMinutes,
|
|
35663
|
+
elapsedMinutes: elapsedMinutesClamped,
|
|
35664
|
+
hasData: activeMinutes + idleMinutes > 0
|
|
35665
|
+
};
|
|
35666
|
+
};
|
|
35667
|
+
var getUptimeUtilizationPercent = (shift) => {
|
|
35668
|
+
const efficiency = shift.efficiency;
|
|
35669
|
+
if (Number.isFinite(efficiency)) {
|
|
35670
|
+
return Math.round(Math.max(0, Math.min(100, Number(efficiency))));
|
|
35671
|
+
}
|
|
35672
|
+
const idleSeconds = Number.isFinite(shift.idleTime) ? Number(shift.idleTime) : 0;
|
|
35673
|
+
const activeSeconds = Number.isFinite(shift.activeTimeSeconds) ? Number(shift.activeTimeSeconds) : null;
|
|
35674
|
+
let availableSeconds = Number.isFinite(shift.availableTimeSeconds) ? Number(shift.availableTimeSeconds) : null;
|
|
35675
|
+
if (availableSeconds === null) {
|
|
35676
|
+
if ((activeSeconds ?? 0) > 0 || idleSeconds > 0) {
|
|
35677
|
+
availableSeconds = (activeSeconds ?? 0) + idleSeconds;
|
|
35678
|
+
} else {
|
|
35679
|
+
return 0;
|
|
35680
|
+
}
|
|
35681
|
+
}
|
|
35682
|
+
if (availableSeconds <= 0) return 0;
|
|
35683
|
+
const clampedIdleSeconds = Math.min(Math.max(idleSeconds, 0), availableSeconds);
|
|
35684
|
+
const productiveSeconds = Math.max(
|
|
35685
|
+
activeSeconds ?? availableSeconds - clampedIdleSeconds,
|
|
35686
|
+
0
|
|
35687
|
+
);
|
|
35688
|
+
return Math.round(productiveSeconds / availableSeconds * 100);
|
|
35689
|
+
};
|
|
35690
|
+
|
|
35195
35691
|
// src/components/charts/skuDividerUtils.ts
|
|
35196
35692
|
var HOURLY_TIME_RE = /^(\d{1,2}):(\d{2})/;
|
|
35197
35693
|
var parseTimeOfDay = (timeValue) => {
|
|
@@ -35203,37 +35699,163 @@ var parseTimeOfDay = (timeValue) => {
|
|
|
35203
35699
|
if (hour < 0 || hour > 23 || minute < 0 || minute > 59) return null;
|
|
35204
35700
|
return { hour, minute };
|
|
35205
35701
|
};
|
|
35206
|
-
var
|
|
35207
|
-
|
|
35702
|
+
var parseDateKey = (value) => {
|
|
35703
|
+
const [yearPart, monthPart, dayPart] = value.split("-").map(Number);
|
|
35704
|
+
if (!Number.isFinite(yearPart) || !Number.isFinite(monthPart) || !Number.isFinite(dayPart)) {
|
|
35705
|
+
return null;
|
|
35706
|
+
}
|
|
35707
|
+
return Date.UTC(yearPart, monthPart - 1, dayPart);
|
|
35708
|
+
};
|
|
35709
|
+
var diffDays = (left, right) => {
|
|
35710
|
+
const leftUtc = parseDateKey(left);
|
|
35711
|
+
const rightUtc = parseDateKey(right);
|
|
35712
|
+
if (leftUtc === null || rightUtc === null) return null;
|
|
35713
|
+
return Math.round((leftUtc - rightUtc) / (24 * 60 * 60 * 1e3));
|
|
35714
|
+
};
|
|
35715
|
+
var getSegmentMinutes = (isoString, timeZone, reportDate) => {
|
|
35716
|
+
const date = new Date(isoString);
|
|
35717
|
+
if (Number.isNaN(date.getTime())) return Number.NaN;
|
|
35718
|
+
const formatter = new Intl.DateTimeFormat("en-US", {
|
|
35719
|
+
timeZone,
|
|
35720
|
+
year: "numeric",
|
|
35721
|
+
month: "2-digit",
|
|
35722
|
+
day: "2-digit",
|
|
35723
|
+
hour: "2-digit",
|
|
35724
|
+
minute: "2-digit",
|
|
35725
|
+
hourCycle: "h23"
|
|
35726
|
+
});
|
|
35727
|
+
const parts = formatter.formatToParts(date).reduce((acc, part) => {
|
|
35728
|
+
if (part.type !== "literal") {
|
|
35729
|
+
acc[part.type] = part.value;
|
|
35730
|
+
}
|
|
35731
|
+
return acc;
|
|
35732
|
+
}, {});
|
|
35733
|
+
const dateKey = `${parts.year}-${parts.month}-${parts.day}`;
|
|
35734
|
+
let minutes = Number(parts.hour) * 60 + Number(parts.minute);
|
|
35735
|
+
const dayDelta = diffDays(dateKey, reportDate);
|
|
35736
|
+
if (dayDelta === null) return Number.NaN;
|
|
35737
|
+
minutes += dayDelta * 24 * 60;
|
|
35738
|
+
return minutes;
|
|
35739
|
+
};
|
|
35740
|
+
var computeSegmentOffsetUtc = (segment, shift) => {
|
|
35208
35741
|
const parsedMs = Date.parse(segment.start_time);
|
|
35209
35742
|
if (Number.isNaN(parsedMs)) return null;
|
|
35210
35743
|
const date = new Date(parsedMs);
|
|
35211
35744
|
const segHour = date.getUTCHours();
|
|
35212
35745
|
const segMinute = date.getUTCMinutes();
|
|
35213
35746
|
const segMinutes = segHour * 60 + segMinute;
|
|
35214
|
-
|
|
35747
|
+
const shiftStartMinutes = shift.startHour * 60 + shift.startMinute;
|
|
35215
35748
|
let deltaMinutes = segMinutes - shiftStartMinutes;
|
|
35216
35749
|
if (deltaMinutes < 0) deltaMinutes += 24 * 60;
|
|
35217
35750
|
const offsetHours = deltaMinutes / 60;
|
|
35218
35751
|
if (offsetHours < 0 || offsetHours > shift.slotCount) return null;
|
|
35219
35752
|
return offsetHours;
|
|
35220
35753
|
};
|
|
35754
|
+
var computeSegmentOffset = (segment, shift) => {
|
|
35755
|
+
if (!segment?.start_time) return null;
|
|
35756
|
+
if (shift.shiftDate && shift.timezone) {
|
|
35757
|
+
const segmentMinutes = getSegmentMinutes(
|
|
35758
|
+
segment.start_time,
|
|
35759
|
+
shift.timezone,
|
|
35760
|
+
shift.shiftDate
|
|
35761
|
+
);
|
|
35762
|
+
if (!Number.isFinite(segmentMinutes)) return null;
|
|
35763
|
+
const shiftStartMinutes = shift.startHour * 60 + shift.startMinute;
|
|
35764
|
+
const deltaMinutes = segmentMinutes - shiftStartMinutes;
|
|
35765
|
+
const offsetHours = deltaMinutes / 60;
|
|
35766
|
+
if (offsetHours < 0 || offsetHours > shift.slotCount) return null;
|
|
35767
|
+
return offsetHours;
|
|
35768
|
+
}
|
|
35769
|
+
return computeSegmentOffsetUtc(segment, shift);
|
|
35770
|
+
};
|
|
35221
35771
|
var resolveShiftWindow2 = (params) => {
|
|
35222
35772
|
const startTime = parseTimeOfDay(params.shiftStart);
|
|
35223
35773
|
if (!startTime) return null;
|
|
35224
35774
|
return {
|
|
35225
35775
|
startHour: startTime.hour,
|
|
35226
35776
|
startMinute: startTime.minute,
|
|
35227
|
-
slotCount: params.slotCount
|
|
35777
|
+
slotCount: params.slotCount,
|
|
35778
|
+
...params.shiftDate ? { shiftDate: params.shiftDate } : {},
|
|
35779
|
+
...params.timezone ? { timezone: params.timezone } : {}
|
|
35228
35780
|
};
|
|
35229
35781
|
};
|
|
35782
|
+
var resolveTimelineEndOffset = ({
|
|
35783
|
+
shiftStart,
|
|
35784
|
+
shiftEnd,
|
|
35785
|
+
slotCount,
|
|
35786
|
+
shiftDate,
|
|
35787
|
+
timezone,
|
|
35788
|
+
now: now4
|
|
35789
|
+
}) => {
|
|
35790
|
+
const normalizedSlotCount = Number.isFinite(slotCount) && slotCount > 0 ? slotCount : 0;
|
|
35791
|
+
if (!shiftDate || !timezone) {
|
|
35792
|
+
return normalizedSlotCount;
|
|
35793
|
+
}
|
|
35794
|
+
const shiftMinutes = getShiftDurationMinutes(shiftStart, shiftEnd);
|
|
35795
|
+
const elapsedMinutes = getShiftElapsedMinutes({
|
|
35796
|
+
shiftStart,
|
|
35797
|
+
shiftEnd,
|
|
35798
|
+
shiftDate,
|
|
35799
|
+
timezone,
|
|
35800
|
+
now: now4
|
|
35801
|
+
});
|
|
35802
|
+
if (shiftMinutes === null || elapsedMinutes === null) {
|
|
35803
|
+
return normalizedSlotCount;
|
|
35804
|
+
}
|
|
35805
|
+
if (elapsedMinutes >= shiftMinutes) {
|
|
35806
|
+
return normalizedSlotCount;
|
|
35807
|
+
}
|
|
35808
|
+
return Math.max(0, Math.min(elapsedMinutes / 60, normalizedSlotCount));
|
|
35809
|
+
};
|
|
35810
|
+
var formatSkuRailLabel = (label, segmentWidth, options = {}) => {
|
|
35811
|
+
const horizontalPadding = options.horizontalPadding ?? 8;
|
|
35812
|
+
const minVisibleChars = options.minVisibleChars ?? 4;
|
|
35813
|
+
const averageCharacterWidth = options.averageCharacterWidth ?? 6.6;
|
|
35814
|
+
const compactAverageCharacterWidth = options.compactAverageCharacterWidth ?? 5;
|
|
35815
|
+
const cssTruncation = options.cssTruncation ?? false;
|
|
35816
|
+
const fullLabelThreshold = 6;
|
|
35817
|
+
if (!label) return null;
|
|
35818
|
+
if (!Number.isFinite(segmentWidth) || segmentWidth <= horizontalPadding * 2) return null;
|
|
35819
|
+
const usableWidth = Math.max(segmentWidth - horizontalPadding * 2, 0);
|
|
35820
|
+
const maxChars = Math.floor(usableWidth / averageCharacterWidth);
|
|
35821
|
+
if (maxChars >= Math.max(minVisibleChars, fullLabelThreshold)) {
|
|
35822
|
+
if (label.length <= maxChars || cssTruncation) return label;
|
|
35823
|
+
if (maxChars <= 1) return null;
|
|
35824
|
+
return `${label.slice(0, Math.max(maxChars - 1, 1)).trimEnd()}\u2026`;
|
|
35825
|
+
}
|
|
35826
|
+
const compactLabel = buildCompactSkuRailLabel(label, Math.max(maxChars, 1));
|
|
35827
|
+
const compactMaxChars = Math.floor(usableWidth / compactAverageCharacterWidth);
|
|
35828
|
+
if (compactMaxChars < 1) return null;
|
|
35829
|
+
if (compactLabel.length <= compactMaxChars || cssTruncation) return compactLabel;
|
|
35830
|
+
if (compactMaxChars <= 2) {
|
|
35831
|
+
return compactLabel.slice(0, compactMaxChars);
|
|
35832
|
+
}
|
|
35833
|
+
return `${compactLabel.slice(0, Math.max(compactMaxChars - 1, 1)).trimEnd()}\u2026`;
|
|
35834
|
+
};
|
|
35835
|
+
var buildCompactSkuRailLabel = (label, maxChars) => {
|
|
35836
|
+
const words = label.trim().split(/\s+/).filter(Boolean);
|
|
35837
|
+
if (words.length === 0) return label;
|
|
35838
|
+
const [firstWord] = words;
|
|
35839
|
+
const acronym = words.map((word) => word[0]).join("").toUpperCase();
|
|
35840
|
+
if (maxChars <= 3 && acronym) {
|
|
35841
|
+
return acronym;
|
|
35842
|
+
}
|
|
35843
|
+
if (firstWord.length >= 4) {
|
|
35844
|
+
return firstWord;
|
|
35845
|
+
}
|
|
35846
|
+
return acronym || firstWord;
|
|
35847
|
+
};
|
|
35230
35848
|
var HourlyOutputChartComponent = ({
|
|
35231
35849
|
data,
|
|
35232
35850
|
pphThreshold,
|
|
35851
|
+
hourlyTargetOutput,
|
|
35233
35852
|
shiftStart,
|
|
35234
35853
|
shiftEnd,
|
|
35854
|
+
shiftBreaks = [],
|
|
35235
35855
|
showIdleTime = false,
|
|
35236
35856
|
idleTimeHourly,
|
|
35857
|
+
shiftDate,
|
|
35858
|
+
timezone,
|
|
35237
35859
|
skuSegments,
|
|
35238
35860
|
activeSkuId,
|
|
35239
35861
|
className = ""
|
|
@@ -35241,6 +35863,7 @@ var HourlyOutputChartComponent = ({
|
|
|
35241
35863
|
const containerRef = React144__namespace.default.useRef(null);
|
|
35242
35864
|
const [containerReady, setContainerReady] = React144__namespace.default.useState(false);
|
|
35243
35865
|
const [containerWidth, setContainerWidth] = React144__namespace.default.useState(0);
|
|
35866
|
+
const [hoveredSkuRailLabel, setHoveredSkuRailLabel] = React144__namespace.default.useState(null);
|
|
35244
35867
|
const idleSlots = React144__namespace.default.useMemo(
|
|
35245
35868
|
() => buildHourlyIdleSlots({
|
|
35246
35869
|
idleTimeHourly,
|
|
@@ -35386,14 +36009,54 @@ var HourlyOutputChartComponent = ({
|
|
|
35386
36009
|
}, [containerWidth]);
|
|
35387
36010
|
const shiftWindow = React144__namespace.default.useMemo(() => resolveShiftWindow2({
|
|
35388
36011
|
shiftStart,
|
|
35389
|
-
slotCount: SHIFT_DURATION
|
|
35390
|
-
|
|
36012
|
+
slotCount: SHIFT_DURATION,
|
|
36013
|
+
shiftDate,
|
|
36014
|
+
timezone
|
|
36015
|
+
}), [shiftStart, shiftEnd, SHIFT_DURATION, shiftDate, timezone]);
|
|
36016
|
+
const fallbackTimelineEndOffset = React144__namespace.default.useMemo(
|
|
36017
|
+
() => resolveTimelineEndOffset({
|
|
36018
|
+
shiftStart,
|
|
36019
|
+
shiftEnd,
|
|
36020
|
+
slotCount: SHIFT_DURATION,
|
|
36021
|
+
shiftDate,
|
|
36022
|
+
timezone
|
|
36023
|
+
}),
|
|
36024
|
+
[shiftStart, shiftEnd, SHIFT_DURATION, shiftDate, timezone]
|
|
36025
|
+
);
|
|
36026
|
+
const observedTimelineEndOffset = React144__namespace.default.useMemo(() => {
|
|
36027
|
+
let lastObservedMinute = -1;
|
|
36028
|
+
idleSlots.forEach((slot) => {
|
|
36029
|
+
slot.idleArray.forEach((value, minuteIndex) => {
|
|
36030
|
+
if (value !== "x" && value !== null && value !== void 0) {
|
|
36031
|
+
lastObservedMinute = Math.max(
|
|
36032
|
+
lastObservedMinute,
|
|
36033
|
+
slot.hourIndex * 60 + minuteIndex
|
|
36034
|
+
);
|
|
36035
|
+
}
|
|
36036
|
+
});
|
|
36037
|
+
});
|
|
36038
|
+
if (lastObservedMinute < 0) return null;
|
|
36039
|
+
return Math.min((lastObservedMinute + 1) / 60, SHIFT_DURATION);
|
|
36040
|
+
}, [idleSlots, SHIFT_DURATION]);
|
|
36041
|
+
const timelineEndOffset = observedTimelineEndOffset ?? fallbackTimelineEndOffset;
|
|
36042
|
+
const targetLineEndOffset = React144__namespace.default.useMemo(() => {
|
|
36043
|
+
if (timelineEndOffset >= SHIFT_DURATION) {
|
|
36044
|
+
return SHIFT_DURATION;
|
|
36045
|
+
}
|
|
36046
|
+
if (Number.isInteger(timelineEndOffset)) {
|
|
36047
|
+
return timelineEndOffset;
|
|
36048
|
+
}
|
|
36049
|
+
return Math.min(Math.floor(timelineEndOffset) + 1, SHIFT_DURATION);
|
|
36050
|
+
}, [timelineEndOffset, SHIFT_DURATION]);
|
|
35391
36051
|
const skuTimelineSegments = React144__namespace.default.useMemo(() => {
|
|
35392
|
-
if (!skuSegments || skuSegments.length === 0 || !shiftWindow)
|
|
35393
|
-
|
|
35394
|
-
|
|
35395
|
-
|
|
35396
|
-
|
|
36052
|
+
if (!skuSegments || skuSegments.length === 0 || !shiftWindow || timelineEndOffset <= 0) {
|
|
36053
|
+
return [];
|
|
36054
|
+
}
|
|
36055
|
+
const withOffsets = skuSegments.flatMap((segment) => {
|
|
36056
|
+
const offset = computeSegmentOffset(segment, shiftWindow);
|
|
36057
|
+
if (offset === null) return [];
|
|
36058
|
+
return [{ segment, offset }];
|
|
36059
|
+
}).sort((a, b) => a.offset - b.offset);
|
|
35397
36060
|
if (withOffsets.length === 0) return [];
|
|
35398
36061
|
const deduped = [];
|
|
35399
36062
|
const DUPLICATE_OFFSET_THRESHOLD = 1 / 60;
|
|
@@ -35407,9 +36070,9 @@ var HourlyOutputChartComponent = ({
|
|
|
35407
36070
|
deduped[0] = { ...deduped[0], offset: 0 };
|
|
35408
36071
|
}
|
|
35409
36072
|
return deduped.map((entry, index) => {
|
|
35410
|
-
const nextOffset = index < deduped.length - 1 ? deduped[index + 1].offset :
|
|
35411
|
-
const start = Math.max(0, Math.min(entry.offset,
|
|
35412
|
-
const end = Math.max(start, Math.min(nextOffset,
|
|
36073
|
+
const nextOffset = index < deduped.length - 1 ? deduped[index + 1].offset : timelineEndOffset;
|
|
36074
|
+
const start = Math.max(0, Math.min(entry.offset, timelineEndOffset));
|
|
36075
|
+
const end = Math.max(start, Math.min(nextOffset, timelineEndOffset));
|
|
35413
36076
|
return {
|
|
35414
36077
|
skuId: entry.segment.sku_id,
|
|
35415
36078
|
label: entry.segment.sku_code,
|
|
@@ -35418,15 +36081,94 @@ var HourlyOutputChartComponent = ({
|
|
|
35418
36081
|
pphThreshold: entry.segment.pph_threshold ?? pphThreshold
|
|
35419
36082
|
};
|
|
35420
36083
|
}).filter((segment) => segment.end > segment.start);
|
|
35421
|
-
}, [skuSegments, shiftWindow,
|
|
36084
|
+
}, [skuSegments, shiftWindow, timelineEndOffset, pphThreshold]);
|
|
36085
|
+
const targetTimelineSegments = React144__namespace.default.useMemo(() => {
|
|
36086
|
+
if (skuTimelineSegments.length === 0) return [];
|
|
36087
|
+
return skuTimelineSegments.map((segment, index) => ({
|
|
36088
|
+
...segment,
|
|
36089
|
+
end: index === skuTimelineSegments.length - 1 ? Math.max(segment.start, targetLineEndOffset) : segment.end
|
|
36090
|
+
})).filter((segment) => segment.end > segment.start);
|
|
36091
|
+
}, [skuTimelineSegments, targetLineEndOffset]);
|
|
36092
|
+
const hasExplicitHourlyTargetOutputProp = React144__namespace.default.useMemo(
|
|
36093
|
+
() => hourlyTargetOutput !== void 0,
|
|
36094
|
+
[hourlyTargetOutput]
|
|
36095
|
+
);
|
|
36096
|
+
const fallbackHourlyTargetOutput = React144__namespace.default.useMemo(() => {
|
|
36097
|
+
if (hasExplicitHourlyTargetOutputProp) return void 0;
|
|
36098
|
+
if (skuTimelineSegments.length > 0) return void 0;
|
|
36099
|
+
const plan = buildHourlyTargetPlan({
|
|
36100
|
+
shiftStart,
|
|
36101
|
+
shiftEnd,
|
|
36102
|
+
breaks: shiftBreaks,
|
|
36103
|
+
pphThreshold,
|
|
36104
|
+
rounding: "floor"
|
|
36105
|
+
});
|
|
36106
|
+
if (!plan.targets.length) return void 0;
|
|
36107
|
+
return plan.targets.map((value) => Number.isFinite(value) ? value : null);
|
|
36108
|
+
}, [
|
|
36109
|
+
hasExplicitHourlyTargetOutputProp,
|
|
36110
|
+
skuTimelineSegments.length,
|
|
36111
|
+
shiftStart,
|
|
36112
|
+
shiftEnd,
|
|
36113
|
+
shiftBreaks,
|
|
36114
|
+
pphThreshold
|
|
36115
|
+
]);
|
|
36116
|
+
const effectiveHourlyTargetOutput = React144__namespace.default.useMemo(
|
|
36117
|
+
() => hasExplicitHourlyTargetOutputProp ? hourlyTargetOutput : fallbackHourlyTargetOutput,
|
|
36118
|
+
[hasExplicitHourlyTargetOutputProp, hourlyTargetOutput, fallbackHourlyTargetOutput]
|
|
36119
|
+
);
|
|
36120
|
+
const hasHourlyTargetOutputProp = React144__namespace.default.useMemo(
|
|
36121
|
+
() => effectiveHourlyTargetOutput !== void 0,
|
|
36122
|
+
[effectiveHourlyTargetOutput]
|
|
36123
|
+
);
|
|
36124
|
+
const hasExplicitHourlyTargets = React144__namespace.default.useMemo(
|
|
36125
|
+
() => Array.isArray(effectiveHourlyTargetOutput) && effectiveHourlyTargetOutput.some((value) => value !== null && value !== void 0),
|
|
36126
|
+
[effectiveHourlyTargetOutput]
|
|
36127
|
+
);
|
|
36128
|
+
const hourlyTargetSegments = React144__namespace.default.useMemo(() => {
|
|
36129
|
+
if (!hasExplicitHourlyTargets) return [];
|
|
36130
|
+
const segments = [];
|
|
36131
|
+
let runStart = null;
|
|
36132
|
+
let runValue = null;
|
|
36133
|
+
const flush = (endIndex) => {
|
|
36134
|
+
if (runStart === null || runValue === null) return;
|
|
36135
|
+
segments.push({ start: runStart, end: endIndex, value: runValue });
|
|
36136
|
+
runStart = null;
|
|
36137
|
+
runValue = null;
|
|
36138
|
+
};
|
|
36139
|
+
for (let i = 0; i < SHIFT_DURATION; i += 1) {
|
|
36140
|
+
const rawValue = Array.isArray(effectiveHourlyTargetOutput) ? effectiveHourlyTargetOutput[i] : null;
|
|
36141
|
+
const value = rawValue === null || rawValue === void 0 ? null : Number(rawValue);
|
|
36142
|
+
if (value === null || !Number.isFinite(value)) {
|
|
36143
|
+
flush(i);
|
|
36144
|
+
continue;
|
|
36145
|
+
}
|
|
36146
|
+
if (runStart === null || runValue === null) {
|
|
36147
|
+
runStart = i;
|
|
36148
|
+
runValue = value;
|
|
36149
|
+
continue;
|
|
36150
|
+
}
|
|
36151
|
+
if (Math.abs(runValue - value) > 1e-6) {
|
|
36152
|
+
flush(i);
|
|
36153
|
+
runStart = i;
|
|
36154
|
+
runValue = value;
|
|
36155
|
+
}
|
|
36156
|
+
}
|
|
36157
|
+
flush(SHIFT_DURATION);
|
|
36158
|
+
return segments.filter((segment) => segment.end > segment.start);
|
|
36159
|
+
}, [SHIFT_DURATION, hasExplicitHourlyTargets, effectiveHourlyTargetOutput]);
|
|
35422
36160
|
const activeSkuHourIndices = React144__namespace.default.useMemo(() => {
|
|
35423
36161
|
const indices = /* @__PURE__ */ new Set();
|
|
35424
36162
|
const targets = Array(SHIFT_DURATION).fill(pphThreshold);
|
|
35425
|
-
if (!skuSegments || !shiftWindow) return { indices, targets };
|
|
35426
|
-
const segmentsWithOffsets = skuSegments.
|
|
35427
|
-
|
|
35428
|
-
|
|
35429
|
-
|
|
36163
|
+
if (!skuSegments || !shiftWindow) return { indices, targets, hasTimeline: false };
|
|
36164
|
+
const segmentsWithOffsets = skuSegments.flatMap((segment) => {
|
|
36165
|
+
const offset = computeSegmentOffset(segment, shiftWindow);
|
|
36166
|
+
if (offset === null) return [];
|
|
36167
|
+
return [{ ...segment, offset }];
|
|
36168
|
+
}).sort((a, b) => a.offset - b.offset);
|
|
36169
|
+
if (segmentsWithOffsets.length === 0) {
|
|
36170
|
+
return { indices, targets, hasTimeline: false };
|
|
36171
|
+
}
|
|
35430
36172
|
for (let i = 0; i < SHIFT_DURATION; i++) {
|
|
35431
36173
|
const midpoint = i + 0.5;
|
|
35432
36174
|
let activeSeg = segmentsWithOffsets[0];
|
|
@@ -35446,13 +36188,15 @@ var HourlyOutputChartComponent = ({
|
|
|
35446
36188
|
}
|
|
35447
36189
|
}
|
|
35448
36190
|
}
|
|
35449
|
-
return { indices, targets };
|
|
36191
|
+
return { indices, targets, hasTimeline: true };
|
|
35450
36192
|
}, [skuSegments, activeSkuId, shiftWindow, SHIFT_DURATION, pphThreshold]);
|
|
35451
36193
|
const chartData = React144__namespace.default.useMemo(() => {
|
|
35452
|
-
const { indices, targets } = activeSkuHourIndices;
|
|
36194
|
+
const { indices, targets, hasTimeline } = activeSkuHourIndices;
|
|
35453
36195
|
return Array.from({ length: SHIFT_DURATION }, (_, i) => {
|
|
35454
36196
|
const idleSlot = idleSlots[i];
|
|
35455
|
-
const
|
|
36197
|
+
const explicitTarget = hasHourlyTargetOutputProp ? effectiveHourlyTargetOutput?.[i] ?? null : void 0;
|
|
36198
|
+
const currentTarget = hasHourlyTargetOutputProp ? explicitTarget : targets[i] || pphThreshold;
|
|
36199
|
+
const comparisonTarget = currentTarget === null || currentTarget === void 0 ? targets[i] || pphThreshold : currentTarget;
|
|
35456
36200
|
return {
|
|
35457
36201
|
hourIndex: idleSlot?.hourIndex ?? i,
|
|
35458
36202
|
hour: idleSlot?.hour || "",
|
|
@@ -35460,16 +36204,16 @@ var HourlyOutputChartComponent = ({
|
|
|
35460
36204
|
output: animatedData[i] || 0,
|
|
35461
36205
|
originalOutput: data[i] || 0,
|
|
35462
36206
|
// Keep original data for labels
|
|
35463
|
-
target: currentTarget,
|
|
35464
|
-
color: (animatedData[i] || 0) >=
|
|
36207
|
+
target: currentTarget ?? null,
|
|
36208
|
+
color: (animatedData[i] || 0) >= comparisonTarget ? "#00AB45" : "#E34329",
|
|
35465
36209
|
idleMinutes: idleSlot?.idleMinutes || 0,
|
|
35466
36210
|
idleArray: idleSlot?.idleArray || [],
|
|
35467
36211
|
skuIndex: i,
|
|
35468
36212
|
isHighlighted: indices.has(i),
|
|
35469
|
-
isDimmed: !!activeSkuId && !indices.has(i)
|
|
36213
|
+
isDimmed: hasTimeline && !!activeSkuId && !indices.has(i)
|
|
35470
36214
|
};
|
|
35471
36215
|
});
|
|
35472
|
-
}, [animatedData, data, pphThreshold, idleSlots, SHIFT_DURATION, activeSkuHourIndices, activeSkuId]);
|
|
36216
|
+
}, [animatedData, data, pphThreshold, idleSlots, SHIFT_DURATION, activeSkuHourIndices, activeSkuId, effectiveHourlyTargetOutput, hasHourlyTargetOutputProp]);
|
|
35473
36217
|
const renderSkuTimelineRail = React144__namespace.default.useCallback((props) => {
|
|
35474
36218
|
if (!skuTimelineSegments.length || SHIFT_DURATION <= 0) return null;
|
|
35475
36219
|
const offset = props?.offset;
|
|
@@ -35481,8 +36225,9 @@ var HourlyOutputChartComponent = ({
|
|
|
35481
36225
|
const railHeight = 3;
|
|
35482
36226
|
const railY = top - 10;
|
|
35483
36227
|
const baselineY = railY + railHeight / 2;
|
|
35484
|
-
const
|
|
35485
|
-
|
|
36228
|
+
const showHoverLabel = (label, centerX, rY) => {
|
|
36229
|
+
setHoveredSkuRailLabel({ label, centerX, railY: rY });
|
|
36230
|
+
};
|
|
35486
36231
|
return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
|
|
35487
36232
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
35488
36233
|
"line",
|
|
@@ -35500,9 +36245,43 @@ var HourlyOutputChartComponent = ({
|
|
|
35500
36245
|
const xStart = left + segment.start / SHIFT_DURATION * width;
|
|
35501
36246
|
const xEnd = left + segment.end / SHIFT_DURATION * width;
|
|
35502
36247
|
const segmentWidth = Math.max(1, xEnd - xStart);
|
|
36248
|
+
const labelPadding = segmentWidth < 48 ? 4 : 8;
|
|
36249
|
+
const inlineLabelText = formatSkuRailLabel(segment.label, segmentWidth, {
|
|
36250
|
+
horizontalPadding: labelPadding,
|
|
36251
|
+
cssTruncation: true
|
|
36252
|
+
});
|
|
36253
|
+
const badgeLabelText = formatSkuRailLabel(segment.label, 28, {
|
|
36254
|
+
horizontalPadding: 4,
|
|
36255
|
+
cssTruncation: true
|
|
36256
|
+
});
|
|
36257
|
+
const isMicroSegment = segmentWidth < 18;
|
|
36258
|
+
const labelText = isMicroSegment ? badgeLabelText : inlineLabelText;
|
|
36259
|
+
const labelClipWidth = Math.max(segmentWidth - labelPadding * 2, 0);
|
|
35503
36260
|
const isActive = !!activeSkuId && segment.skuId === activeSkuId;
|
|
35504
36261
|
const isDimmed = !!activeSkuId && segment.skuId !== activeSkuId;
|
|
36262
|
+
const hoverHitWidth = Math.max(segmentWidth + 12, isMicroSegment ? 20 : segmentWidth);
|
|
36263
|
+
const hoverHitX = Math.max(
|
|
36264
|
+
left,
|
|
36265
|
+
Math.min(xStart - (hoverHitWidth - segmentWidth) / 2, left + width - hoverHitWidth)
|
|
36266
|
+
);
|
|
36267
|
+
const hoverHitHeight = isMicroSegment ? 34 : 28;
|
|
36268
|
+
const hoverHitY = railY - (isMicroSegment ? 30 : 24);
|
|
36269
|
+
const microBadgeWidth = labelText ? Math.max(labelText.length * 6.2 + 10, 18) : 0;
|
|
36270
|
+
const microBadgeX = xStart + segmentWidth / 2 - microBadgeWidth / 2;
|
|
36271
|
+
const microBadgeY = railY - 24;
|
|
35505
36272
|
return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
|
|
36273
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
36274
|
+
"rect",
|
|
36275
|
+
{
|
|
36276
|
+
x: hoverHitX,
|
|
36277
|
+
y: hoverHitY,
|
|
36278
|
+
width: hoverHitWidth,
|
|
36279
|
+
height: hoverHitHeight,
|
|
36280
|
+
fill: "transparent",
|
|
36281
|
+
onMouseEnter: () => showHoverLabel(segment.label, xStart + segmentWidth / 2, railY),
|
|
36282
|
+
onMouseLeave: () => setHoveredSkuRailLabel((current) => current?.label === segment.label ? null : current)
|
|
36283
|
+
}
|
|
36284
|
+
),
|
|
35506
36285
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
35507
36286
|
"rect",
|
|
35508
36287
|
{
|
|
@@ -35513,7 +36292,7 @@ var HourlyOutputChartComponent = ({
|
|
|
35513
36292
|
rx: 1.5,
|
|
35514
36293
|
fill: isActive ? "#3b82f6" : "#cbd5e1",
|
|
35515
36294
|
opacity: isDimmed ? 0.3 : 1,
|
|
35516
|
-
style: { transition: "all 0.3s ease" }
|
|
36295
|
+
style: { transition: "all 0.3s ease", pointerEvents: "none" }
|
|
35517
36296
|
}
|
|
35518
36297
|
),
|
|
35519
36298
|
index > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -35525,22 +36304,59 @@ var HourlyOutputChartComponent = ({
|
|
|
35525
36304
|
y2: railY + railHeight + 3,
|
|
35526
36305
|
stroke: "#94a3b8",
|
|
35527
36306
|
strokeWidth: 1,
|
|
35528
|
-
opacity: 0.6
|
|
36307
|
+
opacity: 0.6,
|
|
36308
|
+
style: { pointerEvents: "none" }
|
|
35529
36309
|
}
|
|
35530
36310
|
),
|
|
35531
|
-
|
|
35532
|
-
|
|
36311
|
+
isMicroSegment && labelText && /* @__PURE__ */ jsxRuntime.jsxs("g", { style: { pointerEvents: "none" }, children: [
|
|
36312
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
36313
|
+
"rect",
|
|
36314
|
+
{
|
|
36315
|
+
x: microBadgeX,
|
|
36316
|
+
y: microBadgeY,
|
|
36317
|
+
width: microBadgeWidth,
|
|
36318
|
+
height: 14,
|
|
36319
|
+
rx: 7,
|
|
36320
|
+
fill: "white",
|
|
36321
|
+
stroke: isActive ? "#3b82f6" : "#cbd5e1",
|
|
36322
|
+
strokeWidth: 1,
|
|
36323
|
+
opacity: isDimmed ? 0.55 : 0.98
|
|
36324
|
+
}
|
|
36325
|
+
),
|
|
36326
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
36327
|
+
"text",
|
|
36328
|
+
{
|
|
36329
|
+
x: xStart + segmentWidth / 2,
|
|
36330
|
+
y: microBadgeY + 10,
|
|
36331
|
+
textAnchor: "middle",
|
|
36332
|
+
fontSize: 8,
|
|
36333
|
+
fontWeight: 700,
|
|
36334
|
+
fill: isActive ? "#2563eb" : "#64748b",
|
|
36335
|
+
opacity: isDimmed ? 0.55 : 1,
|
|
36336
|
+
style: { transition: "all 0.3s ease" },
|
|
36337
|
+
children: labelText
|
|
36338
|
+
}
|
|
36339
|
+
)
|
|
36340
|
+
] }),
|
|
36341
|
+
!isMicroSegment && labelText && labelClipWidth > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
36342
|
+
"foreignObject",
|
|
35533
36343
|
{
|
|
35534
|
-
x: xStart +
|
|
35535
|
-
y:
|
|
35536
|
-
|
|
35537
|
-
|
|
35538
|
-
|
|
35539
|
-
|
|
35540
|
-
|
|
35541
|
-
|
|
35542
|
-
|
|
35543
|
-
|
|
36344
|
+
x: xStart + labelPadding,
|
|
36345
|
+
y: railY - 24,
|
|
36346
|
+
width: labelClipWidth,
|
|
36347
|
+
height: 18,
|
|
36348
|
+
style: { pointerEvents: "none", overflow: "visible" },
|
|
36349
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
36350
|
+
"div",
|
|
36351
|
+
{
|
|
36352
|
+
className: `w-full h-full flex items-center justify-center text-[10px] font-bold tracking-[0.02em] truncate pt-0.5 ${isActive ? "text-blue-600" : "text-slate-500"}`,
|
|
36353
|
+
style: {
|
|
36354
|
+
opacity: isDimmed ? 0.4 : 1,
|
|
36355
|
+
transition: "all 0.3s ease"
|
|
36356
|
+
},
|
|
36357
|
+
children: labelText
|
|
36358
|
+
}
|
|
36359
|
+
)
|
|
35544
36360
|
}
|
|
35545
36361
|
)
|
|
35546
36362
|
] }, `sku-rail-${segment.skuId}-${segment.start}-${index}`);
|
|
@@ -35598,14 +36414,17 @@ var HourlyOutputChartComponent = ({
|
|
|
35598
36414
|
);
|
|
35599
36415
|
}, [idleBarState.visible, idleBarState.key, idleBarState.shouldAnimate]);
|
|
35600
36416
|
const maxDataValue = Math.max(...data, 0);
|
|
35601
|
-
const
|
|
36417
|
+
const numericChartTargets = chartData.map((d) => d.target).filter((target) => target !== null && Number.isFinite(target));
|
|
36418
|
+
const maxTargetValue = Math.max(...numericChartTargets, pphThreshold, 0);
|
|
35602
36419
|
const maxYValue = Math.max(
|
|
35603
36420
|
Math.ceil(maxTargetValue * 1.5),
|
|
35604
36421
|
Math.ceil(maxDataValue * 1.15)
|
|
35605
36422
|
// Add 15% headroom above max value
|
|
35606
36423
|
);
|
|
35607
36424
|
const generateYAxisTicks = () => {
|
|
35608
|
-
const uniqueTargets = [...new Set(
|
|
36425
|
+
const uniqueTargets = [...new Set(
|
|
36426
|
+
chartData.map((d) => d.target).filter((target) => target !== null && Number.isFinite(target)).map((target) => Math.round(target))
|
|
36427
|
+
)].sort((a, b) => a - b);
|
|
35609
36428
|
const rawTicks = [0];
|
|
35610
36429
|
uniqueTargets.forEach((target) => {
|
|
35611
36430
|
if (target > 0) {
|
|
@@ -35640,14 +36459,54 @@ var HourlyOutputChartComponent = ({
|
|
|
35640
36459
|
};
|
|
35641
36460
|
const renderTargetLine = React144__namespace.default.useCallback((props) => {
|
|
35642
36461
|
const { offset, yAxisMap } = props;
|
|
35643
|
-
if (!offset || !yAxisMap || SHIFT_DURATION <= 0) return null;
|
|
36462
|
+
if (!offset || !yAxisMap || SHIFT_DURATION <= 0 || targetLineEndOffset <= 0) return null;
|
|
35644
36463
|
const { left, width } = offset;
|
|
35645
36464
|
const yAxis = yAxisMap["default"] || yAxisMap[0];
|
|
35646
36465
|
if (!yAxis || !yAxis.scale) return null;
|
|
35647
36466
|
const lines = [];
|
|
35648
36467
|
const offsetToX = (o) => left + o / SHIFT_DURATION * width;
|
|
35649
|
-
if (
|
|
35650
|
-
|
|
36468
|
+
if (hasHourlyTargetOutputProp && hourlyTargetSegments.length > 0) {
|
|
36469
|
+
hourlyTargetSegments.forEach((segment, index) => {
|
|
36470
|
+
const y = yAxis.scale(segment.value);
|
|
36471
|
+
const xStart = offsetToX(segment.start);
|
|
36472
|
+
const xEnd = offsetToX(segment.end);
|
|
36473
|
+
lines.push(
|
|
36474
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
36475
|
+
"line",
|
|
36476
|
+
{
|
|
36477
|
+
x1: xStart,
|
|
36478
|
+
y1: y,
|
|
36479
|
+
x2: xEnd,
|
|
36480
|
+
y2: y,
|
|
36481
|
+
stroke: "#E34329",
|
|
36482
|
+
strokeDasharray: "3 3",
|
|
36483
|
+
strokeWidth: 2
|
|
36484
|
+
},
|
|
36485
|
+
`target-hourly-h-${index}`
|
|
36486
|
+
)
|
|
36487
|
+
);
|
|
36488
|
+
const next = hourlyTargetSegments[index + 1];
|
|
36489
|
+
if (next && Math.abs(next.value - segment.value) > 1e-6) {
|
|
36490
|
+
const nextY = yAxis.scale(next.value);
|
|
36491
|
+
lines.push(
|
|
36492
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
36493
|
+
"line",
|
|
36494
|
+
{
|
|
36495
|
+
x1: xEnd,
|
|
36496
|
+
y1: y,
|
|
36497
|
+
x2: xEnd,
|
|
36498
|
+
y2: nextY,
|
|
36499
|
+
stroke: "#E34329",
|
|
36500
|
+
strokeDasharray: "3 3",
|
|
36501
|
+
strokeWidth: 2
|
|
36502
|
+
},
|
|
36503
|
+
`target-hourly-v-${index}`
|
|
36504
|
+
)
|
|
36505
|
+
);
|
|
36506
|
+
}
|
|
36507
|
+
});
|
|
36508
|
+
} else if (!hasHourlyTargetOutputProp && targetTimelineSegments.length > 0) {
|
|
36509
|
+
targetTimelineSegments.forEach((segment, index) => {
|
|
35651
36510
|
const target = segment.pphThreshold || pphThreshold;
|
|
35652
36511
|
const y = yAxis.scale(target);
|
|
35653
36512
|
const xStart = offsetToX(segment.start);
|
|
@@ -35667,7 +36526,7 @@ var HourlyOutputChartComponent = ({
|
|
|
35667
36526
|
`target-h-${index}`
|
|
35668
36527
|
)
|
|
35669
36528
|
);
|
|
35670
|
-
const next =
|
|
36529
|
+
const next = targetTimelineSegments[index + 1];
|
|
35671
36530
|
if (next) {
|
|
35672
36531
|
const nextTarget = next.pphThreshold || pphThreshold;
|
|
35673
36532
|
if (nextTarget !== target) {
|
|
@@ -35690,7 +36549,7 @@ var HourlyOutputChartComponent = ({
|
|
|
35690
36549
|
}
|
|
35691
36550
|
}
|
|
35692
36551
|
});
|
|
35693
|
-
} else {
|
|
36552
|
+
} else if (!hasHourlyTargetOutputProp) {
|
|
35694
36553
|
const y = yAxis.scale(pphThreshold);
|
|
35695
36554
|
lines.push(
|
|
35696
36555
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -35709,10 +36568,13 @@ var HourlyOutputChartComponent = ({
|
|
|
35709
36568
|
);
|
|
35710
36569
|
}
|
|
35711
36570
|
return /* @__PURE__ */ jsxRuntime.jsx("g", { children: lines });
|
|
35712
|
-
}, [
|
|
36571
|
+
}, [hourlyTargetSegments, targetTimelineSegments, SHIFT_DURATION, pphThreshold, targetLineEndOffset, hasHourlyTargetOutputProp]);
|
|
35713
36572
|
const renderLegend = () => {
|
|
35714
|
-
const uniqueTargets = [...new Set(
|
|
35715
|
-
|
|
36573
|
+
const uniqueTargets = [...new Set(
|
|
36574
|
+
chartData.map((d) => d.target).filter((target) => target !== null && Number.isFinite(target)).map((target) => Math.round(target))
|
|
36575
|
+
)].sort((a, b) => a - b);
|
|
36576
|
+
const unitLabel = hasHourlyTargetOutputProp ? "units" : "units/hr";
|
|
36577
|
+
const targetText = uniqueTargets.length === 0 ? `Target` : uniqueTargets.length === 1 ? `Target: ${uniqueTargets[0]} ${unitLabel}` : `Target: ${uniqueTargets[0]} - ${uniqueTargets[uniqueTargets.length - 1]} ${unitLabel}`;
|
|
35716
36578
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center text-sm text-gray-600 bg-white py-1", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 border border-gray-100 rounded-full px-3 py-1", children: [
|
|
35717
36579
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-8 flex items-center", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full border-t-2 border-[#E34329] border-dashed" }) }),
|
|
35718
36580
|
/* @__PURE__ */ jsxRuntime.jsx("span", { children: targetText })
|
|
@@ -35725,254 +36587,274 @@ var HourlyOutputChartComponent = ({
|
|
|
35725
36587
|
className: `w-full h-full min-w-0 flex flex-col ${className}`,
|
|
35726
36588
|
style: { minHeight: "200px", minWidth: 0 },
|
|
35727
36589
|
children: [
|
|
35728
|
-
containerReady ? /* @__PURE__ */ jsxRuntime.
|
|
35729
|
-
recharts.
|
|
35730
|
-
|
|
35731
|
-
|
|
35732
|
-
|
|
35733
|
-
|
|
35734
|
-
|
|
35735
|
-
|
|
35736
|
-
|
|
35737
|
-
|
|
35738
|
-
|
|
35739
|
-
|
|
35740
|
-
|
|
35741
|
-
|
|
35742
|
-
|
|
35743
|
-
|
|
35744
|
-
|
|
35745
|
-
|
|
35746
|
-
|
|
35747
|
-
|
|
35748
|
-
|
|
35749
|
-
|
|
35750
|
-
|
|
35751
|
-
|
|
35752
|
-
|
|
35753
|
-
|
|
35754
|
-
|
|
35755
|
-
|
|
35756
|
-
|
|
35757
|
-
),
|
|
35758
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
35759
|
-
recharts.XAxis,
|
|
35760
|
-
{
|
|
35761
|
-
xAxisId: "sku",
|
|
35762
|
-
type: "number",
|
|
35763
|
-
dataKey: "skuIndex",
|
|
35764
|
-
domain: [0, Math.max(SHIFT_DURATION, 0)],
|
|
35765
|
-
hide: true,
|
|
35766
|
-
allowDataOverflow: true
|
|
35767
|
-
}
|
|
35768
|
-
),
|
|
35769
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
35770
|
-
recharts.YAxis,
|
|
35771
|
-
{
|
|
35772
|
-
yAxisId: "default",
|
|
35773
|
-
tickMargin: 8,
|
|
35774
|
-
width: 48,
|
|
35775
|
-
domain: [0, maxYValue],
|
|
35776
|
-
ticks: generateYAxisTicks(),
|
|
35777
|
-
tickFormatter: (value) => value,
|
|
35778
|
-
tick: (props) => {
|
|
35779
|
-
const { x, y, payload } = props;
|
|
35780
|
-
return /* @__PURE__ */ jsxRuntime.jsx("g", { transform: `translate(${x},${y})`, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
35781
|
-
"text",
|
|
35782
|
-
{
|
|
35783
|
-
x: -2,
|
|
35784
|
-
y: 0,
|
|
35785
|
-
dy: 4,
|
|
35786
|
-
textAnchor: "end",
|
|
35787
|
-
fill: "#666",
|
|
35788
|
-
fontSize: 12,
|
|
35789
|
-
children: payload.value
|
|
35790
|
-
},
|
|
35791
|
-
`tick-${payload.value}-${x}-${y}`
|
|
35792
|
-
) });
|
|
36590
|
+
containerReady ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-h-0 relative", children: [
|
|
36591
|
+
/* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
36592
|
+
recharts.BarChart,
|
|
36593
|
+
{
|
|
36594
|
+
data: chartData,
|
|
36595
|
+
margin: {
|
|
36596
|
+
// Reserve headroom for the SKU timeline rail + staggered
|
|
36597
|
+
// labels only when SKU segments are rendered. Non-SKU charts
|
|
36598
|
+
// keep the original 10px top so recharts has enough vertical
|
|
36599
|
+
// space to show the target (pph) tick label on the Y-axis.
|
|
36600
|
+
top: skuTimelineSegments.length > 0 ? 40 : 10,
|
|
36601
|
+
right: 10,
|
|
36602
|
+
bottom: 10,
|
|
36603
|
+
left: 6
|
|
36604
|
+
},
|
|
36605
|
+
barCategoryGap: "25%",
|
|
36606
|
+
children: [
|
|
36607
|
+
/* @__PURE__ */ jsxRuntime.jsx(recharts.CartesianGrid, { strokeDasharray: "3 3", vertical: false }),
|
|
36608
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
36609
|
+
recharts.XAxis,
|
|
36610
|
+
{
|
|
36611
|
+
xAxisId: "default",
|
|
36612
|
+
dataKey: "hour",
|
|
36613
|
+
tick: { fontSize: xAxisConfig.tickFont },
|
|
36614
|
+
interval: xAxisConfig.interval,
|
|
36615
|
+
angle: xAxisConfig.angle,
|
|
36616
|
+
textAnchor: "end",
|
|
36617
|
+
tickMargin: xAxisConfig.tickMargin,
|
|
36618
|
+
height: xAxisConfig.height
|
|
35793
36619
|
}
|
|
35794
|
-
|
|
35795
|
-
|
|
35796
|
-
|
|
35797
|
-
|
|
35798
|
-
|
|
35799
|
-
|
|
35800
|
-
|
|
35801
|
-
|
|
35802
|
-
|
|
35803
|
-
|
|
35804
|
-
|
|
35805
|
-
|
|
35806
|
-
|
|
35807
|
-
|
|
35808
|
-
|
|
35809
|
-
|
|
35810
|
-
|
|
35811
|
-
|
|
35812
|
-
|
|
35813
|
-
|
|
35814
|
-
|
|
35815
|
-
|
|
35816
|
-
|
|
35817
|
-
|
|
35818
|
-
|
|
35819
|
-
|
|
35820
|
-
|
|
35821
|
-
|
|
35822
|
-
|
|
35823
|
-
|
|
35824
|
-
|
|
35825
|
-
|
|
35826
|
-
|
|
35827
|
-
|
|
35828
|
-
|
|
35829
|
-
|
|
35830
|
-
|
|
35831
|
-
|
|
35832
|
-
|
|
35833
|
-
|
|
35834
|
-
|
|
35835
|
-
|
|
35836
|
-
|
|
35837
|
-
|
|
35838
|
-
|
|
35839
|
-
|
|
35840
|
-
|
|
36620
|
+
),
|
|
36621
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
36622
|
+
recharts.XAxis,
|
|
36623
|
+
{
|
|
36624
|
+
xAxisId: "sku",
|
|
36625
|
+
type: "number",
|
|
36626
|
+
dataKey: "skuIndex",
|
|
36627
|
+
domain: [0, Math.max(SHIFT_DURATION, 0)],
|
|
36628
|
+
hide: true,
|
|
36629
|
+
allowDataOverflow: true
|
|
36630
|
+
}
|
|
36631
|
+
),
|
|
36632
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
36633
|
+
recharts.YAxis,
|
|
36634
|
+
{
|
|
36635
|
+
yAxisId: "default",
|
|
36636
|
+
tickMargin: 8,
|
|
36637
|
+
width: 48,
|
|
36638
|
+
domain: [0, maxYValue],
|
|
36639
|
+
ticks: generateYAxisTicks(),
|
|
36640
|
+
tickFormatter: (value) => value,
|
|
36641
|
+
tick: (props) => {
|
|
36642
|
+
const { x, y, payload } = props;
|
|
36643
|
+
return /* @__PURE__ */ jsxRuntime.jsx("g", { transform: `translate(${x},${y})`, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
36644
|
+
"text",
|
|
36645
|
+
{
|
|
36646
|
+
x: -2,
|
|
36647
|
+
y: 0,
|
|
36648
|
+
dy: 4,
|
|
36649
|
+
textAnchor: "end",
|
|
36650
|
+
fill: "#666",
|
|
36651
|
+
fontSize: 12,
|
|
36652
|
+
children: payload.value
|
|
36653
|
+
},
|
|
36654
|
+
`tick-${payload.value}-${x}-${y}`
|
|
36655
|
+
) });
|
|
36656
|
+
}
|
|
36657
|
+
}
|
|
36658
|
+
),
|
|
36659
|
+
/* @__PURE__ */ jsxRuntime.jsx(recharts.YAxis, { yAxisId: "idle", domain: [0, 60], hide: true }),
|
|
36660
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
36661
|
+
recharts.Tooltip,
|
|
36662
|
+
{
|
|
36663
|
+
cursor: { fill: "#f1f5f9" },
|
|
36664
|
+
contentStyle: { backgroundColor: "transparent", border: "none", padding: 0 },
|
|
36665
|
+
content: (props) => {
|
|
36666
|
+
if (!props.active || !props.payload || props.payload.length === 0)
|
|
36667
|
+
return null;
|
|
36668
|
+
const data2 = props.payload[0].payload;
|
|
36669
|
+
const idlePeriods = showIdleTime ? getHourlyIdlePeriods({
|
|
36670
|
+
idleArray: data2.idleArray,
|
|
36671
|
+
shiftStart,
|
|
36672
|
+
hourIndex: Number.isFinite(data2.hourIndex) ? data2.hourIndex : 0
|
|
36673
|
+
}) : [];
|
|
36674
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white/95 backdrop-blur-md border border-slate-200/60 shadow-xl shadow-slate-200/40 rounded-xl p-4 min-w-[240px] text-slate-700", children: [
|
|
36675
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between mb-4 pb-3 border-b border-slate-100", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-semibold text-slate-900 text-sm tracking-tight", children: data2.timeRange }) }),
|
|
36676
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
|
|
36677
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
36678
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-slate-500 font-medium tracking-wide", children: "Output" }),
|
|
36679
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-bold text-slate-900 text-sm", children: [
|
|
36680
|
+
Math.round(data2.output),
|
|
36681
|
+
" ",
|
|
36682
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-slate-400 font-normal text-xs ml-0.5", children: "units" })
|
|
36683
|
+
] })
|
|
36684
|
+
] }),
|
|
36685
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
36686
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-slate-500 font-medium tracking-wide", children: "Target" }),
|
|
36687
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-bold text-slate-700 text-sm", children: [
|
|
36688
|
+
Math.round(data2.target),
|
|
36689
|
+
" ",
|
|
36690
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-slate-400 font-normal text-xs ml-0.5", children: "units" })
|
|
36691
|
+
] })
|
|
36692
|
+
] }),
|
|
36693
|
+
showIdleTime && data2.idleMinutes > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
36694
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "pt-3 mt-3 border-t border-slate-100", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
36695
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-slate-500 font-medium tracking-wide", children: "Idle Time" }),
|
|
36696
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-bold text-orange-600 text-sm flex items-center gap-1.5", children: [
|
|
36697
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-1.5 h-1.5 rounded-full bg-orange-500 shadow-[0_0_6px_rgba(249,115,22,0.6)] animate-pulse" }),
|
|
36698
|
+
data2.idleMinutes,
|
|
36699
|
+
" ",
|
|
36700
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-orange-500/70 font-normal text-xs ml-0.5", children: "min" })
|
|
36701
|
+
] })
|
|
36702
|
+
] }) }),
|
|
36703
|
+
idlePeriods.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3 bg-slate-50/80 rounded-lg p-3 border border-slate-100/50", children: [
|
|
36704
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-semibold text-slate-400 text-[10px] mb-2.5 uppercase tracking-wider", children: "Idle Periods" }),
|
|
36705
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2.5 max-h-32 overflow-y-auto pr-1 custom-scrollbar", children: idlePeriods.map((period, index) => {
|
|
36706
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
36707
|
+
"div",
|
|
36708
|
+
{
|
|
36709
|
+
className: "flex items-start gap-2.5 text-xs",
|
|
36710
|
+
children: [
|
|
36711
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-[5px] w-1.5 h-1.5 bg-orange-400 rounded-full flex-shrink-0 shadow-[0_0_4px_rgba(251,146,60,0.5)]" }),
|
|
36712
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-slate-700 font-medium tracking-tight", children: period.duration === 1 ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
36713
|
+
period.startTime,
|
|
36714
|
+
" ",
|
|
36715
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-slate-400 font-normal ml-1", children: [
|
|
36716
|
+
"(",
|
|
36717
|
+
period.duration,
|
|
36718
|
+
"m)"
|
|
36719
|
+
] })
|
|
36720
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
36721
|
+
period.startTime,
|
|
36722
|
+
" ",
|
|
36723
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-slate-400 mx-0.5", children: "\u2192" }),
|
|
36724
|
+
" ",
|
|
36725
|
+
period.endTime,
|
|
36726
|
+
" ",
|
|
36727
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-slate-400 font-normal ml-1", children: [
|
|
36728
|
+
"(",
|
|
36729
|
+
period.duration,
|
|
36730
|
+
"m)"
|
|
36731
|
+
] })
|
|
36732
|
+
] }) })
|
|
36733
|
+
]
|
|
36734
|
+
},
|
|
36735
|
+
index
|
|
36736
|
+
);
|
|
36737
|
+
}) })
|
|
35841
36738
|
] })
|
|
35842
|
-
] }) }),
|
|
35843
|
-
idlePeriods.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3 bg-gray-50 rounded-lg p-2.5", children: [
|
|
35844
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-medium text-gray-700 text-xs mb-2", children: "Idle periods:" }),
|
|
35845
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-1 max-h-32 overflow-y-auto pr-1", children: idlePeriods.map((period, index) => {
|
|
35846
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
35847
|
-
"div",
|
|
35848
|
-
{
|
|
35849
|
-
className: "text-gray-600 flex items-center gap-2 text-xs",
|
|
35850
|
-
children: [
|
|
35851
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block w-1.5 h-1.5 bg-orange-400 rounded-full flex-shrink-0" }),
|
|
35852
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: period.duration === 1 ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
35853
|
-
period.startTime,
|
|
35854
|
-
" (",
|
|
35855
|
-
period.duration,
|
|
35856
|
-
" min)"
|
|
35857
|
-
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
35858
|
-
period.startTime,
|
|
35859
|
-
" -",
|
|
35860
|
-
" ",
|
|
35861
|
-
period.endTime,
|
|
35862
|
-
" (",
|
|
35863
|
-
period.duration,
|
|
35864
|
-
" mins)"
|
|
35865
|
-
] }) })
|
|
35866
|
-
]
|
|
35867
|
-
},
|
|
35868
|
-
index
|
|
35869
|
-
);
|
|
35870
|
-
}) })
|
|
35871
36739
|
] })
|
|
35872
36740
|
] })
|
|
35873
|
-
] })
|
|
35874
|
-
|
|
35875
|
-
|
|
35876
|
-
|
|
35877
|
-
|
|
35878
|
-
|
|
35879
|
-
|
|
35880
|
-
|
|
35881
|
-
|
|
35882
|
-
|
|
35883
|
-
|
|
35884
|
-
|
|
35885
|
-
|
|
35886
|
-
|
|
35887
|
-
|
|
35888
|
-
|
|
35889
|
-
|
|
35890
|
-
|
|
35891
|
-
|
|
35892
|
-
|
|
35893
|
-
|
|
35894
|
-
|
|
35895
|
-
|
|
35896
|
-
|
|
35897
|
-
|
|
35898
|
-
|
|
35899
|
-
|
|
35900
|
-
|
|
35901
|
-
|
|
35902
|
-
|
|
35903
|
-
|
|
35904
|
-
|
|
35905
|
-
|
|
35906
|
-
|
|
35907
|
-
|
|
35908
|
-
|
|
36741
|
+
] });
|
|
36742
|
+
},
|
|
36743
|
+
animationDuration: 200
|
|
36744
|
+
}
|
|
36745
|
+
),
|
|
36746
|
+
/* @__PURE__ */ jsxRuntime.jsx(recharts.Customized, { component: renderTargetLine }),
|
|
36747
|
+
/* @__PURE__ */ jsxRuntime.jsx(recharts.Customized, { component: renderSkuTimelineRail }),
|
|
36748
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
36749
|
+
recharts.Bar,
|
|
36750
|
+
{
|
|
36751
|
+
xAxisId: "default",
|
|
36752
|
+
dataKey: "output",
|
|
36753
|
+
yAxisId: "default",
|
|
36754
|
+
maxBarSize: 35,
|
|
36755
|
+
radius: [10, 10, 0, 0],
|
|
36756
|
+
isAnimationActive: false,
|
|
36757
|
+
children: [
|
|
36758
|
+
chartData.map((entry, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
36759
|
+
recharts.Cell,
|
|
36760
|
+
{
|
|
36761
|
+
fill: entry.color,
|
|
36762
|
+
stroke: "transparent",
|
|
36763
|
+
strokeWidth: 0,
|
|
36764
|
+
style: {
|
|
36765
|
+
filter: entry.isHighlighted ? "drop-shadow(0 4px 6px rgba(0, 0, 0, 0.12)) brightness(1.05)" : "brightness(1)",
|
|
36766
|
+
transform: entry.isHighlighted ? "translateY(-4px)" : "translateY(0)",
|
|
36767
|
+
opacity: entry.isDimmed ? 0.4 : 1,
|
|
36768
|
+
transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)",
|
|
36769
|
+
cursor: "pointer"
|
|
36770
|
+
},
|
|
36771
|
+
onMouseEnter: (e) => {
|
|
36772
|
+
const target = e.target;
|
|
36773
|
+
target.style.filter = "drop-shadow(0 4px 6px rgba(0, 0, 0, 0.12)) brightness(1.05)";
|
|
36774
|
+
target.style.transform = "translateY(-4px)";
|
|
36775
|
+
target.style.opacity = "1";
|
|
36776
|
+
},
|
|
36777
|
+
onMouseLeave: (e) => {
|
|
36778
|
+
const target = e.target;
|
|
36779
|
+
target.style.filter = entry.isHighlighted ? "drop-shadow(0 4px 6px rgba(0, 0, 0, 0.12)) brightness(1.05)" : "brightness(1)";
|
|
36780
|
+
target.style.transform = entry.isHighlighted ? "translateY(-4px)" : "translateY(0)";
|
|
36781
|
+
target.style.opacity = entry.isDimmed ? "0.4" : "1";
|
|
36782
|
+
}
|
|
35909
36783
|
},
|
|
35910
|
-
|
|
35911
|
-
|
|
35912
|
-
|
|
35913
|
-
|
|
35914
|
-
|
|
36784
|
+
`cell-${index}`
|
|
36785
|
+
)),
|
|
36786
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
36787
|
+
recharts.LabelList,
|
|
36788
|
+
{
|
|
36789
|
+
dataKey: "originalOutput",
|
|
36790
|
+
position: "top",
|
|
36791
|
+
content: (props) => {
|
|
36792
|
+
const { x, y, width, value, payload } = props;
|
|
36793
|
+
const actualValue = payload?.originalOutput || value;
|
|
36794
|
+
if (!actualValue || actualValue === 0) return null;
|
|
36795
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
36796
|
+
"text",
|
|
36797
|
+
{
|
|
36798
|
+
x: x + width / 2,
|
|
36799
|
+
y: y - 8,
|
|
36800
|
+
textAnchor: "middle",
|
|
36801
|
+
fontSize: "12",
|
|
36802
|
+
fontWeight: "600",
|
|
36803
|
+
fill: "#374151",
|
|
36804
|
+
style: {
|
|
36805
|
+
opacity: 1,
|
|
36806
|
+
pointerEvents: "none",
|
|
36807
|
+
transition: "none"
|
|
36808
|
+
},
|
|
36809
|
+
children: Math.round(actualValue)
|
|
36810
|
+
}
|
|
36811
|
+
);
|
|
36812
|
+
}
|
|
35915
36813
|
}
|
|
35916
|
-
|
|
35917
|
-
|
|
35918
|
-
|
|
35919
|
-
|
|
35920
|
-
|
|
35921
|
-
|
|
35922
|
-
|
|
35923
|
-
|
|
35924
|
-
|
|
35925
|
-
|
|
35926
|
-
|
|
35927
|
-
|
|
35928
|
-
|
|
35929
|
-
|
|
35930
|
-
|
|
35931
|
-
|
|
35932
|
-
|
|
35933
|
-
|
|
35934
|
-
|
|
35935
|
-
|
|
35936
|
-
|
|
35937
|
-
style: {
|
|
35938
|
-
opacity: 1,
|
|
35939
|
-
pointerEvents: "none",
|
|
35940
|
-
transition: "none"
|
|
35941
|
-
},
|
|
35942
|
-
children: Math.round(actualValue)
|
|
35943
|
-
}
|
|
35944
|
-
);
|
|
36814
|
+
)
|
|
36815
|
+
]
|
|
36816
|
+
}
|
|
36817
|
+
),
|
|
36818
|
+
IdleBar,
|
|
36819
|
+
/* @__PURE__ */ jsxRuntime.jsx("defs", { children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
36820
|
+
"pattern",
|
|
36821
|
+
{
|
|
36822
|
+
id: "idlePattern",
|
|
36823
|
+
patternUnits: "userSpaceOnUse",
|
|
36824
|
+
width: "4",
|
|
36825
|
+
height: "4",
|
|
36826
|
+
children: [
|
|
36827
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { width: "4", height: "4", fill: "#4b5563", opacity: "0.6" }),
|
|
36828
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
36829
|
+
"path",
|
|
36830
|
+
{
|
|
36831
|
+
d: "M 0,4 l 4,-4 M -1,1 l 2,-2 M 3,5 l 2,-2",
|
|
36832
|
+
stroke: "#374151",
|
|
36833
|
+
strokeWidth: "0.8",
|
|
36834
|
+
opacity: "0.8"
|
|
35945
36835
|
}
|
|
35946
|
-
|
|
35947
|
-
|
|
35948
|
-
|
|
35949
|
-
}
|
|
35950
|
-
|
|
35951
|
-
|
|
35952
|
-
|
|
35953
|
-
|
|
35954
|
-
|
|
35955
|
-
|
|
35956
|
-
|
|
35957
|
-
|
|
35958
|
-
|
|
35959
|
-
|
|
35960
|
-
|
|
35961
|
-
|
|
35962
|
-
|
|
35963
|
-
|
|
35964
|
-
|
|
35965
|
-
|
|
35966
|
-
|
|
35967
|
-
|
|
35968
|
-
}
|
|
35969
|
-
)
|
|
35970
|
-
]
|
|
35971
|
-
}
|
|
35972
|
-
) })
|
|
35973
|
-
]
|
|
35974
|
-
}
|
|
35975
|
-
) }) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full flex items-center justify-center bg-gray-50 rounded-lg", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-500 text-sm", children: "Loading chart..." }) }),
|
|
36836
|
+
)
|
|
36837
|
+
]
|
|
36838
|
+
}
|
|
36839
|
+
) })
|
|
36840
|
+
]
|
|
36841
|
+
}
|
|
36842
|
+
) }),
|
|
36843
|
+
hoveredSkuRailLabel && /* @__PURE__ */ jsxRuntime.jsx(
|
|
36844
|
+
"div",
|
|
36845
|
+
{
|
|
36846
|
+
className: "absolute z-50 pointer-events-none transform -translate-x-1/2 -translate-y-full transition-opacity duration-200",
|
|
36847
|
+
style: {
|
|
36848
|
+
left: hoveredSkuRailLabel.centerX,
|
|
36849
|
+
top: hoveredSkuRailLabel.railY - 12
|
|
36850
|
+
},
|
|
36851
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white/95 backdrop-blur-md border border-slate-200/80 shadow-xl shadow-slate-200/50 rounded-lg px-3 py-2 flex flex-col min-w-[100px] max-w-[280px]", children: [
|
|
36852
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-bold tracking-wider text-blue-500 uppercase leading-none mb-1", children: "SKU" }),
|
|
36853
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[13px] font-semibold text-slate-800 whitespace-normal break-words leading-snug", children: hoveredSkuRailLabel.label })
|
|
36854
|
+
] })
|
|
36855
|
+
}
|
|
36856
|
+
)
|
|
36857
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full flex items-center justify-center bg-gray-50 rounded-lg", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-500 text-sm", children: "Loading chart..." }) }),
|
|
35976
36858
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-none pt-2", children: renderLegend() })
|
|
35977
36859
|
]
|
|
35978
36860
|
}
|
|
@@ -35990,6 +36872,38 @@ var HourlyOutputChart = React144__namespace.default.memo(
|
|
|
35990
36872
|
if (!prevProps.data.every((val, idx) => val === nextProps.data[idx])) {
|
|
35991
36873
|
return false;
|
|
35992
36874
|
}
|
|
36875
|
+
const prevHasHourlyTargetOutputProp = prevProps.hourlyTargetOutput !== void 0;
|
|
36876
|
+
const nextHasHourlyTargetOutputProp = nextProps.hourlyTargetOutput !== void 0;
|
|
36877
|
+
if (prevHasHourlyTargetOutputProp !== nextHasHourlyTargetOutputProp) {
|
|
36878
|
+
return false;
|
|
36879
|
+
}
|
|
36880
|
+
if (prevProps.hourlyTargetOutput === null || nextProps.hourlyTargetOutput === null) {
|
|
36881
|
+
if (prevProps.hourlyTargetOutput !== nextProps.hourlyTargetOutput) {
|
|
36882
|
+
return false;
|
|
36883
|
+
}
|
|
36884
|
+
}
|
|
36885
|
+
const prevHourlyTargets = prevProps.hourlyTargetOutput || [];
|
|
36886
|
+
const nextHourlyTargets = nextProps.hourlyTargetOutput || [];
|
|
36887
|
+
if (prevHourlyTargets.length !== nextHourlyTargets.length) {
|
|
36888
|
+
return false;
|
|
36889
|
+
}
|
|
36890
|
+
for (let i = 0; i < prevHourlyTargets.length; i += 1) {
|
|
36891
|
+
if (prevHourlyTargets[i] !== nextHourlyTargets[i]) {
|
|
36892
|
+
return false;
|
|
36893
|
+
}
|
|
36894
|
+
}
|
|
36895
|
+
const prevShiftBreaks = prevProps.shiftBreaks || [];
|
|
36896
|
+
const nextShiftBreaks = nextProps.shiftBreaks || [];
|
|
36897
|
+
if (prevShiftBreaks.length !== nextShiftBreaks.length) {
|
|
36898
|
+
return false;
|
|
36899
|
+
}
|
|
36900
|
+
for (let i = 0; i < prevShiftBreaks.length; i += 1) {
|
|
36901
|
+
const prevBreak = prevShiftBreaks[i] || {};
|
|
36902
|
+
const nextBreak = nextShiftBreaks[i] || {};
|
|
36903
|
+
if (prevBreak.startTime !== nextBreak.startTime || prevBreak.endTime !== nextBreak.endTime || prevBreak.duration !== nextBreak.duration || prevBreak.remarks !== nextBreak.remarks) {
|
|
36904
|
+
return false;
|
|
36905
|
+
}
|
|
36906
|
+
}
|
|
35993
36907
|
const prevIdle = prevProps.idleTimeHourly || {};
|
|
35994
36908
|
const nextIdle = nextProps.idleTimeHourly || {};
|
|
35995
36909
|
const prevKeys = Object.keys(prevIdle);
|
|
@@ -36177,7 +37091,6 @@ var VideoCard = React144__namespace.default.memo(({
|
|
|
36177
37091
|
shouldPlay,
|
|
36178
37092
|
onClick,
|
|
36179
37093
|
onFatalError,
|
|
36180
|
-
isVeryLowEfficiency = false,
|
|
36181
37094
|
legend,
|
|
36182
37095
|
cropping,
|
|
36183
37096
|
canvasFps = 30,
|
|
@@ -36249,11 +37162,6 @@ var VideoCard = React144__namespace.default.memo(({
|
|
|
36249
37162
|
}
|
|
36250
37163
|
},
|
|
36251
37164
|
children: [
|
|
36252
|
-
isVeryLowEfficiency && /* @__PURE__ */ jsxRuntime.jsx("div", { className: `absolute ${compact ? "top-0.5 left-1" : "top-1 left-2"} z-30`, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
|
|
36253
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute -inset-1 bg-red-400/50 rounded-full blur-sm animate-pulse" }),
|
|
36254
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute -inset-0.5 bg-red-500/30 rounded-full blur-md animate-ping [animation-duration:1.5s]" }),
|
|
36255
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: `bg-[#E34329] ${compact ? "w-4 h-4" : "w-5 h-5"} rounded-full flex items-center justify-center text-white font-bold ${compact ? "text-[10px]" : "text-xs"} shadow-lg ring-2 ring-red-400/40 border border-red-400/80 animate-pulse`, children: "!" })
|
|
36256
|
-
] }) }),
|
|
36257
37165
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full h-full overflow-hidden bg-black", children: [
|
|
36258
37166
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-black z-0", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "animate-pulse flex flex-col items-center", children: [
|
|
36259
37167
|
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Camera, { className: `w-5 h-5 sm:${compact ? "w-4 h-4" : "w-6 h-6"} text-gray-500` }),
|
|
@@ -36663,7 +37571,6 @@ var VideoGridView = React144__namespace.default.memo(({
|
|
|
36663
37571
|
const workspaceId = workspace.workspace_uuid || workspace.workspace_name;
|
|
36664
37572
|
const workspaceKey = `${workspace.line_id || "unknown"}-${workspaceId}`;
|
|
36665
37573
|
const isVisible = visibleWorkspaces.has(workspaceId);
|
|
36666
|
-
const isVeryLowEfficiency = workspace.show_exclamation ?? (workspace.efficiency < 50 && workspace.efficiency >= 10);
|
|
36667
37574
|
const workspaceCropping = getWorkspaceCropping(workspaceId, workspace.workspace_name);
|
|
36668
37575
|
const workspaceStream = videoStreamsByWorkspaceId?.[workspaceId];
|
|
36669
37576
|
const lastSeenLabel = workspace.workspace_uuid ? lastSeenByWorkspaceId[workspace.workspace_uuid]?.timeSinceLastUpdate : void 0;
|
|
@@ -36680,7 +37587,6 @@ var VideoGridView = React144__namespace.default.memo(({
|
|
|
36680
37587
|
workspaceId,
|
|
36681
37588
|
workspaceKey,
|
|
36682
37589
|
isVisible,
|
|
36683
|
-
isVeryLowEfficiency,
|
|
36684
37590
|
workspaceCropping,
|
|
36685
37591
|
fallbackUrl,
|
|
36686
37592
|
hlsUrl,
|
|
@@ -36728,7 +37634,6 @@ var VideoGridView = React144__namespace.default.memo(({
|
|
|
36728
37634
|
isR2Stream: card.isR2Stream,
|
|
36729
37635
|
fallbackUrl: card.fallbackUrl
|
|
36730
37636
|
}),
|
|
36731
|
-
isVeryLowEfficiency: card.isVeryLowEfficiency,
|
|
36732
37637
|
legend: effectiveLegend,
|
|
36733
37638
|
cropping: card.workspaceCropping,
|
|
36734
37639
|
canvasFps: effectiveCanvasFps,
|
|
@@ -37648,223 +38553,6 @@ var UptimeLineChartComponent = ({ points, className = "" }) => {
|
|
|
37648
38553
|
] }) }) });
|
|
37649
38554
|
};
|
|
37650
38555
|
var UptimeLineChart = React144__namespace.default.memo(UptimeLineChartComponent);
|
|
37651
|
-
var padTime = (value) => value.toString().padStart(2, "0");
|
|
37652
|
-
var parseTime = (timeValue) => {
|
|
37653
|
-
if (!timeValue) return null;
|
|
37654
|
-
const [hourPart, minutePart] = timeValue.split(":");
|
|
37655
|
-
const hour = Number.parseInt(hourPart, 10);
|
|
37656
|
-
const minute = Number.parseInt(minutePart ?? "0", 10);
|
|
37657
|
-
if (!Number.isFinite(hour) || !Number.isFinite(minute)) return null;
|
|
37658
|
-
return { hour, minute };
|
|
37659
|
-
};
|
|
37660
|
-
var normalizeIdleTimeHourly = (idleTimeHourly) => {
|
|
37661
|
-
if (!idleTimeHourly || typeof idleTimeHourly !== "object") {
|
|
37662
|
-
return {};
|
|
37663
|
-
}
|
|
37664
|
-
return Object.fromEntries(
|
|
37665
|
-
Object.entries(idleTimeHourly).map(([key, value]) => {
|
|
37666
|
-
if (Array.isArray(value)) return [key, value];
|
|
37667
|
-
if (value && Array.isArray(value.values)) {
|
|
37668
|
-
return [key, value.values];
|
|
37669
|
-
}
|
|
37670
|
-
return [key, []];
|
|
37671
|
-
})
|
|
37672
|
-
);
|
|
37673
|
-
};
|
|
37674
|
-
var interpretIdleValue = (value) => {
|
|
37675
|
-
if (value === 1 || value === "1") return "idle";
|
|
37676
|
-
if (value === 0 || value === "0") return "active";
|
|
37677
|
-
if (value === "x" || value === null || value === void 0) return "unknown";
|
|
37678
|
-
return "unknown";
|
|
37679
|
-
};
|
|
37680
|
-
var getShiftDurationMinutes = (shiftStart, shiftEnd) => {
|
|
37681
|
-
const start = parseTime(shiftStart);
|
|
37682
|
-
const end = parseTime(shiftEnd);
|
|
37683
|
-
if (!start || !end) return null;
|
|
37684
|
-
let duration = end.hour * 60 + end.minute - (start.hour * 60 + start.minute);
|
|
37685
|
-
if (duration <= 0) {
|
|
37686
|
-
duration += 24 * 60;
|
|
37687
|
-
}
|
|
37688
|
-
return duration > 0 ? duration : null;
|
|
37689
|
-
};
|
|
37690
|
-
var getShiftElapsedMinutes = ({
|
|
37691
|
-
shiftStart,
|
|
37692
|
-
shiftEnd,
|
|
37693
|
-
shiftDate,
|
|
37694
|
-
timezone,
|
|
37695
|
-
now: now4 = /* @__PURE__ */ new Date()
|
|
37696
|
-
}) => {
|
|
37697
|
-
if (!shiftDate || !timezone) return null;
|
|
37698
|
-
const startTime = parseTime(shiftStart);
|
|
37699
|
-
const endTime = parseTime(shiftEnd);
|
|
37700
|
-
if (!startTime || !endTime) return null;
|
|
37701
|
-
const shiftStartDate = dateFnsTz.fromZonedTime(
|
|
37702
|
-
`${shiftDate}T${padTime(startTime.hour)}:${padTime(startTime.minute)}:00`,
|
|
37703
|
-
timezone
|
|
37704
|
-
);
|
|
37705
|
-
let shiftEndDate = dateFnsTz.fromZonedTime(
|
|
37706
|
-
`${shiftDate}T${padTime(endTime.hour)}:${padTime(endTime.minute)}:00`,
|
|
37707
|
-
timezone
|
|
37708
|
-
);
|
|
37709
|
-
if (shiftEndDate <= shiftStartDate) {
|
|
37710
|
-
shiftEndDate = dateFns.addDays(shiftEndDate, 1);
|
|
37711
|
-
}
|
|
37712
|
-
const shiftMinutes = Math.max(dateFns.differenceInMinutes(shiftEndDate, shiftStartDate), 0);
|
|
37713
|
-
if (shiftMinutes <= 0) return null;
|
|
37714
|
-
const elapsed = dateFns.differenceInMinutes(now4, shiftStartDate);
|
|
37715
|
-
return Math.min(Math.max(elapsed, 0), shiftMinutes);
|
|
37716
|
-
};
|
|
37717
|
-
var maskFutureHourlySeries = ({
|
|
37718
|
-
data,
|
|
37719
|
-
shiftStart,
|
|
37720
|
-
shiftEnd,
|
|
37721
|
-
shiftDate,
|
|
37722
|
-
timezone,
|
|
37723
|
-
now: now4 = /* @__PURE__ */ new Date()
|
|
37724
|
-
}) => {
|
|
37725
|
-
if (!Array.isArray(data)) {
|
|
37726
|
-
return [];
|
|
37727
|
-
}
|
|
37728
|
-
const normalizedData = data.map((value) => typeof value === "number" && Number.isFinite(value) ? value : null);
|
|
37729
|
-
if (!normalizedData.length) {
|
|
37730
|
-
return normalizedData;
|
|
37731
|
-
}
|
|
37732
|
-
const shiftMinutes = getShiftDurationMinutes(shiftStart, shiftEnd);
|
|
37733
|
-
const elapsedMinutes = getShiftElapsedMinutes({
|
|
37734
|
-
shiftStart,
|
|
37735
|
-
shiftEnd,
|
|
37736
|
-
shiftDate,
|
|
37737
|
-
timezone,
|
|
37738
|
-
now: now4
|
|
37739
|
-
});
|
|
37740
|
-
if (shiftMinutes === null || elapsedMinutes === null || elapsedMinutes >= shiftMinutes) {
|
|
37741
|
-
return normalizedData;
|
|
37742
|
-
}
|
|
37743
|
-
return normalizedData.map((value, index) => {
|
|
37744
|
-
const slotStartMinutes = index * 60;
|
|
37745
|
-
return slotStartMinutes > elapsedMinutes ? null : value;
|
|
37746
|
-
});
|
|
37747
|
-
};
|
|
37748
|
-
var buildUptimeSeries = ({
|
|
37749
|
-
idleTimeHourly,
|
|
37750
|
-
shiftStart,
|
|
37751
|
-
shiftEnd,
|
|
37752
|
-
shiftDate,
|
|
37753
|
-
timezone,
|
|
37754
|
-
elapsedMinutes
|
|
37755
|
-
}) => {
|
|
37756
|
-
const normalizedIdle = normalizeIdleTimeHourly(idleTimeHourly || {});
|
|
37757
|
-
const hasIdleData = Object.keys(normalizedIdle).length > 0;
|
|
37758
|
-
if (!hasIdleData || !shiftDate || !timezone) {
|
|
37759
|
-
return {
|
|
37760
|
-
points: [],
|
|
37761
|
-
activeMinutes: 0,
|
|
37762
|
-
idleMinutes: 0,
|
|
37763
|
-
availableMinutes: 0,
|
|
37764
|
-
shiftMinutes: 0,
|
|
37765
|
-
elapsedMinutes: 0,
|
|
37766
|
-
hasData: false
|
|
37767
|
-
};
|
|
37768
|
-
}
|
|
37769
|
-
const startTime = parseTime(shiftStart);
|
|
37770
|
-
const endTime = parseTime(shiftEnd);
|
|
37771
|
-
if (!startTime || !endTime) {
|
|
37772
|
-
return {
|
|
37773
|
-
points: [],
|
|
37774
|
-
activeMinutes: 0,
|
|
37775
|
-
idleMinutes: 0,
|
|
37776
|
-
availableMinutes: 0,
|
|
37777
|
-
shiftMinutes: 0,
|
|
37778
|
-
elapsedMinutes: 0,
|
|
37779
|
-
hasData: false
|
|
37780
|
-
};
|
|
37781
|
-
}
|
|
37782
|
-
const shiftStartDate = dateFnsTz.fromZonedTime(
|
|
37783
|
-
`${shiftDate}T${padTime(startTime.hour)}:${padTime(startTime.minute)}:00`,
|
|
37784
|
-
timezone
|
|
37785
|
-
);
|
|
37786
|
-
let shiftEndDate = dateFnsTz.fromZonedTime(
|
|
37787
|
-
`${shiftDate}T${padTime(endTime.hour)}:${padTime(endTime.minute)}:00`,
|
|
37788
|
-
timezone
|
|
37789
|
-
);
|
|
37790
|
-
if (shiftEndDate <= shiftStartDate) {
|
|
37791
|
-
shiftEndDate = dateFns.addDays(shiftEndDate, 1);
|
|
37792
|
-
}
|
|
37793
|
-
const shiftMinutes = Math.max(dateFns.differenceInMinutes(shiftEndDate, shiftStartDate), 0);
|
|
37794
|
-
if (shiftMinutes <= 0) {
|
|
37795
|
-
return {
|
|
37796
|
-
points: [],
|
|
37797
|
-
activeMinutes: 0,
|
|
37798
|
-
idleMinutes: 0,
|
|
37799
|
-
availableMinutes: 0,
|
|
37800
|
-
shiftMinutes: 0,
|
|
37801
|
-
elapsedMinutes: 0,
|
|
37802
|
-
hasData: false
|
|
37803
|
-
};
|
|
37804
|
-
}
|
|
37805
|
-
const elapsedMinutesClamped = Number.isFinite(elapsedMinutes) ? Math.min(Math.max(Math.floor(elapsedMinutes ?? 0), 0), shiftMinutes) : shiftMinutes;
|
|
37806
|
-
const points = [];
|
|
37807
|
-
let activeMinutes = 0;
|
|
37808
|
-
let idleMinutes = 0;
|
|
37809
|
-
for (let minuteIndex = 0; minuteIndex < shiftMinutes; minuteIndex += 1) {
|
|
37810
|
-
const minuteDate = dateFns.addMinutes(shiftStartDate, minuteIndex);
|
|
37811
|
-
const timeLabel = dateFnsTz.formatInTimeZone(minuteDate, timezone, "h:mm a");
|
|
37812
|
-
if (minuteIndex >= elapsedMinutesClamped) {
|
|
37813
|
-
points.push({
|
|
37814
|
-
minuteIndex,
|
|
37815
|
-
timeLabel,
|
|
37816
|
-
uptime: null,
|
|
37817
|
-
status: "unknown"
|
|
37818
|
-
});
|
|
37819
|
-
continue;
|
|
37820
|
-
}
|
|
37821
|
-
const hourKey = dateFnsTz.formatInTimeZone(minuteDate, timezone, "H");
|
|
37822
|
-
const minuteKey = Number.parseInt(dateFnsTz.formatInTimeZone(minuteDate, timezone, "m"), 10);
|
|
37823
|
-
const hourBucket = normalizedIdle[hourKey] || [];
|
|
37824
|
-
const value = Array.isArray(hourBucket) ? hourBucket[minuteKey] : void 0;
|
|
37825
|
-
const status = interpretIdleValue(value);
|
|
37826
|
-
if (status === "active") activeMinutes += 1;
|
|
37827
|
-
if (status === "idle") idleMinutes += 1;
|
|
37828
|
-
points.push({
|
|
37829
|
-
minuteIndex,
|
|
37830
|
-
timeLabel,
|
|
37831
|
-
uptime: status === "active" ? 1 : status === "idle" ? 0 : null,
|
|
37832
|
-
status
|
|
37833
|
-
});
|
|
37834
|
-
}
|
|
37835
|
-
return {
|
|
37836
|
-
points,
|
|
37837
|
-
activeMinutes,
|
|
37838
|
-
idleMinutes,
|
|
37839
|
-
availableMinutes: activeMinutes + idleMinutes,
|
|
37840
|
-
shiftMinutes,
|
|
37841
|
-
elapsedMinutes: elapsedMinutesClamped,
|
|
37842
|
-
hasData: activeMinutes + idleMinutes > 0
|
|
37843
|
-
};
|
|
37844
|
-
};
|
|
37845
|
-
var getUptimeUtilizationPercent = (shift) => {
|
|
37846
|
-
const efficiency = shift.efficiency;
|
|
37847
|
-
if (Number.isFinite(efficiency)) {
|
|
37848
|
-
return Math.round(Math.max(0, Math.min(100, Number(efficiency))));
|
|
37849
|
-
}
|
|
37850
|
-
const idleSeconds = Number.isFinite(shift.idleTime) ? Number(shift.idleTime) : 0;
|
|
37851
|
-
const activeSeconds = Number.isFinite(shift.activeTimeSeconds) ? Number(shift.activeTimeSeconds) : null;
|
|
37852
|
-
let availableSeconds = Number.isFinite(shift.availableTimeSeconds) ? Number(shift.availableTimeSeconds) : null;
|
|
37853
|
-
if (availableSeconds === null) {
|
|
37854
|
-
if ((activeSeconds ?? 0) > 0 || idleSeconds > 0) {
|
|
37855
|
-
availableSeconds = (activeSeconds ?? 0) + idleSeconds;
|
|
37856
|
-
} else {
|
|
37857
|
-
return 0;
|
|
37858
|
-
}
|
|
37859
|
-
}
|
|
37860
|
-
if (availableSeconds <= 0) return 0;
|
|
37861
|
-
const clampedIdleSeconds = Math.min(Math.max(idleSeconds, 0), availableSeconds);
|
|
37862
|
-
const productiveSeconds = Math.max(
|
|
37863
|
-
activeSeconds ?? availableSeconds - clampedIdleSeconds,
|
|
37864
|
-
0
|
|
37865
|
-
);
|
|
37866
|
-
return Math.round(productiveSeconds / availableSeconds * 100);
|
|
37867
|
-
};
|
|
37868
38556
|
var getTimeFromTimeString = (timeStr) => {
|
|
37869
38557
|
if (!timeStr) {
|
|
37870
38558
|
return { hour: 0, minute: 0, decimalHour: 0 };
|
|
@@ -49930,261 +50618,6 @@ Underperforming Workspaces: ${lineInfo.metrics.underperforming_workspaces} / ${l
|
|
|
49930
50618
|
}
|
|
49931
50619
|
);
|
|
49932
50620
|
};
|
|
49933
|
-
|
|
49934
|
-
// src/lib/utils/hourlyTargets.ts
|
|
49935
|
-
var stripSeconds2 = (timeStr) => timeStr ? timeStr.slice(0, 5) : timeStr;
|
|
49936
|
-
var MINUTES_PER_DAY = 24 * 60;
|
|
49937
|
-
var parseTimeToMinutes2 = (timeString) => {
|
|
49938
|
-
const normalized = stripSeconds2(timeString || "");
|
|
49939
|
-
if (!normalized || !/^[0-2]\d:[0-5]\d$/.test(normalized)) return Number.NaN;
|
|
49940
|
-
const [hours, minutes] = normalized.split(":").map(Number);
|
|
49941
|
-
return hours * 60 + minutes;
|
|
49942
|
-
};
|
|
49943
|
-
var normalizeBreaksOnShiftTimeline = (shiftStart, breaks) => {
|
|
49944
|
-
const shiftStartMinutes = parseTimeToMinutes2(shiftStart);
|
|
49945
|
-
if (!Number.isFinite(shiftStartMinutes)) return [];
|
|
49946
|
-
const normalizedBreaks = [];
|
|
49947
|
-
for (const entry of breaks) {
|
|
49948
|
-
const startRaw = parseTimeToMinutes2(entry.startTime);
|
|
49949
|
-
const endRaw = parseTimeToMinutes2(entry.endTime);
|
|
49950
|
-
if (!Number.isFinite(startRaw) || !Number.isFinite(endRaw)) continue;
|
|
49951
|
-
let start = startRaw;
|
|
49952
|
-
let end = endRaw;
|
|
49953
|
-
if (end <= start) {
|
|
49954
|
-
end += 24 * 60;
|
|
49955
|
-
}
|
|
49956
|
-
if (start < shiftStartMinutes) {
|
|
49957
|
-
start += 24 * 60;
|
|
49958
|
-
end += 24 * 60;
|
|
49959
|
-
}
|
|
49960
|
-
const label = entry.remarks?.trim() || "Break";
|
|
49961
|
-
normalizedBreaks.push({ start, end, label });
|
|
49962
|
-
}
|
|
49963
|
-
return normalizedBreaks;
|
|
49964
|
-
};
|
|
49965
|
-
var roundTarget = (value, mode) => {
|
|
49966
|
-
if (!Number.isFinite(value)) return 0;
|
|
49967
|
-
switch (mode) {
|
|
49968
|
-
case "floor":
|
|
49969
|
-
return Math.floor(value);
|
|
49970
|
-
case "ceil":
|
|
49971
|
-
return Math.ceil(value);
|
|
49972
|
-
case "round":
|
|
49973
|
-
default:
|
|
49974
|
-
return Math.round(value);
|
|
49975
|
-
}
|
|
49976
|
-
};
|
|
49977
|
-
var formatDateKey = (date) => {
|
|
49978
|
-
const year = date.getUTCFullYear();
|
|
49979
|
-
const month = `${date.getUTCMonth() + 1}`.padStart(2, "0");
|
|
49980
|
-
const day = `${date.getUTCDate()}`.padStart(2, "0");
|
|
49981
|
-
return `${year}-${month}-${day}`;
|
|
49982
|
-
};
|
|
49983
|
-
var shiftDateKey = (dateKey, deltaDays) => {
|
|
49984
|
-
const [yearPart, monthPart, dayPart] = dateKey.split("-").map(Number);
|
|
49985
|
-
const year = Number.isFinite(yearPart) ? yearPart : 1970;
|
|
49986
|
-
const month = Number.isFinite(monthPart) ? monthPart : 1;
|
|
49987
|
-
const day = Number.isFinite(dayPart) ? dayPart : 1;
|
|
49988
|
-
const date = new Date(Date.UTC(year, month - 1, day));
|
|
49989
|
-
date.setUTCDate(date.getUTCDate() + deltaDays);
|
|
49990
|
-
return formatDateKey(date);
|
|
49991
|
-
};
|
|
49992
|
-
var getZonedNowSnapshot = (timeZone, now4) => {
|
|
49993
|
-
const formatter = new Intl.DateTimeFormat("en-US", {
|
|
49994
|
-
timeZone,
|
|
49995
|
-
year: "numeric",
|
|
49996
|
-
month: "2-digit",
|
|
49997
|
-
day: "2-digit",
|
|
49998
|
-
hour: "2-digit",
|
|
49999
|
-
minute: "2-digit",
|
|
50000
|
-
hourCycle: "h23"
|
|
50001
|
-
});
|
|
50002
|
-
const parts = formatter.formatToParts(now4).reduce((acc, part) => {
|
|
50003
|
-
if (part.type !== "literal") {
|
|
50004
|
-
acc[part.type] = part.value;
|
|
50005
|
-
}
|
|
50006
|
-
return acc;
|
|
50007
|
-
}, {});
|
|
50008
|
-
const year = Number(parts.year);
|
|
50009
|
-
const month = Number(parts.month);
|
|
50010
|
-
const day = Number(parts.day);
|
|
50011
|
-
const hour = Number(parts.hour);
|
|
50012
|
-
const minute = Number(parts.minute);
|
|
50013
|
-
return {
|
|
50014
|
-
dateKey: `${String(year).padStart(4, "0")}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`,
|
|
50015
|
-
minutesOfDay: (Number.isFinite(hour) ? hour : 0) * 60 + (Number.isFinite(minute) ? minute : 0)
|
|
50016
|
-
};
|
|
50017
|
-
};
|
|
50018
|
-
var getDateKeyInTimeZone = (timeZone, now4 = /* @__PURE__ */ new Date()) => getZonedNowSnapshot(timeZone, now4).dateKey;
|
|
50019
|
-
var buildHourlyIntervals = ({
|
|
50020
|
-
shiftStart,
|
|
50021
|
-
shiftEnd,
|
|
50022
|
-
bucketMinutes = 60,
|
|
50023
|
-
fallbackHours = 11
|
|
50024
|
-
}) => {
|
|
50025
|
-
const startMinutes = parseTimeToMinutes2(shiftStart);
|
|
50026
|
-
if (!Number.isFinite(startMinutes)) return [];
|
|
50027
|
-
const bucket = Number.isFinite(bucketMinutes) && bucketMinutes > 0 ? Math.floor(bucketMinutes) : 60;
|
|
50028
|
-
let totalMinutes;
|
|
50029
|
-
const endRaw = shiftEnd ? parseTimeToMinutes2(shiftEnd) : Number.NaN;
|
|
50030
|
-
if (!Number.isFinite(endRaw)) {
|
|
50031
|
-
totalMinutes = Math.max(0, Math.round(fallbackHours * 60));
|
|
50032
|
-
} else {
|
|
50033
|
-
let endMinutes = endRaw;
|
|
50034
|
-
if (endMinutes <= startMinutes) {
|
|
50035
|
-
endMinutes += 24 * 60;
|
|
50036
|
-
}
|
|
50037
|
-
totalMinutes = endMinutes - startMinutes;
|
|
50038
|
-
}
|
|
50039
|
-
if (!Number.isFinite(totalMinutes) || totalMinutes <= 0) return [];
|
|
50040
|
-
const count = Math.ceil(totalMinutes / bucket);
|
|
50041
|
-
const shiftEndMinutes = startMinutes + totalMinutes;
|
|
50042
|
-
const intervals = [];
|
|
50043
|
-
for (let i = 0; i < count; i += 1) {
|
|
50044
|
-
const start = startMinutes + i * bucket;
|
|
50045
|
-
const end = Math.min(start + bucket, shiftEndMinutes);
|
|
50046
|
-
const minutes = Math.max(0, end - start);
|
|
50047
|
-
if (minutes <= 0) continue;
|
|
50048
|
-
intervals.push({ start, end, minutes });
|
|
50049
|
-
}
|
|
50050
|
-
return intervals;
|
|
50051
|
-
};
|
|
50052
|
-
var computeBreakMinutesByInterval = ({
|
|
50053
|
-
intervals,
|
|
50054
|
-
shiftStart,
|
|
50055
|
-
breaks
|
|
50056
|
-
}) => {
|
|
50057
|
-
if (!intervals.length || !breaks.length) return intervals.map(() => 0);
|
|
50058
|
-
const normalizedBreaks = normalizeBreaksOnShiftTimeline(shiftStart, breaks);
|
|
50059
|
-
return intervals.map((interval) => {
|
|
50060
|
-
if (!normalizedBreaks.length) return 0;
|
|
50061
|
-
let total = 0;
|
|
50062
|
-
for (const brk of normalizedBreaks) {
|
|
50063
|
-
const overlap = Math.max(0, Math.min(interval.end, brk.end) - Math.max(interval.start, brk.start));
|
|
50064
|
-
total += overlap;
|
|
50065
|
-
if (total >= interval.minutes) return interval.minutes;
|
|
50066
|
-
}
|
|
50067
|
-
return Math.min(interval.minutes, total);
|
|
50068
|
-
});
|
|
50069
|
-
};
|
|
50070
|
-
var computeBreakRemarksByInterval = ({
|
|
50071
|
-
intervals,
|
|
50072
|
-
shiftStart,
|
|
50073
|
-
breaks
|
|
50074
|
-
}) => {
|
|
50075
|
-
if (!intervals.length || !breaks.length) return intervals.map(() => "");
|
|
50076
|
-
const normalizedBreaks = normalizeBreaksOnShiftTimeline(shiftStart, breaks);
|
|
50077
|
-
return intervals.map((interval) => {
|
|
50078
|
-
const labels = normalizedBreaks.filter((brk) => Math.max(0, Math.min(interval.end, brk.end) - Math.max(interval.start, brk.start)) > 0).map((brk) => brk.label).filter((label, index, values) => label && values.indexOf(label) === index);
|
|
50079
|
-
return labels.join(", ");
|
|
50080
|
-
});
|
|
50081
|
-
};
|
|
50082
|
-
var computeEffectiveTargets = ({
|
|
50083
|
-
intervals,
|
|
50084
|
-
breakMinutes,
|
|
50085
|
-
pphThreshold,
|
|
50086
|
-
rounding = "round"
|
|
50087
|
-
}) => {
|
|
50088
|
-
return intervals.map((interval, idx) => {
|
|
50089
|
-
const intervalMinutes = Number(interval?.minutes) || 0;
|
|
50090
|
-
const breakMins = Number(breakMinutes?.[idx]) || 0;
|
|
50091
|
-
const plannedWorkMinutes = Math.max(0, intervalMinutes - breakMins);
|
|
50092
|
-
if (!Number.isFinite(pphThreshold) || pphThreshold <= 0) return 0;
|
|
50093
|
-
if (plannedWorkMinutes <= 0) return 0;
|
|
50094
|
-
return roundTarget(pphThreshold * plannedWorkMinutes / 60, rounding);
|
|
50095
|
-
});
|
|
50096
|
-
};
|
|
50097
|
-
var buildHourlyTargetPlan = ({
|
|
50098
|
-
shiftStart,
|
|
50099
|
-
shiftEnd,
|
|
50100
|
-
breaks = [],
|
|
50101
|
-
pphThreshold,
|
|
50102
|
-
bucketMinutes = 60,
|
|
50103
|
-
fallbackHours = 11,
|
|
50104
|
-
rounding = "round"
|
|
50105
|
-
}) => {
|
|
50106
|
-
const intervals = buildHourlyIntervals({
|
|
50107
|
-
shiftStart,
|
|
50108
|
-
shiftEnd,
|
|
50109
|
-
bucketMinutes,
|
|
50110
|
-
fallbackHours
|
|
50111
|
-
});
|
|
50112
|
-
const breakMinutes = computeBreakMinutesByInterval({
|
|
50113
|
-
intervals,
|
|
50114
|
-
shiftStart,
|
|
50115
|
-
breaks
|
|
50116
|
-
});
|
|
50117
|
-
const breakRemarks = computeBreakRemarksByInterval({
|
|
50118
|
-
intervals,
|
|
50119
|
-
shiftStart,
|
|
50120
|
-
breaks
|
|
50121
|
-
});
|
|
50122
|
-
const productiveMinutes = intervals.map((interval, idx) => Math.max(0, (Number(interval?.minutes) || 0) - (Number(breakMinutes[idx]) || 0)));
|
|
50123
|
-
const targets = computeEffectiveTargets({
|
|
50124
|
-
intervals,
|
|
50125
|
-
breakMinutes,
|
|
50126
|
-
pphThreshold,
|
|
50127
|
-
rounding
|
|
50128
|
-
});
|
|
50129
|
-
return {
|
|
50130
|
-
intervals,
|
|
50131
|
-
breakMinutes,
|
|
50132
|
-
breakRemarks,
|
|
50133
|
-
productiveMinutes,
|
|
50134
|
-
targets
|
|
50135
|
-
};
|
|
50136
|
-
};
|
|
50137
|
-
var isHourlyIntervalComplete = ({
|
|
50138
|
-
reportDate,
|
|
50139
|
-
shiftStart,
|
|
50140
|
-
shiftEnd,
|
|
50141
|
-
interval,
|
|
50142
|
-
timeZone = "Asia/Kolkata",
|
|
50143
|
-
now: now4 = /* @__PURE__ */ new Date()
|
|
50144
|
-
}) => {
|
|
50145
|
-
if (!reportDate) return true;
|
|
50146
|
-
const snapshot = getZonedNowSnapshot(timeZone, now4);
|
|
50147
|
-
const shiftStartMinutes = parseTimeToMinutes2(shiftStart);
|
|
50148
|
-
const shiftEndMinutes = shiftEnd ? parseTimeToMinutes2(shiftEnd) : Number.NaN;
|
|
50149
|
-
const wrapsMidnight = Number.isFinite(shiftStartMinutes) && Number.isFinite(shiftEndMinutes) && shiftEndMinutes <= shiftStartMinutes;
|
|
50150
|
-
if (reportDate === snapshot.dateKey) {
|
|
50151
|
-
return interval.end <= snapshot.minutesOfDay;
|
|
50152
|
-
}
|
|
50153
|
-
if (wrapsMidnight && reportDate === shiftDateKey(snapshot.dateKey, -1)) {
|
|
50154
|
-
return interval.end <= snapshot.minutesOfDay + MINUTES_PER_DAY;
|
|
50155
|
-
}
|
|
50156
|
-
return reportDate < snapshot.dateKey;
|
|
50157
|
-
};
|
|
50158
|
-
var isShiftInProgressForReportDate = ({
|
|
50159
|
-
reportDate,
|
|
50160
|
-
shiftStart,
|
|
50161
|
-
shiftEnd,
|
|
50162
|
-
timeZone = "Asia/Kolkata",
|
|
50163
|
-
now: now4 = /* @__PURE__ */ new Date()
|
|
50164
|
-
}) => {
|
|
50165
|
-
if (!reportDate || !shiftStart || !shiftEnd) return false;
|
|
50166
|
-
const shiftStartMinutes = parseTimeToMinutes2(shiftStart);
|
|
50167
|
-
const shiftEndMinutesRaw = parseTimeToMinutes2(shiftEnd);
|
|
50168
|
-
if (!Number.isFinite(shiftStartMinutes) || !Number.isFinite(shiftEndMinutesRaw)) {
|
|
50169
|
-
return false;
|
|
50170
|
-
}
|
|
50171
|
-
let shiftEndMinutes = shiftEndMinutesRaw;
|
|
50172
|
-
const wrapsMidnight = shiftEndMinutes <= shiftStartMinutes;
|
|
50173
|
-
if (wrapsMidnight) {
|
|
50174
|
-
shiftEndMinutes += MINUTES_PER_DAY;
|
|
50175
|
-
}
|
|
50176
|
-
const snapshot = getZonedNowSnapshot(timeZone, now4);
|
|
50177
|
-
let currentMinutes = null;
|
|
50178
|
-
if (reportDate === snapshot.dateKey) {
|
|
50179
|
-
currentMinutes = snapshot.minutesOfDay;
|
|
50180
|
-
} else if (wrapsMidnight && reportDate === shiftDateKey(snapshot.dateKey, -1)) {
|
|
50181
|
-
currentMinutes = snapshot.minutesOfDay + MINUTES_PER_DAY;
|
|
50182
|
-
}
|
|
50183
|
-
if (currentMinutes === null) {
|
|
50184
|
-
return false;
|
|
50185
|
-
}
|
|
50186
|
-
return shiftStartMinutes <= currentMinutes && currentMinutes < shiftEndMinutes;
|
|
50187
|
-
};
|
|
50188
50621
|
var formatOperationalDateKey = (dateKey, options) => {
|
|
50189
50622
|
const [yearPart, monthPart, dayPart] = dateKey.split("-").map(Number);
|
|
50190
50623
|
const year = Number.isFinite(yearPart) ? yearPart : 1970;
|
|
@@ -50195,7 +50628,7 @@ var formatOperationalDateKey = (dateKey, options) => {
|
|
|
50195
50628
|
timeZone: "UTC"
|
|
50196
50629
|
});
|
|
50197
50630
|
};
|
|
50198
|
-
var
|
|
50631
|
+
var getSegmentMinutes2 = (isoString, timeZone, reportDate) => {
|
|
50199
50632
|
const date = new Date(isoString);
|
|
50200
50633
|
if (isNaN(date.getTime())) return NaN;
|
|
50201
50634
|
const formatter = new Intl.DateTimeFormat("en-US", {
|
|
@@ -50216,13 +50649,13 @@ var getSegmentMinutes = (isoString, timeZone, reportDate) => {
|
|
|
50216
50649
|
if (dateKey > reportDate) {
|
|
50217
50650
|
const d1 = new Date(dateKey);
|
|
50218
50651
|
const d2 = new Date(reportDate);
|
|
50219
|
-
const
|
|
50220
|
-
minutes +=
|
|
50652
|
+
const diffDays2 = Math.round((d1.getTime() - d2.getTime()) / (1e3 * 3600 * 24));
|
|
50653
|
+
minutes += diffDays2 * 24 * 60;
|
|
50221
50654
|
} else if (dateKey < reportDate) {
|
|
50222
50655
|
const d1 = new Date(dateKey);
|
|
50223
50656
|
const d2 = new Date(reportDate);
|
|
50224
|
-
const
|
|
50225
|
-
minutes -=
|
|
50657
|
+
const diffDays2 = Math.round((d2.getTime() - d1.getTime()) / (1e3 * 3600 * 24));
|
|
50658
|
+
minutes -= diffDays2 * 24 * 60;
|
|
50226
50659
|
}
|
|
50227
50660
|
return minutes;
|
|
50228
50661
|
};
|
|
@@ -50632,7 +51065,7 @@ var LinePdfGenerator = ({
|
|
|
50632
51065
|
const skuRemarksByIndex = {};
|
|
50633
51066
|
if (lineInfo.metrics.sku_segments && lineInfo.metrics.sku_segments.length > 0) {
|
|
50634
51067
|
lineInfo.metrics.sku_segments.forEach((segment, segmentIndex) => {
|
|
50635
|
-
const segmentMinutes =
|
|
51068
|
+
const segmentMinutes = getSegmentMinutes2(segment.start_time, reportTimezone, lineInfo.date);
|
|
50636
51069
|
if (!isNaN(segmentMinutes)) {
|
|
50637
51070
|
const intervalIndex = hourlyTimeRanges.findIndex(
|
|
50638
51071
|
(inv) => segmentMinutes >= inv.start && segmentMinutes < inv.end
|
|
@@ -52367,7 +52800,7 @@ var formatOperationalDateKey2 = (dateKey, options) => {
|
|
|
52367
52800
|
timeZone: "UTC"
|
|
52368
52801
|
});
|
|
52369
52802
|
};
|
|
52370
|
-
var
|
|
52803
|
+
var getSegmentMinutes3 = (isoString, timeZone, reportDate) => {
|
|
52371
52804
|
const date = new Date(isoString);
|
|
52372
52805
|
if (isNaN(date.getTime())) return NaN;
|
|
52373
52806
|
const formatter = new Intl.DateTimeFormat("en-US", {
|
|
@@ -52388,13 +52821,13 @@ var getSegmentMinutes2 = (isoString, timeZone, reportDate) => {
|
|
|
52388
52821
|
if (dateKey > reportDate) {
|
|
52389
52822
|
const d1 = new Date(dateKey);
|
|
52390
52823
|
const d2 = new Date(reportDate);
|
|
52391
|
-
const
|
|
52392
|
-
minutes +=
|
|
52824
|
+
const diffDays2 = Math.round((d1.getTime() - d2.getTime()) / (1e3 * 3600 * 24));
|
|
52825
|
+
minutes += diffDays2 * 24 * 60;
|
|
52393
52826
|
} else if (dateKey < reportDate) {
|
|
52394
52827
|
const d1 = new Date(dateKey);
|
|
52395
52828
|
const d2 = new Date(reportDate);
|
|
52396
|
-
const
|
|
52397
|
-
minutes -=
|
|
52829
|
+
const diffDays2 = Math.round((d2.getTime() - d1.getTime()) / (1e3 * 3600 * 24));
|
|
52830
|
+
minutes -= diffDays2 * 24 * 60;
|
|
52398
52831
|
}
|
|
52399
52832
|
return minutes;
|
|
52400
52833
|
};
|
|
@@ -52626,7 +53059,7 @@ var WorkspacePdfGenerator = ({
|
|
|
52626
53059
|
const skuRemarksByIndex = {};
|
|
52627
53060
|
if (workspace.sku_segments && workspace.sku_segments.length > 0) {
|
|
52628
53061
|
workspace.sku_segments.forEach((segment, segmentIndex) => {
|
|
52629
|
-
const segmentMinutes =
|
|
53062
|
+
const segmentMinutes = getSegmentMinutes3(segment.start_time, reportTimezone, workspace.date);
|
|
52630
53063
|
if (!isNaN(segmentMinutes)) {
|
|
52631
53064
|
const intervalIndex = hourlyIntervals.findIndex(
|
|
52632
53065
|
(inv) => segmentMinutes >= inv.start && segmentMinutes < inv.end
|
|
@@ -53468,12 +53901,10 @@ var formatPercentRange = (min, max) => {
|
|
|
53468
53901
|
return `${format10(min)}-${format10(max)}%`;
|
|
53469
53902
|
};
|
|
53470
53903
|
var Legend5 = ({
|
|
53471
|
-
useBottleneckLabel = false,
|
|
53472
53904
|
legend,
|
|
53473
53905
|
metricLabel = "Efficiency"
|
|
53474
53906
|
}) => {
|
|
53475
53907
|
const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;
|
|
53476
|
-
const exclamationLabel = useBottleneckLabel ? "Bottleneck" : "<50% efficiency";
|
|
53477
53908
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2 sm:gap-4 text-xs font-medium text-slate-600", children: [
|
|
53478
53909
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "font-medium text-gray-700 hidden sm:block", children: [
|
|
53479
53910
|
metricLabel,
|
|
@@ -53492,11 +53923,6 @@ var Legend5 = ({
|
|
|
53492
53923
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-2 h-2 sm:w-2.5 sm:h-2.5 rounded-full bg-[#E34329]" }),
|
|
53493
53924
|
/* @__PURE__ */ jsxRuntime.jsx("span", { children: formatPercentRange(effectiveLegend.red_min, effectiveLegend.red_max) })
|
|
53494
53925
|
] })
|
|
53495
|
-
] }),
|
|
53496
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden sm:block w-px h-4 bg-slate-200 mx-1" }),
|
|
53497
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
53498
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center w-3 h-3 sm:w-4 sm:h-4 bg-[#E34329] rounded-full text-white font-bold text-[8px] sm:text-[10px]", children: "!" }),
|
|
53499
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: exclamationLabel })
|
|
53500
53926
|
] })
|
|
53501
53927
|
] });
|
|
53502
53928
|
};
|
|
@@ -53610,7 +54036,6 @@ var WorkspaceGrid = React144__namespace.default.memo(({
|
|
|
53610
54036
|
factoryView = "factory",
|
|
53611
54037
|
line2Uuid = "line-2",
|
|
53612
54038
|
className = "",
|
|
53613
|
-
hasFlowBuffers = false,
|
|
53614
54039
|
legend = DEFAULT_EFFICIENCY_LEGEND,
|
|
53615
54040
|
videoSources = {},
|
|
53616
54041
|
videoStreamsByWorkspaceId = {},
|
|
@@ -53654,7 +54079,7 @@ var WorkspaceGrid = React144__namespace.default.memo(({
|
|
|
53654
54079
|
);
|
|
53655
54080
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex flex-col w-full h-full overflow-hidden bg-slate-50/50 ${className}`, children: [
|
|
53656
54081
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-none px-4 py-3 z-20 flex flex-row items-center justify-between gap-4", children: [
|
|
53657
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden sm:block", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "inline-flex bg-white/95 rounded-lg shadow-sm px-4 py-2 border border-slate-200/60 backdrop-blur-sm", children: /* @__PURE__ */ jsxRuntime.jsx(Legend5, { legend,
|
|
54082
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden sm:block", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "inline-flex bg-white/95 rounded-lg shadow-sm px-4 py-2 border border-slate-200/60 backdrop-blur-sm", children: /* @__PURE__ */ jsxRuntime.jsx(Legend5, { legend, metricLabel: legendMetricLabel }) }) }),
|
|
53658
54083
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-3 shrink-0", children: [
|
|
53659
54084
|
toolbarRightContent,
|
|
53660
54085
|
mapViewEnabled && /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -53674,7 +54099,7 @@ var WorkspaceGrid = React144__namespace.default.memo(({
|
|
|
53674
54099
|
)
|
|
53675
54100
|
] })
|
|
53676
54101
|
] }),
|
|
53677
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "sm:hidden px-3 py-2 bg-white border-b border-slate-200/60 z-10", children: /* @__PURE__ */ jsxRuntime.jsx(Legend5, { legend,
|
|
54102
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "sm:hidden px-3 py-2 bg-white border-b border-slate-200/60 z-10", children: /* @__PURE__ */ jsxRuntime.jsx(Legend5, { legend, metricLabel: legendMetricLabel }) }),
|
|
53678
54103
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 relative overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(AnimatePresence, { mode: "wait", children: viewMode === "video" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
53679
54104
|
motion.div,
|
|
53680
54105
|
{
|
|
@@ -64335,6 +64760,7 @@ var buildLineInfoSnapshot = (lineDetails, metrics2) => {
|
|
|
64335
64760
|
underperforming_workspace_uuids: metrics2.underperforming_workspace_uuids || [],
|
|
64336
64761
|
output_array: metrics2.output_array || [],
|
|
64337
64762
|
output_hourly: metrics2.output_hourly,
|
|
64763
|
+
hourly_target_output: metrics2.hourly_target_output,
|
|
64338
64764
|
line_threshold: metrics2.line_threshold ?? 0,
|
|
64339
64765
|
threshold_pph: metrics2.threshold_pph ?? 0,
|
|
64340
64766
|
shift_start: metrics2.shift_start || "06:00",
|
|
@@ -64421,6 +64847,7 @@ var transformLineMetrics = (lineId, detailResponse, queryDate, queryShiftId) =>
|
|
|
64421
64847
|
underperforming_workspace_names: [],
|
|
64422
64848
|
underperforming_workspace_uuids: [],
|
|
64423
64849
|
output_array: [],
|
|
64850
|
+
hourly_target_output: void 0,
|
|
64424
64851
|
line_threshold: 0,
|
|
64425
64852
|
threshold_pph: 0,
|
|
64426
64853
|
shift_start: "06:00",
|
|
@@ -65458,6 +65885,10 @@ var BottomSection = React144.memo(({
|
|
|
65458
65885
|
workspaceDisplayNames,
|
|
65459
65886
|
hourlyOutputData,
|
|
65460
65887
|
hourlyThreshold,
|
|
65888
|
+
hourlyTargetOutput,
|
|
65889
|
+
shiftBreaks,
|
|
65890
|
+
idleTimeHourly,
|
|
65891
|
+
timezone,
|
|
65461
65892
|
urlDate,
|
|
65462
65893
|
urlShift,
|
|
65463
65894
|
navigate,
|
|
@@ -65628,8 +66059,13 @@ var BottomSection = React144.memo(({
|
|
|
65628
66059
|
{
|
|
65629
66060
|
data: hourlyOutputData,
|
|
65630
66061
|
pphThreshold: hourlyThreshold,
|
|
66062
|
+
hourlyTargetOutput,
|
|
65631
66063
|
shiftStart: lineInfo.metrics.shift_start || "06:00",
|
|
65632
66064
|
shiftEnd: lineInfo.metrics.shift_end,
|
|
66065
|
+
shiftBreaks,
|
|
66066
|
+
idleTimeHourly,
|
|
66067
|
+
shiftDate: lineInfo.date,
|
|
66068
|
+
timezone,
|
|
65633
66069
|
skuSegments: skuAware ? skuSegments : void 0,
|
|
65634
66070
|
activeSkuId
|
|
65635
66071
|
}
|
|
@@ -65650,6 +66086,12 @@ var BottomSection = React144.memo(({
|
|
|
65650
66086
|
if (prevProps.lineInfo.monitoring_mode !== nextProps.lineInfo.monitoring_mode) return false;
|
|
65651
66087
|
if (prevProps.skuAware !== nextProps.skuAware) return false;
|
|
65652
66088
|
if (prevProps.activeSkuId !== nextProps.activeSkuId) return false;
|
|
66089
|
+
if (JSON.stringify(prevProps.shiftBreaks || []) !== JSON.stringify(nextProps.shiftBreaks || [])) {
|
|
66090
|
+
return false;
|
|
66091
|
+
}
|
|
66092
|
+
if (JSON.stringify(prevProps.hourlyTargetOutput || []) !== JSON.stringify(nextProps.hourlyTargetOutput || [])) {
|
|
66093
|
+
return false;
|
|
66094
|
+
}
|
|
65653
66095
|
const prevSkuSegmentsSignature = JSON.stringify(
|
|
65654
66096
|
(prevProps.skuSegments || []).map((segment) => ({
|
|
65655
66097
|
sku_id: segment.sku_id,
|
|
@@ -65722,7 +66164,9 @@ var BottomSection = React144.memo(({
|
|
|
65722
66164
|
);
|
|
65723
66165
|
if (prevWorkspaceSignature !== nextWorkspaceSignature) return false;
|
|
65724
66166
|
if (prevProps.lineInfo.metrics.shift_start !== nextProps.lineInfo.metrics.shift_start) return false;
|
|
66167
|
+
if (prevProps.lineInfo.metrics.shift_end !== nextProps.lineInfo.metrics.shift_end) return false;
|
|
65725
66168
|
if (prevProps.hourlyThreshold !== nextProps.hourlyThreshold) return false;
|
|
66169
|
+
if (JSON.stringify(prevProps.idleTimeHourly || {}) !== JSON.stringify(nextProps.idleTimeHourly || {})) return false;
|
|
65726
66170
|
if (prevProps.urlDate !== nextProps.urlDate || prevProps.urlShift !== nextProps.urlShift) return false;
|
|
65727
66171
|
if (prevProps.workspaceDisplayNames !== nextProps.workspaceDisplayNames) return false;
|
|
65728
66172
|
return true;
|
|
@@ -65990,6 +66434,7 @@ var KPIDetailView = ({
|
|
|
65990
66434
|
}
|
|
65991
66435
|
}, [urlDate, urlShift, urlTab]);
|
|
65992
66436
|
const { shiftConfig, isLoading: isShiftConfigLoading } = useDynamicShiftConfig(lineId);
|
|
66437
|
+
const lineTimezone = shiftConfig?.timezone || configuredTimezone;
|
|
65993
66438
|
const getShiftName = React144.useCallback((shiftId) => {
|
|
65994
66439
|
return getShiftNameById(shiftId, configuredTimezone, shiftConfig);
|
|
65995
66440
|
}, [configuredTimezone, shiftConfig]);
|
|
@@ -66030,12 +66475,12 @@ var KPIDetailView = ({
|
|
|
66030
66475
|
operationalTodayDate.setHours(0, 0, 0, 0);
|
|
66031
66476
|
compareDateInZone.setHours(0, 0, 0, 0);
|
|
66032
66477
|
const diffTime = compareDateInZone.getTime() - operationalTodayDate.getTime();
|
|
66033
|
-
const
|
|
66034
|
-
if (
|
|
66035
|
-
if (
|
|
66036
|
-
if (
|
|
66037
|
-
if (
|
|
66038
|
-
if (
|
|
66478
|
+
const diffDays2 = Math.round(diffTime / (1e3 * 60 * 60 * 24));
|
|
66479
|
+
if (diffDays2 === 0) return "Today";
|
|
66480
|
+
if (diffDays2 === -1) return "Yesterday";
|
|
66481
|
+
if (diffDays2 === 1) return "Tomorrow";
|
|
66482
|
+
if (diffDays2 < -1) return `${Math.abs(diffDays2)} days ago`;
|
|
66483
|
+
if (diffDays2 > 1) return `${diffDays2} days ahead`;
|
|
66039
66484
|
return "Today";
|
|
66040
66485
|
}, [configuredTimezone, shiftConfig]);
|
|
66041
66486
|
const {
|
|
@@ -66245,6 +66690,7 @@ var KPIDetailView = ({
|
|
|
66245
66690
|
underperforming_workspace_uuids: metrics2.underperforming_workspace_uuids || [],
|
|
66246
66691
|
output_array: metrics2.output_array || [],
|
|
66247
66692
|
output_hourly: metrics2.output_hourly,
|
|
66693
|
+
hourly_target_output: metrics2.hourly_target_output,
|
|
66248
66694
|
line_threshold: metrics2.line_threshold ?? 0,
|
|
66249
66695
|
threshold_pph: metrics2.threshold_pph ?? 0,
|
|
66250
66696
|
shift_start: metrics2.shift_start || "06:00",
|
|
@@ -66254,7 +66700,7 @@ var KPIDetailView = ({
|
|
|
66254
66700
|
idle_time_hourly: metrics2.idle_time_hourly || null,
|
|
66255
66701
|
// Multi-SKU additive fields (Phase 6) — propagated from
|
|
66256
66702
|
// `useLineDetailPageData`. Backend authoritative; we never recompute.
|
|
66257
|
-
// The
|
|
66703
|
+
// The output-card selection below uses these to swap header KPIs per selected SKU.
|
|
66258
66704
|
sku_aware: Boolean(metrics2.sku_aware),
|
|
66259
66705
|
real_sku_count: metrics2.real_sku_count ?? 0,
|
|
66260
66706
|
sku_breakdown: Array.isArray(metrics2.sku_breakdown) ? metrics2.sku_breakdown : [],
|
|
@@ -66351,7 +66797,7 @@ var KPIDetailView = ({
|
|
|
66351
66797
|
}, [lineSkuSegments, outputChartSkuBreakdown, hasUrlDate, hasUrlShift]);
|
|
66352
66798
|
const normalizedSelectedSkuId = selectedSkuId !== "all" ? selectedSkuId : null;
|
|
66353
66799
|
const isLineSkuAware = Boolean(resolvedLineInfo?.metrics.sku_aware);
|
|
66354
|
-
const showSkuSelector = isLineSkuAware && realSkuOptions.length > 0;
|
|
66800
|
+
const showSkuSelector = isLineSkuAware && realSkuOptions.length > 0 && !isUptimeMode;
|
|
66355
66801
|
React144.useEffect(() => {
|
|
66356
66802
|
if (selectedSkuId === "all") return;
|
|
66357
66803
|
const stillPresent = realSkuOptions.some((item) => item.sku_id === selectedSkuId);
|
|
@@ -66378,15 +66824,11 @@ var KPIDetailView = ({
|
|
|
66378
66824
|
...resolvedLineInfo.metrics,
|
|
66379
66825
|
current_output: selectedSkuRow.current_output ?? 0,
|
|
66380
66826
|
ideal_output: selectedSkuRow.ideal_output ?? 0,
|
|
66381
|
-
avg_efficiency: selectedSkuRow.avg_efficiency ?? resolvedLineInfo.metrics.avg_efficiency,
|
|
66382
66827
|
total_workspaces: selectedSkuRow.total_workspaces ?? resolvedLineInfo.metrics.total_workspaces,
|
|
66383
66828
|
underperforming_workspaces: selectedSkuRow.underperforming_workspaces ?? resolvedLineInfo.metrics.underperforming_workspaces,
|
|
66384
66829
|
underperforming_workspace_names: selectedSkuRow.underperforming_workspace_names ?? resolvedLineInfo.metrics.underperforming_workspace_names,
|
|
66385
66830
|
underperforming_workspace_uuids: selectedSkuRow.underperforming_workspace_uuids ?? resolvedLineInfo.metrics.underperforming_workspace_uuids,
|
|
66386
|
-
|
|
66387
|
-
output_hourly: selectedSkuRow.output_hourly ?? resolvedLineInfo.metrics.output_hourly,
|
|
66388
|
-
line_threshold: selectedSkuRow.line_threshold ?? resolvedLineInfo.metrics.line_threshold,
|
|
66389
|
-
poorest_performing_workspaces: selectedSkuRow.poorest_performing_workspaces ?? resolvedLineInfo.metrics.poorest_performing_workspaces
|
|
66831
|
+
line_threshold: selectedSkuRow.line_threshold ?? resolvedLineInfo.metrics.line_threshold
|
|
66390
66832
|
}
|
|
66391
66833
|
};
|
|
66392
66834
|
}, [resolvedLineInfo, selectedSkuRow]);
|
|
@@ -67165,11 +67607,9 @@ var KPIDetailView = ({
|
|
|
67165
67607
|
showIdleTime: idleTimeVlmEnabled
|
|
67166
67608
|
}
|
|
67167
67609
|
) : (
|
|
67168
|
-
//
|
|
67169
|
-
//
|
|
67170
|
-
//
|
|
67171
|
-
// cards. When `selectedSkuId === 'all'`, this is exactly
|
|
67172
|
-
// `resolvedLineInfo` (aggregate path preserved).
|
|
67610
|
+
// Keep the line output + underperforming cards SKU-aware,
|
|
67611
|
+
// while Average Efficiency stays on the aggregate line
|
|
67612
|
+
// metrics even when a specific SKU is selected.
|
|
67173
67613
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
67174
67614
|
MetricCards,
|
|
67175
67615
|
{
|
|
@@ -67214,6 +67654,10 @@ var KPIDetailView = ({
|
|
|
67214
67654
|
workspaceDisplayNames,
|
|
67215
67655
|
hourlyOutputData,
|
|
67216
67656
|
hourlyThreshold,
|
|
67657
|
+
hourlyTargetOutput: chartMetrics?.hourly_target_output,
|
|
67658
|
+
shiftBreaks: shiftConfig?.shifts?.find((shift) => shift.shiftId === resolvedLineInfo.shift_id)?.breaks || [],
|
|
67659
|
+
idleTimeHourly: chartMetrics?.idle_time_hourly,
|
|
67660
|
+
timezone: lineTimezone,
|
|
67217
67661
|
urlDate,
|
|
67218
67662
|
urlShift,
|
|
67219
67663
|
navigate,
|
|
@@ -67279,8 +67723,8 @@ var KPIDetailView = ({
|
|
|
67279
67723
|
showIdleTime: idleTimeVlmEnabled
|
|
67280
67724
|
}
|
|
67281
67725
|
) : (
|
|
67282
|
-
//
|
|
67283
|
-
//
|
|
67726
|
+
// Keep the line output + underperforming cards SKU-aware,
|
|
67727
|
+
// while Average Efficiency stays aggregate.
|
|
67284
67728
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
67285
67729
|
MetricCards,
|
|
67286
67730
|
{
|
|
@@ -67325,6 +67769,8 @@ var KPIDetailView = ({
|
|
|
67325
67769
|
workspaceDisplayNames,
|
|
67326
67770
|
hourlyOutputData,
|
|
67327
67771
|
hourlyThreshold,
|
|
67772
|
+
idleTimeHourly: chartMetrics?.idle_time_hourly,
|
|
67773
|
+
timezone: lineTimezone,
|
|
67328
67774
|
urlDate,
|
|
67329
67775
|
urlShift,
|
|
67330
67776
|
navigate,
|
|
@@ -73561,12 +74007,12 @@ var getDaysDifference = (date, timezone = "UTC", shiftStartTime = "06:00") => {
|
|
|
73561
74007
|
operationalTodayDate.setHours(0, 0, 0, 0);
|
|
73562
74008
|
compareDateInTz.setHours(0, 0, 0, 0);
|
|
73563
74009
|
const diffTime = compareDateInTz.getTime() - operationalTodayDate.getTime();
|
|
73564
|
-
const
|
|
73565
|
-
if (
|
|
73566
|
-
if (
|
|
73567
|
-
if (
|
|
73568
|
-
if (
|
|
73569
|
-
if (
|
|
74010
|
+
const diffDays2 = Math.round(diffTime / (1e3 * 60 * 60 * 24));
|
|
74011
|
+
if (diffDays2 === 0) return "Today";
|
|
74012
|
+
if (diffDays2 === -1) return "Yesterday";
|
|
74013
|
+
if (diffDays2 === 1) return "Tomorrow";
|
|
74014
|
+
if (diffDays2 < -1) return `${Math.abs(diffDays2)} days ago`;
|
|
74015
|
+
if (diffDays2 > 1) return `${diffDays2} days ahead`;
|
|
73570
74016
|
return "Today";
|
|
73571
74017
|
};
|
|
73572
74018
|
var getInitialTab = (sourceType, defaultTab, fromMonthly, urlDate) => {
|
|
@@ -74002,7 +74448,7 @@ var WorkspaceDetailView = ({
|
|
|
74002
74448
|
() => resolveLiveSkuId(workspace?.sku_segments),
|
|
74003
74449
|
[workspace?.sku_segments]
|
|
74004
74450
|
);
|
|
74005
|
-
const activeSkuId = selectedSkuId
|
|
74451
|
+
const activeSkuId = selectedSkuId;
|
|
74006
74452
|
const resolvedLineId = effectiveLineId || workspace?.line_id || cachedDetailedMetrics?.line_id || cachedOverviewMetrics?.line_id || overviewFallback?.line_id;
|
|
74007
74453
|
const { timezone: cycleTimeTimezone } = useTimezone({
|
|
74008
74454
|
lineId: resolvedLineId || void 0,
|
|
@@ -74970,14 +75416,16 @@ var WorkspaceDetailView = ({
|
|
|
74970
75416
|
{
|
|
74971
75417
|
data: workspace.hourly_action_counts || [],
|
|
74972
75418
|
pphThreshold: workspace.pph_threshold || 0,
|
|
75419
|
+
hourlyTargetOutput: workspace.hourly_target_output,
|
|
74973
75420
|
shiftStart: workspace.shift_start || "06:00",
|
|
74974
75421
|
shiftEnd: workspace.shift_end,
|
|
75422
|
+
shiftBreaks: shiftConfig?.shifts?.find((shift2) => shift2.shiftId === workspace.shift_id)?.breaks || [],
|
|
74975
75423
|
showIdleTime: showChartIdleTime,
|
|
74976
75424
|
idleTimeHourly: workspace.idle_time_hourly,
|
|
74977
75425
|
idleTimeClips,
|
|
74978
75426
|
idleTimeClipClassifications,
|
|
74979
75427
|
shiftDate: idleClipDate,
|
|
74980
|
-
timezone,
|
|
75428
|
+
timezone: effectiveCycleTimeTimezone,
|
|
74981
75429
|
skuSegments: isSkuAware ? skuSegments : void 0,
|
|
74982
75430
|
activeSkuId
|
|
74983
75431
|
}
|
|
@@ -75116,14 +75564,16 @@ var WorkspaceDetailView = ({
|
|
|
75116
75564
|
{
|
|
75117
75565
|
data: workspace.hourly_action_counts || [],
|
|
75118
75566
|
pphThreshold: workspace.pph_threshold || 0,
|
|
75567
|
+
hourlyTargetOutput: workspace.hourly_target_output,
|
|
75119
75568
|
shiftStart: workspace.shift_start || "06:00",
|
|
75120
75569
|
shiftEnd: workspace.shift_end,
|
|
75570
|
+
shiftBreaks: shiftConfig?.shifts?.find((shift2) => shift2.shiftId === workspace.shift_id)?.breaks || [],
|
|
75121
75571
|
showIdleTime: showChartIdleTime,
|
|
75122
75572
|
idleTimeHourly: workspace.idle_time_hourly,
|
|
75123
75573
|
idleTimeClips,
|
|
75124
75574
|
idleTimeClipClassifications,
|
|
75125
75575
|
shiftDate: idleClipDate,
|
|
75126
|
-
timezone,
|
|
75576
|
+
timezone: effectiveCycleTimeTimezone,
|
|
75127
75577
|
skuSegments: isSkuAware ? skuSegments : void 0,
|
|
75128
75578
|
activeSkuId
|
|
75129
75579
|
}
|
|
@@ -78400,8 +78850,8 @@ var ImprovementCenterView = () => {
|
|
|
78400
78850
|
const firstSeen = new Date(new Date(openedAt).toLocaleString("en-US", { timeZone: timezone }));
|
|
78401
78851
|
if (Number.isNaN(firstSeen.getTime())) return void 0;
|
|
78402
78852
|
const now4 = new Date((/* @__PURE__ */ new Date()).toLocaleString("en-US", { timeZone: timezone }));
|
|
78403
|
-
const
|
|
78404
|
-
return Math.max(1, Math.ceil(
|
|
78853
|
+
const diffDays2 = Math.max(0, Math.floor((now4.getTime() - firstSeen.getTime()) / (1e3 * 60 * 60 * 24)));
|
|
78854
|
+
return Math.max(1, Math.ceil(diffDays2 / 7));
|
|
78405
78855
|
};
|
|
78406
78856
|
const toZonedDate = (value) => {
|
|
78407
78857
|
if (!value) return void 0;
|
|
@@ -79082,12 +79532,12 @@ var ThreadSidebar = ({
|
|
|
79082
79532
|
const date = new Date(dateString);
|
|
79083
79533
|
const now4 = /* @__PURE__ */ new Date();
|
|
79084
79534
|
const diffMs = now4.getTime() - date.getTime();
|
|
79085
|
-
const
|
|
79086
|
-
if (
|
|
79535
|
+
const diffDays2 = Math.floor(diffMs / (1e3 * 60 * 60 * 24));
|
|
79536
|
+
if (diffDays2 === 0) {
|
|
79087
79537
|
return "Today";
|
|
79088
|
-
} else if (
|
|
79538
|
+
} else if (diffDays2 === 1) {
|
|
79089
79539
|
return "Yesterday";
|
|
79090
|
-
} else if (
|
|
79540
|
+
} else if (diffDays2 < 7) {
|
|
79091
79541
|
return date.toLocaleDateString("en-US", { weekday: "short" });
|
|
79092
79542
|
} else {
|
|
79093
79543
|
return date.toLocaleDateString("en-US", { month: "short", day: "numeric" });
|