@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.mjs
CHANGED
|
@@ -4205,6 +4205,22 @@ var isDummyRow = (row, dummySet) => {
|
|
|
4205
4205
|
if (typeof skuId !== "string" || skuId.length === 0) return false;
|
|
4206
4206
|
return dummySet.has(skuId);
|
|
4207
4207
|
};
|
|
4208
|
+
var isActiveOutputRow = (row) => {
|
|
4209
|
+
const currentOutput = coerceOptionalNumber(row.current_output) ?? 0;
|
|
4210
|
+
const idealOutput = coerceOptionalNumber(row.ideal_output) ?? 0;
|
|
4211
|
+
return currentOutput > 0 || idealOutput > 0;
|
|
4212
|
+
};
|
|
4213
|
+
var roundHalfUpInt = (value) => Math.floor(value + 0.5);
|
|
4214
|
+
var dedupeStringsPreserveOrder = (values) => {
|
|
4215
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4216
|
+
const ordered = [];
|
|
4217
|
+
for (const value of values) {
|
|
4218
|
+
if (!value || seen.has(value)) continue;
|
|
4219
|
+
seen.add(value);
|
|
4220
|
+
ordered.push(value);
|
|
4221
|
+
}
|
|
4222
|
+
return ordered;
|
|
4223
|
+
};
|
|
4208
4224
|
var emptyAggregate = () => ({
|
|
4209
4225
|
current_output: 0,
|
|
4210
4226
|
ideal_output: 0,
|
|
@@ -4323,10 +4339,7 @@ var combineLineMetricsRows = (rows, dummySkuId) => {
|
|
|
4323
4339
|
);
|
|
4324
4340
|
const dummyLineMetricsRow = rows.find((row) => isDummyRow(row, dummySet));
|
|
4325
4341
|
const lineThresholdValue = dummyLineMetricsRow ? safeFloat(dummyLineMetricsRow.line_threshold) : safeFloat(rowsForAggregation[0]?.line_threshold ?? 0);
|
|
4326
|
-
const
|
|
4327
|
-
(acc, row) => acc + safeInt(row.underperforming_workspaces),
|
|
4328
|
-
0
|
|
4329
|
-
);
|
|
4342
|
+
const activeRealRows = rowsForAggregation.filter(isActiveOutputRow);
|
|
4330
4343
|
const weighted = (field) => {
|
|
4331
4344
|
const pairs = [];
|
|
4332
4345
|
rowsForAggregation.forEach((row, idx) => {
|
|
@@ -4348,6 +4361,12 @@ var combineLineMetricsRows = (rows, dummySkuId) => {
|
|
|
4348
4361
|
const avgEfficiency = weighted("avg_efficiency");
|
|
4349
4362
|
const avgCycleTime = weighted("avg_cycle_time");
|
|
4350
4363
|
const thresholdPph = weighted("threshold_pph");
|
|
4364
|
+
const underperformingWorkspaces = activeRealRows.length > 0 ? roundHalfUpInt(
|
|
4365
|
+
activeRealRows.reduce(
|
|
4366
|
+
(acc, row) => acc + safeInt(row.underperforming_workspaces),
|
|
4367
|
+
0
|
|
4368
|
+
) / activeRealRows.length
|
|
4369
|
+
) : 0;
|
|
4351
4370
|
const merged = mergeHourlyFields(rows);
|
|
4352
4371
|
const outputArrays = [];
|
|
4353
4372
|
for (const row of rowsForAggregation) {
|
|
@@ -4370,7 +4389,7 @@ var combineLineMetricsRows = (rows, dummySkuId) => {
|
|
|
4370
4389
|
}
|
|
4371
4390
|
const underperformingNames = [];
|
|
4372
4391
|
const underperformingUuids = [];
|
|
4373
|
-
for (const row of
|
|
4392
|
+
for (const row of activeRealRows) {
|
|
4374
4393
|
const names = row.underperforming_workspace_names;
|
|
4375
4394
|
if (Array.isArray(names)) {
|
|
4376
4395
|
for (const n of names) {
|
|
@@ -4384,6 +4403,8 @@ var combineLineMetricsRows = (rows, dummySkuId) => {
|
|
|
4384
4403
|
}
|
|
4385
4404
|
}
|
|
4386
4405
|
}
|
|
4406
|
+
const dedupedUnderperformingNames = dedupeStringsPreserveOrder(underperformingNames);
|
|
4407
|
+
const dedupedUnderperformingUuids = dedupeStringsPreserveOrder(underperformingUuids);
|
|
4387
4408
|
let totalWorkspacesValue = null;
|
|
4388
4409
|
const primaryTotal = coerceOptionalNumber(primary.total_workspaces);
|
|
4389
4410
|
if (primaryTotal !== null) {
|
|
@@ -4405,9 +4426,9 @@ var combineLineMetricsRows = (rows, dummySkuId) => {
|
|
|
4405
4426
|
line_threshold: lineThresholdValue,
|
|
4406
4427
|
threshold_pph: thresholdPph,
|
|
4407
4428
|
total_workspaces: safeInt(totalWorkspacesValue ?? 0),
|
|
4408
|
-
underperforming_workspaces:
|
|
4409
|
-
underperforming_workspace_names:
|
|
4410
|
-
underperforming_workspace_uuids:
|
|
4429
|
+
underperforming_workspaces: underperformingWorkspaces,
|
|
4430
|
+
underperforming_workspace_names: dedupedUnderperformingNames,
|
|
4431
|
+
underperforming_workspace_uuids: dedupedUnderperformingUuids,
|
|
4411
4432
|
output_array: outputArray,
|
|
4412
4433
|
output_hourly: merged.output_hourly,
|
|
4413
4434
|
idle_time_hourly: merged.idle_time_hourly,
|
|
@@ -5532,11 +5553,11 @@ var getDaysDifferenceInZone = (compareDate, timezone) => {
|
|
|
5532
5553
|
todayInZone.setHours(0, 0, 0, 0);
|
|
5533
5554
|
compareDateInZone.setHours(0, 0, 0, 0);
|
|
5534
5555
|
const diffTime = todayInZone.getTime() - compareDateInZone.getTime();
|
|
5535
|
-
const
|
|
5536
|
-
if (
|
|
5537
|
-
if (
|
|
5538
|
-
if (
|
|
5539
|
-
return `${
|
|
5556
|
+
const diffDays2 = Math.ceil(diffTime / (1e3 * 60 * 60 * 24));
|
|
5557
|
+
if (diffDays2 < 0) return "In the future";
|
|
5558
|
+
if (diffDays2 === 0) return "Today";
|
|
5559
|
+
if (diffDays2 === 1) return "Yesterday";
|
|
5560
|
+
return `${diffDays2} days ago`;
|
|
5540
5561
|
};
|
|
5541
5562
|
var getDashboardHeaderTimeInZone = (date = /* @__PURE__ */ new Date(), timezone, timeOptions, locale = DEFAULT_LOCALE) => {
|
|
5542
5563
|
const defaultOptions = {
|
|
@@ -12983,6 +13004,8 @@ var toWorkspaceDetailedMetrics = ({
|
|
|
12983
13004
|
const targetOutput = coerceNumber(data.target_output ?? data.total_day_output, 0);
|
|
12984
13005
|
const idealOutput = coerceNumber(data.ideal_output ?? data.ideal_output_until_now, 0);
|
|
12985
13006
|
const outputDifference = totalActions - idealOutput;
|
|
13007
|
+
const hasHourlyTargetOutputField = Object.prototype.hasOwnProperty.call(data, "hourly_target_output");
|
|
13008
|
+
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;
|
|
12986
13009
|
const hourlyCycleTimes = Array.isArray(data.hourly_cycle_times) ? data.hourly_cycle_times.map((value) => coerceNumber(value, 0)) : [];
|
|
12987
13010
|
const cycleCompletionClipCount = data.cycle_completion_clip_count === null || data.cycle_completion_clip_count === void 0 ? null : coerceNumber(data.cycle_completion_clip_count, 0);
|
|
12988
13011
|
const cycleTimeDataStatus = data.cycle_time_data_status === "missing_clips" ? "missing_clips" : data.cycle_time_data_status === "available" ? "available" : null;
|
|
@@ -13034,6 +13057,7 @@ var toWorkspaceDetailedMetrics = ({
|
|
|
13034
13057
|
avg_efficiency: coerceNumber(data.efficiency ?? data.avg_efficiency, 0),
|
|
13035
13058
|
total_actions: totalActions,
|
|
13036
13059
|
hourly_action_counts: hourlyActionCounts,
|
|
13060
|
+
hourly_target_output: hourlyTargetOutput,
|
|
13037
13061
|
hourly_cycle_times: hourlyCycleTimes,
|
|
13038
13062
|
cycle_completion_clip_count: cycleCompletionClipCount,
|
|
13039
13063
|
cycle_time_data_status: cycleTimeDataStatus,
|
|
@@ -19650,7 +19674,7 @@ function formatRelativeTime(timestamp) {
|
|
|
19650
19674
|
const diffSeconds = Math.floor(diffMs / 1e3);
|
|
19651
19675
|
const diffMinutes = Math.floor(diffSeconds / 60);
|
|
19652
19676
|
const diffHours = Math.floor(diffMinutes / 60);
|
|
19653
|
-
const
|
|
19677
|
+
const diffDays2 = Math.floor(diffHours / 24);
|
|
19654
19678
|
if (diffSeconds < 60) {
|
|
19655
19679
|
return "Less than a minute ago";
|
|
19656
19680
|
}
|
|
@@ -19662,8 +19686,8 @@ function formatRelativeTime(timestamp) {
|
|
|
19662
19686
|
const hourLabel = diffHours === 1 ? "hour" : "hours";
|
|
19663
19687
|
return `${diffHours} ${hourLabel} ago`;
|
|
19664
19688
|
}
|
|
19665
|
-
const dayLabel =
|
|
19666
|
-
return `${
|
|
19689
|
+
const dayLabel = diffDays2 === 1 ? "day" : "days";
|
|
19690
|
+
return `${diffDays2} ${dayLabel} ago`;
|
|
19667
19691
|
} catch (error) {
|
|
19668
19692
|
console.error("[formatRelativeTime] Error formatting timestamp:", error);
|
|
19669
19693
|
return "Unknown";
|
|
@@ -35163,6 +35187,478 @@ var Button = React144.forwardRef(
|
|
|
35163
35187
|
);
|
|
35164
35188
|
Button.displayName = "Button";
|
|
35165
35189
|
|
|
35190
|
+
// src/lib/utils/hourlyTargets.ts
|
|
35191
|
+
var stripSeconds2 = (timeStr) => timeStr ? timeStr.slice(0, 5) : timeStr;
|
|
35192
|
+
var MINUTES_PER_DAY = 24 * 60;
|
|
35193
|
+
var parseTimeToMinutes2 = (timeString) => {
|
|
35194
|
+
const normalized = stripSeconds2(timeString || "");
|
|
35195
|
+
if (!normalized || !/^[0-2]\d:[0-5]\d$/.test(normalized)) return Number.NaN;
|
|
35196
|
+
const [hours, minutes] = normalized.split(":").map(Number);
|
|
35197
|
+
return hours * 60 + minutes;
|
|
35198
|
+
};
|
|
35199
|
+
var normalizeBreaksOnShiftTimeline = (shiftStart, breaks) => {
|
|
35200
|
+
const shiftStartMinutes = parseTimeToMinutes2(shiftStart);
|
|
35201
|
+
if (!Number.isFinite(shiftStartMinutes)) return [];
|
|
35202
|
+
const normalizedBreaks = [];
|
|
35203
|
+
for (const entry of breaks) {
|
|
35204
|
+
const startRaw = parseTimeToMinutes2(entry.startTime);
|
|
35205
|
+
const endRaw = parseTimeToMinutes2(entry.endTime);
|
|
35206
|
+
if (!Number.isFinite(startRaw) || !Number.isFinite(endRaw)) continue;
|
|
35207
|
+
let start = startRaw;
|
|
35208
|
+
let end = endRaw;
|
|
35209
|
+
if (end <= start) {
|
|
35210
|
+
end += 24 * 60;
|
|
35211
|
+
}
|
|
35212
|
+
if (start < shiftStartMinutes) {
|
|
35213
|
+
start += 24 * 60;
|
|
35214
|
+
end += 24 * 60;
|
|
35215
|
+
}
|
|
35216
|
+
const label = entry.remarks?.trim() || "Break";
|
|
35217
|
+
normalizedBreaks.push({ start, end, label });
|
|
35218
|
+
}
|
|
35219
|
+
return normalizedBreaks;
|
|
35220
|
+
};
|
|
35221
|
+
var roundTarget = (value, mode) => {
|
|
35222
|
+
if (!Number.isFinite(value)) return 0;
|
|
35223
|
+
switch (mode) {
|
|
35224
|
+
case "floor":
|
|
35225
|
+
return Math.floor(value);
|
|
35226
|
+
case "ceil":
|
|
35227
|
+
return Math.ceil(value);
|
|
35228
|
+
case "round":
|
|
35229
|
+
default:
|
|
35230
|
+
return Math.round(value);
|
|
35231
|
+
}
|
|
35232
|
+
};
|
|
35233
|
+
var formatDateKey = (date) => {
|
|
35234
|
+
const year = date.getUTCFullYear();
|
|
35235
|
+
const month = `${date.getUTCMonth() + 1}`.padStart(2, "0");
|
|
35236
|
+
const day = `${date.getUTCDate()}`.padStart(2, "0");
|
|
35237
|
+
return `${year}-${month}-${day}`;
|
|
35238
|
+
};
|
|
35239
|
+
var shiftDateKey = (dateKey, deltaDays) => {
|
|
35240
|
+
const [yearPart, monthPart, dayPart] = dateKey.split("-").map(Number);
|
|
35241
|
+
const year = Number.isFinite(yearPart) ? yearPart : 1970;
|
|
35242
|
+
const month = Number.isFinite(monthPart) ? monthPart : 1;
|
|
35243
|
+
const day = Number.isFinite(dayPart) ? dayPart : 1;
|
|
35244
|
+
const date = new Date(Date.UTC(year, month - 1, day));
|
|
35245
|
+
date.setUTCDate(date.getUTCDate() + deltaDays);
|
|
35246
|
+
return formatDateKey(date);
|
|
35247
|
+
};
|
|
35248
|
+
var getZonedNowSnapshot = (timeZone, now4) => {
|
|
35249
|
+
const formatter = new Intl.DateTimeFormat("en-US", {
|
|
35250
|
+
timeZone,
|
|
35251
|
+
year: "numeric",
|
|
35252
|
+
month: "2-digit",
|
|
35253
|
+
day: "2-digit",
|
|
35254
|
+
hour: "2-digit",
|
|
35255
|
+
minute: "2-digit",
|
|
35256
|
+
hourCycle: "h23"
|
|
35257
|
+
});
|
|
35258
|
+
const parts = formatter.formatToParts(now4).reduce((acc, part) => {
|
|
35259
|
+
if (part.type !== "literal") {
|
|
35260
|
+
acc[part.type] = part.value;
|
|
35261
|
+
}
|
|
35262
|
+
return acc;
|
|
35263
|
+
}, {});
|
|
35264
|
+
const year = Number(parts.year);
|
|
35265
|
+
const month = Number(parts.month);
|
|
35266
|
+
const day = Number(parts.day);
|
|
35267
|
+
const hour = Number(parts.hour);
|
|
35268
|
+
const minute = Number(parts.minute);
|
|
35269
|
+
return {
|
|
35270
|
+
dateKey: `${String(year).padStart(4, "0")}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`,
|
|
35271
|
+
minutesOfDay: (Number.isFinite(hour) ? hour : 0) * 60 + (Number.isFinite(minute) ? minute : 0)
|
|
35272
|
+
};
|
|
35273
|
+
};
|
|
35274
|
+
var getDateKeyInTimeZone = (timeZone, now4 = /* @__PURE__ */ new Date()) => getZonedNowSnapshot(timeZone, now4).dateKey;
|
|
35275
|
+
var buildHourlyIntervals = ({
|
|
35276
|
+
shiftStart,
|
|
35277
|
+
shiftEnd,
|
|
35278
|
+
bucketMinutes = 60,
|
|
35279
|
+
fallbackHours = 11
|
|
35280
|
+
}) => {
|
|
35281
|
+
const startMinutes = parseTimeToMinutes2(shiftStart);
|
|
35282
|
+
if (!Number.isFinite(startMinutes)) return [];
|
|
35283
|
+
const bucket = Number.isFinite(bucketMinutes) && bucketMinutes > 0 ? Math.floor(bucketMinutes) : 60;
|
|
35284
|
+
let totalMinutes;
|
|
35285
|
+
const endRaw = shiftEnd ? parseTimeToMinutes2(shiftEnd) : Number.NaN;
|
|
35286
|
+
if (!Number.isFinite(endRaw)) {
|
|
35287
|
+
totalMinutes = Math.max(0, Math.round(fallbackHours * 60));
|
|
35288
|
+
} else {
|
|
35289
|
+
let endMinutes = endRaw;
|
|
35290
|
+
if (endMinutes <= startMinutes) {
|
|
35291
|
+
endMinutes += 24 * 60;
|
|
35292
|
+
}
|
|
35293
|
+
totalMinutes = endMinutes - startMinutes;
|
|
35294
|
+
}
|
|
35295
|
+
if (!Number.isFinite(totalMinutes) || totalMinutes <= 0) return [];
|
|
35296
|
+
const count = Math.ceil(totalMinutes / bucket);
|
|
35297
|
+
const shiftEndMinutes = startMinutes + totalMinutes;
|
|
35298
|
+
const intervals = [];
|
|
35299
|
+
for (let i = 0; i < count; i += 1) {
|
|
35300
|
+
const start = startMinutes + i * bucket;
|
|
35301
|
+
const end = Math.min(start + bucket, shiftEndMinutes);
|
|
35302
|
+
const minutes = Math.max(0, end - start);
|
|
35303
|
+
if (minutes <= 0) continue;
|
|
35304
|
+
intervals.push({ start, end, minutes });
|
|
35305
|
+
}
|
|
35306
|
+
return intervals;
|
|
35307
|
+
};
|
|
35308
|
+
var computeBreakMinutesByInterval = ({
|
|
35309
|
+
intervals,
|
|
35310
|
+
shiftStart,
|
|
35311
|
+
breaks
|
|
35312
|
+
}) => {
|
|
35313
|
+
if (!intervals.length || !breaks.length) return intervals.map(() => 0);
|
|
35314
|
+
const normalizedBreaks = normalizeBreaksOnShiftTimeline(shiftStart, breaks);
|
|
35315
|
+
return intervals.map((interval) => {
|
|
35316
|
+
if (!normalizedBreaks.length) return 0;
|
|
35317
|
+
let total = 0;
|
|
35318
|
+
for (const brk of normalizedBreaks) {
|
|
35319
|
+
const overlap = Math.max(0, Math.min(interval.end, brk.end) - Math.max(interval.start, brk.start));
|
|
35320
|
+
total += overlap;
|
|
35321
|
+
if (total >= interval.minutes) return interval.minutes;
|
|
35322
|
+
}
|
|
35323
|
+
return Math.min(interval.minutes, total);
|
|
35324
|
+
});
|
|
35325
|
+
};
|
|
35326
|
+
var computeBreakRemarksByInterval = ({
|
|
35327
|
+
intervals,
|
|
35328
|
+
shiftStart,
|
|
35329
|
+
breaks
|
|
35330
|
+
}) => {
|
|
35331
|
+
if (!intervals.length || !breaks.length) return intervals.map(() => "");
|
|
35332
|
+
const normalizedBreaks = normalizeBreaksOnShiftTimeline(shiftStart, breaks);
|
|
35333
|
+
return intervals.map((interval) => {
|
|
35334
|
+
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);
|
|
35335
|
+
return labels.join(", ");
|
|
35336
|
+
});
|
|
35337
|
+
};
|
|
35338
|
+
var computeEffectiveTargets = ({
|
|
35339
|
+
intervals,
|
|
35340
|
+
breakMinutes,
|
|
35341
|
+
pphThreshold,
|
|
35342
|
+
rounding = "round"
|
|
35343
|
+
}) => {
|
|
35344
|
+
return intervals.map((interval, idx) => {
|
|
35345
|
+
const intervalMinutes = Number(interval?.minutes) || 0;
|
|
35346
|
+
const breakMins = Number(breakMinutes?.[idx]) || 0;
|
|
35347
|
+
const plannedWorkMinutes = Math.max(0, intervalMinutes - breakMins);
|
|
35348
|
+
if (!Number.isFinite(pphThreshold) || pphThreshold <= 0) return 0;
|
|
35349
|
+
if (plannedWorkMinutes <= 0) return 0;
|
|
35350
|
+
return roundTarget(pphThreshold * plannedWorkMinutes / 60, rounding);
|
|
35351
|
+
});
|
|
35352
|
+
};
|
|
35353
|
+
var buildHourlyTargetPlan = ({
|
|
35354
|
+
shiftStart,
|
|
35355
|
+
shiftEnd,
|
|
35356
|
+
breaks = [],
|
|
35357
|
+
pphThreshold,
|
|
35358
|
+
bucketMinutes = 60,
|
|
35359
|
+
fallbackHours = 11,
|
|
35360
|
+
rounding = "round"
|
|
35361
|
+
}) => {
|
|
35362
|
+
const intervals = buildHourlyIntervals({
|
|
35363
|
+
shiftStart,
|
|
35364
|
+
shiftEnd,
|
|
35365
|
+
bucketMinutes,
|
|
35366
|
+
fallbackHours
|
|
35367
|
+
});
|
|
35368
|
+
const breakMinutes = computeBreakMinutesByInterval({
|
|
35369
|
+
intervals,
|
|
35370
|
+
shiftStart,
|
|
35371
|
+
breaks
|
|
35372
|
+
});
|
|
35373
|
+
const breakRemarks = computeBreakRemarksByInterval({
|
|
35374
|
+
intervals,
|
|
35375
|
+
shiftStart,
|
|
35376
|
+
breaks
|
|
35377
|
+
});
|
|
35378
|
+
const productiveMinutes = intervals.map((interval, idx) => Math.max(0, (Number(interval?.minutes) || 0) - (Number(breakMinutes[idx]) || 0)));
|
|
35379
|
+
const targets = computeEffectiveTargets({
|
|
35380
|
+
intervals,
|
|
35381
|
+
breakMinutes,
|
|
35382
|
+
pphThreshold,
|
|
35383
|
+
rounding
|
|
35384
|
+
});
|
|
35385
|
+
return {
|
|
35386
|
+
intervals,
|
|
35387
|
+
breakMinutes,
|
|
35388
|
+
breakRemarks,
|
|
35389
|
+
productiveMinutes,
|
|
35390
|
+
targets
|
|
35391
|
+
};
|
|
35392
|
+
};
|
|
35393
|
+
var isHourlyIntervalComplete = ({
|
|
35394
|
+
reportDate,
|
|
35395
|
+
shiftStart,
|
|
35396
|
+
shiftEnd,
|
|
35397
|
+
interval,
|
|
35398
|
+
timeZone = "Asia/Kolkata",
|
|
35399
|
+
now: now4 = /* @__PURE__ */ new Date()
|
|
35400
|
+
}) => {
|
|
35401
|
+
if (!reportDate) return true;
|
|
35402
|
+
const snapshot = getZonedNowSnapshot(timeZone, now4);
|
|
35403
|
+
const shiftStartMinutes = parseTimeToMinutes2(shiftStart);
|
|
35404
|
+
const shiftEndMinutes = shiftEnd ? parseTimeToMinutes2(shiftEnd) : Number.NaN;
|
|
35405
|
+
const wrapsMidnight = Number.isFinite(shiftStartMinutes) && Number.isFinite(shiftEndMinutes) && shiftEndMinutes <= shiftStartMinutes;
|
|
35406
|
+
if (reportDate === snapshot.dateKey) {
|
|
35407
|
+
return interval.end <= snapshot.minutesOfDay;
|
|
35408
|
+
}
|
|
35409
|
+
if (wrapsMidnight && reportDate === shiftDateKey(snapshot.dateKey, -1)) {
|
|
35410
|
+
return interval.end <= snapshot.minutesOfDay + MINUTES_PER_DAY;
|
|
35411
|
+
}
|
|
35412
|
+
return reportDate < snapshot.dateKey;
|
|
35413
|
+
};
|
|
35414
|
+
var isShiftInProgressForReportDate = ({
|
|
35415
|
+
reportDate,
|
|
35416
|
+
shiftStart,
|
|
35417
|
+
shiftEnd,
|
|
35418
|
+
timeZone = "Asia/Kolkata",
|
|
35419
|
+
now: now4 = /* @__PURE__ */ new Date()
|
|
35420
|
+
}) => {
|
|
35421
|
+
if (!reportDate || !shiftStart || !shiftEnd) return false;
|
|
35422
|
+
const shiftStartMinutes = parseTimeToMinutes2(shiftStart);
|
|
35423
|
+
const shiftEndMinutesRaw = parseTimeToMinutes2(shiftEnd);
|
|
35424
|
+
if (!Number.isFinite(shiftStartMinutes) || !Number.isFinite(shiftEndMinutesRaw)) {
|
|
35425
|
+
return false;
|
|
35426
|
+
}
|
|
35427
|
+
let shiftEndMinutes = shiftEndMinutesRaw;
|
|
35428
|
+
const wrapsMidnight = shiftEndMinutes <= shiftStartMinutes;
|
|
35429
|
+
if (wrapsMidnight) {
|
|
35430
|
+
shiftEndMinutes += MINUTES_PER_DAY;
|
|
35431
|
+
}
|
|
35432
|
+
const snapshot = getZonedNowSnapshot(timeZone, now4);
|
|
35433
|
+
let currentMinutes = null;
|
|
35434
|
+
if (reportDate === snapshot.dateKey) {
|
|
35435
|
+
currentMinutes = snapshot.minutesOfDay;
|
|
35436
|
+
} else if (wrapsMidnight && reportDate === shiftDateKey(snapshot.dateKey, -1)) {
|
|
35437
|
+
currentMinutes = snapshot.minutesOfDay + MINUTES_PER_DAY;
|
|
35438
|
+
}
|
|
35439
|
+
if (currentMinutes === null) {
|
|
35440
|
+
return false;
|
|
35441
|
+
}
|
|
35442
|
+
return shiftStartMinutes <= currentMinutes && currentMinutes < shiftEndMinutes;
|
|
35443
|
+
};
|
|
35444
|
+
var padTime = (value) => value.toString().padStart(2, "0");
|
|
35445
|
+
var parseTime = (timeValue) => {
|
|
35446
|
+
if (!timeValue) return null;
|
|
35447
|
+
const [hourPart, minutePart] = timeValue.split(":");
|
|
35448
|
+
const hour = Number.parseInt(hourPart, 10);
|
|
35449
|
+
const minute = Number.parseInt(minutePart ?? "0", 10);
|
|
35450
|
+
if (!Number.isFinite(hour) || !Number.isFinite(minute)) return null;
|
|
35451
|
+
return { hour, minute };
|
|
35452
|
+
};
|
|
35453
|
+
var normalizeIdleTimeHourly = (idleTimeHourly) => {
|
|
35454
|
+
if (!idleTimeHourly || typeof idleTimeHourly !== "object") {
|
|
35455
|
+
return {};
|
|
35456
|
+
}
|
|
35457
|
+
return Object.fromEntries(
|
|
35458
|
+
Object.entries(idleTimeHourly).map(([key, value]) => {
|
|
35459
|
+
if (Array.isArray(value)) return [key, value];
|
|
35460
|
+
if (value && Array.isArray(value.values)) {
|
|
35461
|
+
return [key, value.values];
|
|
35462
|
+
}
|
|
35463
|
+
return [key, []];
|
|
35464
|
+
})
|
|
35465
|
+
);
|
|
35466
|
+
};
|
|
35467
|
+
var interpretIdleValue = (value) => {
|
|
35468
|
+
if (value === 1 || value === "1") return "idle";
|
|
35469
|
+
if (value === 0 || value === "0") return "active";
|
|
35470
|
+
if (value === "x" || value === null || value === void 0) return "unknown";
|
|
35471
|
+
return "unknown";
|
|
35472
|
+
};
|
|
35473
|
+
var getShiftDurationMinutes = (shiftStart, shiftEnd) => {
|
|
35474
|
+
const start = parseTime(shiftStart);
|
|
35475
|
+
const end = parseTime(shiftEnd);
|
|
35476
|
+
if (!start || !end) return null;
|
|
35477
|
+
let duration = end.hour * 60 + end.minute - (start.hour * 60 + start.minute);
|
|
35478
|
+
if (duration <= 0) {
|
|
35479
|
+
duration += 24 * 60;
|
|
35480
|
+
}
|
|
35481
|
+
return duration > 0 ? duration : null;
|
|
35482
|
+
};
|
|
35483
|
+
var getShiftElapsedMinutes = ({
|
|
35484
|
+
shiftStart,
|
|
35485
|
+
shiftEnd,
|
|
35486
|
+
shiftDate,
|
|
35487
|
+
timezone,
|
|
35488
|
+
now: now4 = /* @__PURE__ */ new Date()
|
|
35489
|
+
}) => {
|
|
35490
|
+
if (!shiftDate || !timezone) return null;
|
|
35491
|
+
const startTime = parseTime(shiftStart);
|
|
35492
|
+
const endTime = parseTime(shiftEnd);
|
|
35493
|
+
if (!startTime || !endTime) return null;
|
|
35494
|
+
const shiftStartDate = fromZonedTime(
|
|
35495
|
+
`${shiftDate}T${padTime(startTime.hour)}:${padTime(startTime.minute)}:00`,
|
|
35496
|
+
timezone
|
|
35497
|
+
);
|
|
35498
|
+
let shiftEndDate = fromZonedTime(
|
|
35499
|
+
`${shiftDate}T${padTime(endTime.hour)}:${padTime(endTime.minute)}:00`,
|
|
35500
|
+
timezone
|
|
35501
|
+
);
|
|
35502
|
+
if (shiftEndDate <= shiftStartDate) {
|
|
35503
|
+
shiftEndDate = addDays(shiftEndDate, 1);
|
|
35504
|
+
}
|
|
35505
|
+
const shiftMinutes = Math.max(differenceInMinutes(shiftEndDate, shiftStartDate), 0);
|
|
35506
|
+
if (shiftMinutes <= 0) return null;
|
|
35507
|
+
const elapsed = differenceInMinutes(now4, shiftStartDate);
|
|
35508
|
+
return Math.min(Math.max(elapsed, 0), shiftMinutes);
|
|
35509
|
+
};
|
|
35510
|
+
var maskFutureHourlySeries = ({
|
|
35511
|
+
data,
|
|
35512
|
+
shiftStart,
|
|
35513
|
+
shiftEnd,
|
|
35514
|
+
shiftDate,
|
|
35515
|
+
timezone,
|
|
35516
|
+
now: now4 = /* @__PURE__ */ new Date()
|
|
35517
|
+
}) => {
|
|
35518
|
+
if (!Array.isArray(data)) {
|
|
35519
|
+
return [];
|
|
35520
|
+
}
|
|
35521
|
+
const normalizedData = data.map((value) => typeof value === "number" && Number.isFinite(value) ? value : null);
|
|
35522
|
+
if (!normalizedData.length) {
|
|
35523
|
+
return normalizedData;
|
|
35524
|
+
}
|
|
35525
|
+
const shiftMinutes = getShiftDurationMinutes(shiftStart, shiftEnd);
|
|
35526
|
+
const elapsedMinutes = getShiftElapsedMinutes({
|
|
35527
|
+
shiftStart,
|
|
35528
|
+
shiftEnd,
|
|
35529
|
+
shiftDate,
|
|
35530
|
+
timezone,
|
|
35531
|
+
now: now4
|
|
35532
|
+
});
|
|
35533
|
+
if (shiftMinutes === null || elapsedMinutes === null || elapsedMinutes >= shiftMinutes) {
|
|
35534
|
+
return normalizedData;
|
|
35535
|
+
}
|
|
35536
|
+
return normalizedData.map((value, index) => {
|
|
35537
|
+
const slotStartMinutes = index * 60;
|
|
35538
|
+
return slotStartMinutes > elapsedMinutes ? null : value;
|
|
35539
|
+
});
|
|
35540
|
+
};
|
|
35541
|
+
var buildUptimeSeries = ({
|
|
35542
|
+
idleTimeHourly,
|
|
35543
|
+
shiftStart,
|
|
35544
|
+
shiftEnd,
|
|
35545
|
+
shiftDate,
|
|
35546
|
+
timezone,
|
|
35547
|
+
elapsedMinutes
|
|
35548
|
+
}) => {
|
|
35549
|
+
const normalizedIdle = normalizeIdleTimeHourly(idleTimeHourly || {});
|
|
35550
|
+
const hasIdleData = Object.keys(normalizedIdle).length > 0;
|
|
35551
|
+
if (!hasIdleData || !shiftDate || !timezone) {
|
|
35552
|
+
return {
|
|
35553
|
+
points: [],
|
|
35554
|
+
activeMinutes: 0,
|
|
35555
|
+
idleMinutes: 0,
|
|
35556
|
+
availableMinutes: 0,
|
|
35557
|
+
shiftMinutes: 0,
|
|
35558
|
+
elapsedMinutes: 0,
|
|
35559
|
+
hasData: false
|
|
35560
|
+
};
|
|
35561
|
+
}
|
|
35562
|
+
const startTime = parseTime(shiftStart);
|
|
35563
|
+
const endTime = parseTime(shiftEnd);
|
|
35564
|
+
if (!startTime || !endTime) {
|
|
35565
|
+
return {
|
|
35566
|
+
points: [],
|
|
35567
|
+
activeMinutes: 0,
|
|
35568
|
+
idleMinutes: 0,
|
|
35569
|
+
availableMinutes: 0,
|
|
35570
|
+
shiftMinutes: 0,
|
|
35571
|
+
elapsedMinutes: 0,
|
|
35572
|
+
hasData: false
|
|
35573
|
+
};
|
|
35574
|
+
}
|
|
35575
|
+
const shiftStartDate = fromZonedTime(
|
|
35576
|
+
`${shiftDate}T${padTime(startTime.hour)}:${padTime(startTime.minute)}:00`,
|
|
35577
|
+
timezone
|
|
35578
|
+
);
|
|
35579
|
+
let shiftEndDate = fromZonedTime(
|
|
35580
|
+
`${shiftDate}T${padTime(endTime.hour)}:${padTime(endTime.minute)}:00`,
|
|
35581
|
+
timezone
|
|
35582
|
+
);
|
|
35583
|
+
if (shiftEndDate <= shiftStartDate) {
|
|
35584
|
+
shiftEndDate = addDays(shiftEndDate, 1);
|
|
35585
|
+
}
|
|
35586
|
+
const shiftMinutes = Math.max(differenceInMinutes(shiftEndDate, shiftStartDate), 0);
|
|
35587
|
+
if (shiftMinutes <= 0) {
|
|
35588
|
+
return {
|
|
35589
|
+
points: [],
|
|
35590
|
+
activeMinutes: 0,
|
|
35591
|
+
idleMinutes: 0,
|
|
35592
|
+
availableMinutes: 0,
|
|
35593
|
+
shiftMinutes: 0,
|
|
35594
|
+
elapsedMinutes: 0,
|
|
35595
|
+
hasData: false
|
|
35596
|
+
};
|
|
35597
|
+
}
|
|
35598
|
+
const elapsedMinutesClamped = Number.isFinite(elapsedMinutes) ? Math.min(Math.max(Math.floor(elapsedMinutes ?? 0), 0), shiftMinutes) : shiftMinutes;
|
|
35599
|
+
const points = [];
|
|
35600
|
+
let activeMinutes = 0;
|
|
35601
|
+
let idleMinutes = 0;
|
|
35602
|
+
for (let minuteIndex = 0; minuteIndex < shiftMinutes; minuteIndex += 1) {
|
|
35603
|
+
const minuteDate = addMinutes(shiftStartDate, minuteIndex);
|
|
35604
|
+
const timeLabel = formatInTimeZone(minuteDate, timezone, "h:mm a");
|
|
35605
|
+
if (minuteIndex >= elapsedMinutesClamped) {
|
|
35606
|
+
points.push({
|
|
35607
|
+
minuteIndex,
|
|
35608
|
+
timeLabel,
|
|
35609
|
+
uptime: null,
|
|
35610
|
+
status: "unknown"
|
|
35611
|
+
});
|
|
35612
|
+
continue;
|
|
35613
|
+
}
|
|
35614
|
+
const hourKey = formatInTimeZone(minuteDate, timezone, "H");
|
|
35615
|
+
const minuteKey = Number.parseInt(formatInTimeZone(minuteDate, timezone, "m"), 10);
|
|
35616
|
+
const hourBucket = normalizedIdle[hourKey] || [];
|
|
35617
|
+
const value = Array.isArray(hourBucket) ? hourBucket[minuteKey] : void 0;
|
|
35618
|
+
const status = interpretIdleValue(value);
|
|
35619
|
+
if (status === "active") activeMinutes += 1;
|
|
35620
|
+
if (status === "idle") idleMinutes += 1;
|
|
35621
|
+
points.push({
|
|
35622
|
+
minuteIndex,
|
|
35623
|
+
timeLabel,
|
|
35624
|
+
uptime: status === "active" ? 1 : status === "idle" ? 0 : null,
|
|
35625
|
+
status
|
|
35626
|
+
});
|
|
35627
|
+
}
|
|
35628
|
+
return {
|
|
35629
|
+
points,
|
|
35630
|
+
activeMinutes,
|
|
35631
|
+
idleMinutes,
|
|
35632
|
+
availableMinutes: activeMinutes + idleMinutes,
|
|
35633
|
+
shiftMinutes,
|
|
35634
|
+
elapsedMinutes: elapsedMinutesClamped,
|
|
35635
|
+
hasData: activeMinutes + idleMinutes > 0
|
|
35636
|
+
};
|
|
35637
|
+
};
|
|
35638
|
+
var getUptimeUtilizationPercent = (shift) => {
|
|
35639
|
+
const efficiency = shift.efficiency;
|
|
35640
|
+
if (Number.isFinite(efficiency)) {
|
|
35641
|
+
return Math.round(Math.max(0, Math.min(100, Number(efficiency))));
|
|
35642
|
+
}
|
|
35643
|
+
const idleSeconds = Number.isFinite(shift.idleTime) ? Number(shift.idleTime) : 0;
|
|
35644
|
+
const activeSeconds = Number.isFinite(shift.activeTimeSeconds) ? Number(shift.activeTimeSeconds) : null;
|
|
35645
|
+
let availableSeconds = Number.isFinite(shift.availableTimeSeconds) ? Number(shift.availableTimeSeconds) : null;
|
|
35646
|
+
if (availableSeconds === null) {
|
|
35647
|
+
if ((activeSeconds ?? 0) > 0 || idleSeconds > 0) {
|
|
35648
|
+
availableSeconds = (activeSeconds ?? 0) + idleSeconds;
|
|
35649
|
+
} else {
|
|
35650
|
+
return 0;
|
|
35651
|
+
}
|
|
35652
|
+
}
|
|
35653
|
+
if (availableSeconds <= 0) return 0;
|
|
35654
|
+
const clampedIdleSeconds = Math.min(Math.max(idleSeconds, 0), availableSeconds);
|
|
35655
|
+
const productiveSeconds = Math.max(
|
|
35656
|
+
activeSeconds ?? availableSeconds - clampedIdleSeconds,
|
|
35657
|
+
0
|
|
35658
|
+
);
|
|
35659
|
+
return Math.round(productiveSeconds / availableSeconds * 100);
|
|
35660
|
+
};
|
|
35661
|
+
|
|
35166
35662
|
// src/components/charts/skuDividerUtils.ts
|
|
35167
35663
|
var HOURLY_TIME_RE = /^(\d{1,2}):(\d{2})/;
|
|
35168
35664
|
var parseTimeOfDay = (timeValue) => {
|
|
@@ -35174,37 +35670,163 @@ var parseTimeOfDay = (timeValue) => {
|
|
|
35174
35670
|
if (hour < 0 || hour > 23 || minute < 0 || minute > 59) return null;
|
|
35175
35671
|
return { hour, minute };
|
|
35176
35672
|
};
|
|
35177
|
-
var
|
|
35178
|
-
|
|
35673
|
+
var parseDateKey = (value) => {
|
|
35674
|
+
const [yearPart, monthPart, dayPart] = value.split("-").map(Number);
|
|
35675
|
+
if (!Number.isFinite(yearPart) || !Number.isFinite(monthPart) || !Number.isFinite(dayPart)) {
|
|
35676
|
+
return null;
|
|
35677
|
+
}
|
|
35678
|
+
return Date.UTC(yearPart, monthPart - 1, dayPart);
|
|
35679
|
+
};
|
|
35680
|
+
var diffDays = (left, right) => {
|
|
35681
|
+
const leftUtc = parseDateKey(left);
|
|
35682
|
+
const rightUtc = parseDateKey(right);
|
|
35683
|
+
if (leftUtc === null || rightUtc === null) return null;
|
|
35684
|
+
return Math.round((leftUtc - rightUtc) / (24 * 60 * 60 * 1e3));
|
|
35685
|
+
};
|
|
35686
|
+
var getSegmentMinutes = (isoString, timeZone, reportDate) => {
|
|
35687
|
+
const date = new Date(isoString);
|
|
35688
|
+
if (Number.isNaN(date.getTime())) return Number.NaN;
|
|
35689
|
+
const formatter = new Intl.DateTimeFormat("en-US", {
|
|
35690
|
+
timeZone,
|
|
35691
|
+
year: "numeric",
|
|
35692
|
+
month: "2-digit",
|
|
35693
|
+
day: "2-digit",
|
|
35694
|
+
hour: "2-digit",
|
|
35695
|
+
minute: "2-digit",
|
|
35696
|
+
hourCycle: "h23"
|
|
35697
|
+
});
|
|
35698
|
+
const parts = formatter.formatToParts(date).reduce((acc, part) => {
|
|
35699
|
+
if (part.type !== "literal") {
|
|
35700
|
+
acc[part.type] = part.value;
|
|
35701
|
+
}
|
|
35702
|
+
return acc;
|
|
35703
|
+
}, {});
|
|
35704
|
+
const dateKey = `${parts.year}-${parts.month}-${parts.day}`;
|
|
35705
|
+
let minutes = Number(parts.hour) * 60 + Number(parts.minute);
|
|
35706
|
+
const dayDelta = diffDays(dateKey, reportDate);
|
|
35707
|
+
if (dayDelta === null) return Number.NaN;
|
|
35708
|
+
minutes += dayDelta * 24 * 60;
|
|
35709
|
+
return minutes;
|
|
35710
|
+
};
|
|
35711
|
+
var computeSegmentOffsetUtc = (segment, shift) => {
|
|
35179
35712
|
const parsedMs = Date.parse(segment.start_time);
|
|
35180
35713
|
if (Number.isNaN(parsedMs)) return null;
|
|
35181
35714
|
const date = new Date(parsedMs);
|
|
35182
35715
|
const segHour = date.getUTCHours();
|
|
35183
35716
|
const segMinute = date.getUTCMinutes();
|
|
35184
35717
|
const segMinutes = segHour * 60 + segMinute;
|
|
35185
|
-
|
|
35718
|
+
const shiftStartMinutes = shift.startHour * 60 + shift.startMinute;
|
|
35186
35719
|
let deltaMinutes = segMinutes - shiftStartMinutes;
|
|
35187
35720
|
if (deltaMinutes < 0) deltaMinutes += 24 * 60;
|
|
35188
35721
|
const offsetHours = deltaMinutes / 60;
|
|
35189
35722
|
if (offsetHours < 0 || offsetHours > shift.slotCount) return null;
|
|
35190
35723
|
return offsetHours;
|
|
35191
35724
|
};
|
|
35725
|
+
var computeSegmentOffset = (segment, shift) => {
|
|
35726
|
+
if (!segment?.start_time) return null;
|
|
35727
|
+
if (shift.shiftDate && shift.timezone) {
|
|
35728
|
+
const segmentMinutes = getSegmentMinutes(
|
|
35729
|
+
segment.start_time,
|
|
35730
|
+
shift.timezone,
|
|
35731
|
+
shift.shiftDate
|
|
35732
|
+
);
|
|
35733
|
+
if (!Number.isFinite(segmentMinutes)) return null;
|
|
35734
|
+
const shiftStartMinutes = shift.startHour * 60 + shift.startMinute;
|
|
35735
|
+
const deltaMinutes = segmentMinutes - shiftStartMinutes;
|
|
35736
|
+
const offsetHours = deltaMinutes / 60;
|
|
35737
|
+
if (offsetHours < 0 || offsetHours > shift.slotCount) return null;
|
|
35738
|
+
return offsetHours;
|
|
35739
|
+
}
|
|
35740
|
+
return computeSegmentOffsetUtc(segment, shift);
|
|
35741
|
+
};
|
|
35192
35742
|
var resolveShiftWindow2 = (params) => {
|
|
35193
35743
|
const startTime = parseTimeOfDay(params.shiftStart);
|
|
35194
35744
|
if (!startTime) return null;
|
|
35195
35745
|
return {
|
|
35196
35746
|
startHour: startTime.hour,
|
|
35197
35747
|
startMinute: startTime.minute,
|
|
35198
|
-
slotCount: params.slotCount
|
|
35748
|
+
slotCount: params.slotCount,
|
|
35749
|
+
...params.shiftDate ? { shiftDate: params.shiftDate } : {},
|
|
35750
|
+
...params.timezone ? { timezone: params.timezone } : {}
|
|
35199
35751
|
};
|
|
35200
35752
|
};
|
|
35753
|
+
var resolveTimelineEndOffset = ({
|
|
35754
|
+
shiftStart,
|
|
35755
|
+
shiftEnd,
|
|
35756
|
+
slotCount,
|
|
35757
|
+
shiftDate,
|
|
35758
|
+
timezone,
|
|
35759
|
+
now: now4
|
|
35760
|
+
}) => {
|
|
35761
|
+
const normalizedSlotCount = Number.isFinite(slotCount) && slotCount > 0 ? slotCount : 0;
|
|
35762
|
+
if (!shiftDate || !timezone) {
|
|
35763
|
+
return normalizedSlotCount;
|
|
35764
|
+
}
|
|
35765
|
+
const shiftMinutes = getShiftDurationMinutes(shiftStart, shiftEnd);
|
|
35766
|
+
const elapsedMinutes = getShiftElapsedMinutes({
|
|
35767
|
+
shiftStart,
|
|
35768
|
+
shiftEnd,
|
|
35769
|
+
shiftDate,
|
|
35770
|
+
timezone,
|
|
35771
|
+
now: now4
|
|
35772
|
+
});
|
|
35773
|
+
if (shiftMinutes === null || elapsedMinutes === null) {
|
|
35774
|
+
return normalizedSlotCount;
|
|
35775
|
+
}
|
|
35776
|
+
if (elapsedMinutes >= shiftMinutes) {
|
|
35777
|
+
return normalizedSlotCount;
|
|
35778
|
+
}
|
|
35779
|
+
return Math.max(0, Math.min(elapsedMinutes / 60, normalizedSlotCount));
|
|
35780
|
+
};
|
|
35781
|
+
var formatSkuRailLabel = (label, segmentWidth, options = {}) => {
|
|
35782
|
+
const horizontalPadding = options.horizontalPadding ?? 8;
|
|
35783
|
+
const minVisibleChars = options.minVisibleChars ?? 4;
|
|
35784
|
+
const averageCharacterWidth = options.averageCharacterWidth ?? 6.6;
|
|
35785
|
+
const compactAverageCharacterWidth = options.compactAverageCharacterWidth ?? 5;
|
|
35786
|
+
const cssTruncation = options.cssTruncation ?? false;
|
|
35787
|
+
const fullLabelThreshold = 6;
|
|
35788
|
+
if (!label) return null;
|
|
35789
|
+
if (!Number.isFinite(segmentWidth) || segmentWidth <= horizontalPadding * 2) return null;
|
|
35790
|
+
const usableWidth = Math.max(segmentWidth - horizontalPadding * 2, 0);
|
|
35791
|
+
const maxChars = Math.floor(usableWidth / averageCharacterWidth);
|
|
35792
|
+
if (maxChars >= Math.max(minVisibleChars, fullLabelThreshold)) {
|
|
35793
|
+
if (label.length <= maxChars || cssTruncation) return label;
|
|
35794
|
+
if (maxChars <= 1) return null;
|
|
35795
|
+
return `${label.slice(0, Math.max(maxChars - 1, 1)).trimEnd()}\u2026`;
|
|
35796
|
+
}
|
|
35797
|
+
const compactLabel = buildCompactSkuRailLabel(label, Math.max(maxChars, 1));
|
|
35798
|
+
const compactMaxChars = Math.floor(usableWidth / compactAverageCharacterWidth);
|
|
35799
|
+
if (compactMaxChars < 1) return null;
|
|
35800
|
+
if (compactLabel.length <= compactMaxChars || cssTruncation) return compactLabel;
|
|
35801
|
+
if (compactMaxChars <= 2) {
|
|
35802
|
+
return compactLabel.slice(0, compactMaxChars);
|
|
35803
|
+
}
|
|
35804
|
+
return `${compactLabel.slice(0, Math.max(compactMaxChars - 1, 1)).trimEnd()}\u2026`;
|
|
35805
|
+
};
|
|
35806
|
+
var buildCompactSkuRailLabel = (label, maxChars) => {
|
|
35807
|
+
const words = label.trim().split(/\s+/).filter(Boolean);
|
|
35808
|
+
if (words.length === 0) return label;
|
|
35809
|
+
const [firstWord] = words;
|
|
35810
|
+
const acronym = words.map((word) => word[0]).join("").toUpperCase();
|
|
35811
|
+
if (maxChars <= 3 && acronym) {
|
|
35812
|
+
return acronym;
|
|
35813
|
+
}
|
|
35814
|
+
if (firstWord.length >= 4) {
|
|
35815
|
+
return firstWord;
|
|
35816
|
+
}
|
|
35817
|
+
return acronym || firstWord;
|
|
35818
|
+
};
|
|
35201
35819
|
var HourlyOutputChartComponent = ({
|
|
35202
35820
|
data,
|
|
35203
35821
|
pphThreshold,
|
|
35822
|
+
hourlyTargetOutput,
|
|
35204
35823
|
shiftStart,
|
|
35205
35824
|
shiftEnd,
|
|
35825
|
+
shiftBreaks = [],
|
|
35206
35826
|
showIdleTime = false,
|
|
35207
35827
|
idleTimeHourly,
|
|
35828
|
+
shiftDate,
|
|
35829
|
+
timezone,
|
|
35208
35830
|
skuSegments,
|
|
35209
35831
|
activeSkuId,
|
|
35210
35832
|
className = ""
|
|
@@ -35212,6 +35834,7 @@ var HourlyOutputChartComponent = ({
|
|
|
35212
35834
|
const containerRef = React144__default.useRef(null);
|
|
35213
35835
|
const [containerReady, setContainerReady] = React144__default.useState(false);
|
|
35214
35836
|
const [containerWidth, setContainerWidth] = React144__default.useState(0);
|
|
35837
|
+
const [hoveredSkuRailLabel, setHoveredSkuRailLabel] = React144__default.useState(null);
|
|
35215
35838
|
const idleSlots = React144__default.useMemo(
|
|
35216
35839
|
() => buildHourlyIdleSlots({
|
|
35217
35840
|
idleTimeHourly,
|
|
@@ -35357,14 +35980,54 @@ var HourlyOutputChartComponent = ({
|
|
|
35357
35980
|
}, [containerWidth]);
|
|
35358
35981
|
const shiftWindow = React144__default.useMemo(() => resolveShiftWindow2({
|
|
35359
35982
|
shiftStart,
|
|
35360
|
-
slotCount: SHIFT_DURATION
|
|
35361
|
-
|
|
35983
|
+
slotCount: SHIFT_DURATION,
|
|
35984
|
+
shiftDate,
|
|
35985
|
+
timezone
|
|
35986
|
+
}), [shiftStart, shiftEnd, SHIFT_DURATION, shiftDate, timezone]);
|
|
35987
|
+
const fallbackTimelineEndOffset = React144__default.useMemo(
|
|
35988
|
+
() => resolveTimelineEndOffset({
|
|
35989
|
+
shiftStart,
|
|
35990
|
+
shiftEnd,
|
|
35991
|
+
slotCount: SHIFT_DURATION,
|
|
35992
|
+
shiftDate,
|
|
35993
|
+
timezone
|
|
35994
|
+
}),
|
|
35995
|
+
[shiftStart, shiftEnd, SHIFT_DURATION, shiftDate, timezone]
|
|
35996
|
+
);
|
|
35997
|
+
const observedTimelineEndOffset = React144__default.useMemo(() => {
|
|
35998
|
+
let lastObservedMinute = -1;
|
|
35999
|
+
idleSlots.forEach((slot) => {
|
|
36000
|
+
slot.idleArray.forEach((value, minuteIndex) => {
|
|
36001
|
+
if (value !== "x" && value !== null && value !== void 0) {
|
|
36002
|
+
lastObservedMinute = Math.max(
|
|
36003
|
+
lastObservedMinute,
|
|
36004
|
+
slot.hourIndex * 60 + minuteIndex
|
|
36005
|
+
);
|
|
36006
|
+
}
|
|
36007
|
+
});
|
|
36008
|
+
});
|
|
36009
|
+
if (lastObservedMinute < 0) return null;
|
|
36010
|
+
return Math.min((lastObservedMinute + 1) / 60, SHIFT_DURATION);
|
|
36011
|
+
}, [idleSlots, SHIFT_DURATION]);
|
|
36012
|
+
const timelineEndOffset = observedTimelineEndOffset ?? fallbackTimelineEndOffset;
|
|
36013
|
+
const targetLineEndOffset = React144__default.useMemo(() => {
|
|
36014
|
+
if (timelineEndOffset >= SHIFT_DURATION) {
|
|
36015
|
+
return SHIFT_DURATION;
|
|
36016
|
+
}
|
|
36017
|
+
if (Number.isInteger(timelineEndOffset)) {
|
|
36018
|
+
return timelineEndOffset;
|
|
36019
|
+
}
|
|
36020
|
+
return Math.min(Math.floor(timelineEndOffset) + 1, SHIFT_DURATION);
|
|
36021
|
+
}, [timelineEndOffset, SHIFT_DURATION]);
|
|
35362
36022
|
const skuTimelineSegments = React144__default.useMemo(() => {
|
|
35363
|
-
if (!skuSegments || skuSegments.length === 0 || !shiftWindow)
|
|
35364
|
-
|
|
35365
|
-
|
|
35366
|
-
|
|
35367
|
-
|
|
36023
|
+
if (!skuSegments || skuSegments.length === 0 || !shiftWindow || timelineEndOffset <= 0) {
|
|
36024
|
+
return [];
|
|
36025
|
+
}
|
|
36026
|
+
const withOffsets = skuSegments.flatMap((segment) => {
|
|
36027
|
+
const offset = computeSegmentOffset(segment, shiftWindow);
|
|
36028
|
+
if (offset === null) return [];
|
|
36029
|
+
return [{ segment, offset }];
|
|
36030
|
+
}).sort((a, b) => a.offset - b.offset);
|
|
35368
36031
|
if (withOffsets.length === 0) return [];
|
|
35369
36032
|
const deduped = [];
|
|
35370
36033
|
const DUPLICATE_OFFSET_THRESHOLD = 1 / 60;
|
|
@@ -35378,9 +36041,9 @@ var HourlyOutputChartComponent = ({
|
|
|
35378
36041
|
deduped[0] = { ...deduped[0], offset: 0 };
|
|
35379
36042
|
}
|
|
35380
36043
|
return deduped.map((entry, index) => {
|
|
35381
|
-
const nextOffset = index < deduped.length - 1 ? deduped[index + 1].offset :
|
|
35382
|
-
const start = Math.max(0, Math.min(entry.offset,
|
|
35383
|
-
const end = Math.max(start, Math.min(nextOffset,
|
|
36044
|
+
const nextOffset = index < deduped.length - 1 ? deduped[index + 1].offset : timelineEndOffset;
|
|
36045
|
+
const start = Math.max(0, Math.min(entry.offset, timelineEndOffset));
|
|
36046
|
+
const end = Math.max(start, Math.min(nextOffset, timelineEndOffset));
|
|
35384
36047
|
return {
|
|
35385
36048
|
skuId: entry.segment.sku_id,
|
|
35386
36049
|
label: entry.segment.sku_code,
|
|
@@ -35389,15 +36052,94 @@ var HourlyOutputChartComponent = ({
|
|
|
35389
36052
|
pphThreshold: entry.segment.pph_threshold ?? pphThreshold
|
|
35390
36053
|
};
|
|
35391
36054
|
}).filter((segment) => segment.end > segment.start);
|
|
35392
|
-
}, [skuSegments, shiftWindow,
|
|
36055
|
+
}, [skuSegments, shiftWindow, timelineEndOffset, pphThreshold]);
|
|
36056
|
+
const targetTimelineSegments = React144__default.useMemo(() => {
|
|
36057
|
+
if (skuTimelineSegments.length === 0) return [];
|
|
36058
|
+
return skuTimelineSegments.map((segment, index) => ({
|
|
36059
|
+
...segment,
|
|
36060
|
+
end: index === skuTimelineSegments.length - 1 ? Math.max(segment.start, targetLineEndOffset) : segment.end
|
|
36061
|
+
})).filter((segment) => segment.end > segment.start);
|
|
36062
|
+
}, [skuTimelineSegments, targetLineEndOffset]);
|
|
36063
|
+
const hasExplicitHourlyTargetOutputProp = React144__default.useMemo(
|
|
36064
|
+
() => hourlyTargetOutput !== void 0,
|
|
36065
|
+
[hourlyTargetOutput]
|
|
36066
|
+
);
|
|
36067
|
+
const fallbackHourlyTargetOutput = React144__default.useMemo(() => {
|
|
36068
|
+
if (hasExplicitHourlyTargetOutputProp) return void 0;
|
|
36069
|
+
if (skuTimelineSegments.length > 0) return void 0;
|
|
36070
|
+
const plan = buildHourlyTargetPlan({
|
|
36071
|
+
shiftStart,
|
|
36072
|
+
shiftEnd,
|
|
36073
|
+
breaks: shiftBreaks,
|
|
36074
|
+
pphThreshold,
|
|
36075
|
+
rounding: "floor"
|
|
36076
|
+
});
|
|
36077
|
+
if (!plan.targets.length) return void 0;
|
|
36078
|
+
return plan.targets.map((value) => Number.isFinite(value) ? value : null);
|
|
36079
|
+
}, [
|
|
36080
|
+
hasExplicitHourlyTargetOutputProp,
|
|
36081
|
+
skuTimelineSegments.length,
|
|
36082
|
+
shiftStart,
|
|
36083
|
+
shiftEnd,
|
|
36084
|
+
shiftBreaks,
|
|
36085
|
+
pphThreshold
|
|
36086
|
+
]);
|
|
36087
|
+
const effectiveHourlyTargetOutput = React144__default.useMemo(
|
|
36088
|
+
() => hasExplicitHourlyTargetOutputProp ? hourlyTargetOutput : fallbackHourlyTargetOutput,
|
|
36089
|
+
[hasExplicitHourlyTargetOutputProp, hourlyTargetOutput, fallbackHourlyTargetOutput]
|
|
36090
|
+
);
|
|
36091
|
+
const hasHourlyTargetOutputProp = React144__default.useMemo(
|
|
36092
|
+
() => effectiveHourlyTargetOutput !== void 0,
|
|
36093
|
+
[effectiveHourlyTargetOutput]
|
|
36094
|
+
);
|
|
36095
|
+
const hasExplicitHourlyTargets = React144__default.useMemo(
|
|
36096
|
+
() => Array.isArray(effectiveHourlyTargetOutput) && effectiveHourlyTargetOutput.some((value) => value !== null && value !== void 0),
|
|
36097
|
+
[effectiveHourlyTargetOutput]
|
|
36098
|
+
);
|
|
36099
|
+
const hourlyTargetSegments = React144__default.useMemo(() => {
|
|
36100
|
+
if (!hasExplicitHourlyTargets) return [];
|
|
36101
|
+
const segments = [];
|
|
36102
|
+
let runStart = null;
|
|
36103
|
+
let runValue = null;
|
|
36104
|
+
const flush = (endIndex) => {
|
|
36105
|
+
if (runStart === null || runValue === null) return;
|
|
36106
|
+
segments.push({ start: runStart, end: endIndex, value: runValue });
|
|
36107
|
+
runStart = null;
|
|
36108
|
+
runValue = null;
|
|
36109
|
+
};
|
|
36110
|
+
for (let i = 0; i < SHIFT_DURATION; i += 1) {
|
|
36111
|
+
const rawValue = Array.isArray(effectiveHourlyTargetOutput) ? effectiveHourlyTargetOutput[i] : null;
|
|
36112
|
+
const value = rawValue === null || rawValue === void 0 ? null : Number(rawValue);
|
|
36113
|
+
if (value === null || !Number.isFinite(value)) {
|
|
36114
|
+
flush(i);
|
|
36115
|
+
continue;
|
|
36116
|
+
}
|
|
36117
|
+
if (runStart === null || runValue === null) {
|
|
36118
|
+
runStart = i;
|
|
36119
|
+
runValue = value;
|
|
36120
|
+
continue;
|
|
36121
|
+
}
|
|
36122
|
+
if (Math.abs(runValue - value) > 1e-6) {
|
|
36123
|
+
flush(i);
|
|
36124
|
+
runStart = i;
|
|
36125
|
+
runValue = value;
|
|
36126
|
+
}
|
|
36127
|
+
}
|
|
36128
|
+
flush(SHIFT_DURATION);
|
|
36129
|
+
return segments.filter((segment) => segment.end > segment.start);
|
|
36130
|
+
}, [SHIFT_DURATION, hasExplicitHourlyTargets, effectiveHourlyTargetOutput]);
|
|
35393
36131
|
const activeSkuHourIndices = React144__default.useMemo(() => {
|
|
35394
36132
|
const indices = /* @__PURE__ */ new Set();
|
|
35395
36133
|
const targets = Array(SHIFT_DURATION).fill(pphThreshold);
|
|
35396
|
-
if (!skuSegments || !shiftWindow) return { indices, targets };
|
|
35397
|
-
const segmentsWithOffsets = skuSegments.
|
|
35398
|
-
|
|
35399
|
-
|
|
35400
|
-
|
|
36134
|
+
if (!skuSegments || !shiftWindow) return { indices, targets, hasTimeline: false };
|
|
36135
|
+
const segmentsWithOffsets = skuSegments.flatMap((segment) => {
|
|
36136
|
+
const offset = computeSegmentOffset(segment, shiftWindow);
|
|
36137
|
+
if (offset === null) return [];
|
|
36138
|
+
return [{ ...segment, offset }];
|
|
36139
|
+
}).sort((a, b) => a.offset - b.offset);
|
|
36140
|
+
if (segmentsWithOffsets.length === 0) {
|
|
36141
|
+
return { indices, targets, hasTimeline: false };
|
|
36142
|
+
}
|
|
35401
36143
|
for (let i = 0; i < SHIFT_DURATION; i++) {
|
|
35402
36144
|
const midpoint = i + 0.5;
|
|
35403
36145
|
let activeSeg = segmentsWithOffsets[0];
|
|
@@ -35417,13 +36159,15 @@ var HourlyOutputChartComponent = ({
|
|
|
35417
36159
|
}
|
|
35418
36160
|
}
|
|
35419
36161
|
}
|
|
35420
|
-
return { indices, targets };
|
|
36162
|
+
return { indices, targets, hasTimeline: true };
|
|
35421
36163
|
}, [skuSegments, activeSkuId, shiftWindow, SHIFT_DURATION, pphThreshold]);
|
|
35422
36164
|
const chartData = React144__default.useMemo(() => {
|
|
35423
|
-
const { indices, targets } = activeSkuHourIndices;
|
|
36165
|
+
const { indices, targets, hasTimeline } = activeSkuHourIndices;
|
|
35424
36166
|
return Array.from({ length: SHIFT_DURATION }, (_, i) => {
|
|
35425
36167
|
const idleSlot = idleSlots[i];
|
|
35426
|
-
const
|
|
36168
|
+
const explicitTarget = hasHourlyTargetOutputProp ? effectiveHourlyTargetOutput?.[i] ?? null : void 0;
|
|
36169
|
+
const currentTarget = hasHourlyTargetOutputProp ? explicitTarget : targets[i] || pphThreshold;
|
|
36170
|
+
const comparisonTarget = currentTarget === null || currentTarget === void 0 ? targets[i] || pphThreshold : currentTarget;
|
|
35427
36171
|
return {
|
|
35428
36172
|
hourIndex: idleSlot?.hourIndex ?? i,
|
|
35429
36173
|
hour: idleSlot?.hour || "",
|
|
@@ -35431,16 +36175,16 @@ var HourlyOutputChartComponent = ({
|
|
|
35431
36175
|
output: animatedData[i] || 0,
|
|
35432
36176
|
originalOutput: data[i] || 0,
|
|
35433
36177
|
// Keep original data for labels
|
|
35434
|
-
target: currentTarget,
|
|
35435
|
-
color: (animatedData[i] || 0) >=
|
|
36178
|
+
target: currentTarget ?? null,
|
|
36179
|
+
color: (animatedData[i] || 0) >= comparisonTarget ? "#00AB45" : "#E34329",
|
|
35436
36180
|
idleMinutes: idleSlot?.idleMinutes || 0,
|
|
35437
36181
|
idleArray: idleSlot?.idleArray || [],
|
|
35438
36182
|
skuIndex: i,
|
|
35439
36183
|
isHighlighted: indices.has(i),
|
|
35440
|
-
isDimmed: !!activeSkuId && !indices.has(i)
|
|
36184
|
+
isDimmed: hasTimeline && !!activeSkuId && !indices.has(i)
|
|
35441
36185
|
};
|
|
35442
36186
|
});
|
|
35443
|
-
}, [animatedData, data, pphThreshold, idleSlots, SHIFT_DURATION, activeSkuHourIndices, activeSkuId]);
|
|
36187
|
+
}, [animatedData, data, pphThreshold, idleSlots, SHIFT_DURATION, activeSkuHourIndices, activeSkuId, effectiveHourlyTargetOutput, hasHourlyTargetOutputProp]);
|
|
35444
36188
|
const renderSkuTimelineRail = React144__default.useCallback((props) => {
|
|
35445
36189
|
if (!skuTimelineSegments.length || SHIFT_DURATION <= 0) return null;
|
|
35446
36190
|
const offset = props?.offset;
|
|
@@ -35452,8 +36196,9 @@ var HourlyOutputChartComponent = ({
|
|
|
35452
36196
|
const railHeight = 3;
|
|
35453
36197
|
const railY = top - 10;
|
|
35454
36198
|
const baselineY = railY + railHeight / 2;
|
|
35455
|
-
const
|
|
35456
|
-
|
|
36199
|
+
const showHoverLabel = (label, centerX, rY) => {
|
|
36200
|
+
setHoveredSkuRailLabel({ label, centerX, railY: rY });
|
|
36201
|
+
};
|
|
35457
36202
|
return /* @__PURE__ */ jsxs("g", { children: [
|
|
35458
36203
|
/* @__PURE__ */ jsx(
|
|
35459
36204
|
"line",
|
|
@@ -35471,9 +36216,43 @@ var HourlyOutputChartComponent = ({
|
|
|
35471
36216
|
const xStart = left + segment.start / SHIFT_DURATION * width;
|
|
35472
36217
|
const xEnd = left + segment.end / SHIFT_DURATION * width;
|
|
35473
36218
|
const segmentWidth = Math.max(1, xEnd - xStart);
|
|
36219
|
+
const labelPadding = segmentWidth < 48 ? 4 : 8;
|
|
36220
|
+
const inlineLabelText = formatSkuRailLabel(segment.label, segmentWidth, {
|
|
36221
|
+
horizontalPadding: labelPadding,
|
|
36222
|
+
cssTruncation: true
|
|
36223
|
+
});
|
|
36224
|
+
const badgeLabelText = formatSkuRailLabel(segment.label, 28, {
|
|
36225
|
+
horizontalPadding: 4,
|
|
36226
|
+
cssTruncation: true
|
|
36227
|
+
});
|
|
36228
|
+
const isMicroSegment = segmentWidth < 18;
|
|
36229
|
+
const labelText = isMicroSegment ? badgeLabelText : inlineLabelText;
|
|
36230
|
+
const labelClipWidth = Math.max(segmentWidth - labelPadding * 2, 0);
|
|
35474
36231
|
const isActive = !!activeSkuId && segment.skuId === activeSkuId;
|
|
35475
36232
|
const isDimmed = !!activeSkuId && segment.skuId !== activeSkuId;
|
|
36233
|
+
const hoverHitWidth = Math.max(segmentWidth + 12, isMicroSegment ? 20 : segmentWidth);
|
|
36234
|
+
const hoverHitX = Math.max(
|
|
36235
|
+
left,
|
|
36236
|
+
Math.min(xStart - (hoverHitWidth - segmentWidth) / 2, left + width - hoverHitWidth)
|
|
36237
|
+
);
|
|
36238
|
+
const hoverHitHeight = isMicroSegment ? 34 : 28;
|
|
36239
|
+
const hoverHitY = railY - (isMicroSegment ? 30 : 24);
|
|
36240
|
+
const microBadgeWidth = labelText ? Math.max(labelText.length * 6.2 + 10, 18) : 0;
|
|
36241
|
+
const microBadgeX = xStart + segmentWidth / 2 - microBadgeWidth / 2;
|
|
36242
|
+
const microBadgeY = railY - 24;
|
|
35476
36243
|
return /* @__PURE__ */ jsxs("g", { children: [
|
|
36244
|
+
/* @__PURE__ */ jsx(
|
|
36245
|
+
"rect",
|
|
36246
|
+
{
|
|
36247
|
+
x: hoverHitX,
|
|
36248
|
+
y: hoverHitY,
|
|
36249
|
+
width: hoverHitWidth,
|
|
36250
|
+
height: hoverHitHeight,
|
|
36251
|
+
fill: "transparent",
|
|
36252
|
+
onMouseEnter: () => showHoverLabel(segment.label, xStart + segmentWidth / 2, railY),
|
|
36253
|
+
onMouseLeave: () => setHoveredSkuRailLabel((current) => current?.label === segment.label ? null : current)
|
|
36254
|
+
}
|
|
36255
|
+
),
|
|
35477
36256
|
/* @__PURE__ */ jsx(
|
|
35478
36257
|
"rect",
|
|
35479
36258
|
{
|
|
@@ -35484,7 +36263,7 @@ var HourlyOutputChartComponent = ({
|
|
|
35484
36263
|
rx: 1.5,
|
|
35485
36264
|
fill: isActive ? "#3b82f6" : "#cbd5e1",
|
|
35486
36265
|
opacity: isDimmed ? 0.3 : 1,
|
|
35487
|
-
style: { transition: "all 0.3s ease" }
|
|
36266
|
+
style: { transition: "all 0.3s ease", pointerEvents: "none" }
|
|
35488
36267
|
}
|
|
35489
36268
|
),
|
|
35490
36269
|
index > 0 && /* @__PURE__ */ jsx(
|
|
@@ -35496,22 +36275,59 @@ var HourlyOutputChartComponent = ({
|
|
|
35496
36275
|
y2: railY + railHeight + 3,
|
|
35497
36276
|
stroke: "#94a3b8",
|
|
35498
36277
|
strokeWidth: 1,
|
|
35499
|
-
opacity: 0.6
|
|
36278
|
+
opacity: 0.6,
|
|
36279
|
+
style: { pointerEvents: "none" }
|
|
35500
36280
|
}
|
|
35501
36281
|
),
|
|
35502
|
-
|
|
35503
|
-
|
|
36282
|
+
isMicroSegment && labelText && /* @__PURE__ */ jsxs("g", { style: { pointerEvents: "none" }, children: [
|
|
36283
|
+
/* @__PURE__ */ jsx(
|
|
36284
|
+
"rect",
|
|
36285
|
+
{
|
|
36286
|
+
x: microBadgeX,
|
|
36287
|
+
y: microBadgeY,
|
|
36288
|
+
width: microBadgeWidth,
|
|
36289
|
+
height: 14,
|
|
36290
|
+
rx: 7,
|
|
36291
|
+
fill: "white",
|
|
36292
|
+
stroke: isActive ? "#3b82f6" : "#cbd5e1",
|
|
36293
|
+
strokeWidth: 1,
|
|
36294
|
+
opacity: isDimmed ? 0.55 : 0.98
|
|
36295
|
+
}
|
|
36296
|
+
),
|
|
36297
|
+
/* @__PURE__ */ jsx(
|
|
36298
|
+
"text",
|
|
36299
|
+
{
|
|
36300
|
+
x: xStart + segmentWidth / 2,
|
|
36301
|
+
y: microBadgeY + 10,
|
|
36302
|
+
textAnchor: "middle",
|
|
36303
|
+
fontSize: 8,
|
|
36304
|
+
fontWeight: 700,
|
|
36305
|
+
fill: isActive ? "#2563eb" : "#64748b",
|
|
36306
|
+
opacity: isDimmed ? 0.55 : 1,
|
|
36307
|
+
style: { transition: "all 0.3s ease" },
|
|
36308
|
+
children: labelText
|
|
36309
|
+
}
|
|
36310
|
+
)
|
|
36311
|
+
] }),
|
|
36312
|
+
!isMicroSegment && labelText && labelClipWidth > 0 && /* @__PURE__ */ jsx(
|
|
36313
|
+
"foreignObject",
|
|
35504
36314
|
{
|
|
35505
|
-
x: xStart +
|
|
35506
|
-
y:
|
|
35507
|
-
|
|
35508
|
-
|
|
35509
|
-
|
|
35510
|
-
|
|
35511
|
-
|
|
35512
|
-
|
|
35513
|
-
|
|
35514
|
-
|
|
36315
|
+
x: xStart + labelPadding,
|
|
36316
|
+
y: railY - 24,
|
|
36317
|
+
width: labelClipWidth,
|
|
36318
|
+
height: 18,
|
|
36319
|
+
style: { pointerEvents: "none", overflow: "visible" },
|
|
36320
|
+
children: /* @__PURE__ */ jsx(
|
|
36321
|
+
"div",
|
|
36322
|
+
{
|
|
36323
|
+
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"}`,
|
|
36324
|
+
style: {
|
|
36325
|
+
opacity: isDimmed ? 0.4 : 1,
|
|
36326
|
+
transition: "all 0.3s ease"
|
|
36327
|
+
},
|
|
36328
|
+
children: labelText
|
|
36329
|
+
}
|
|
36330
|
+
)
|
|
35515
36331
|
}
|
|
35516
36332
|
)
|
|
35517
36333
|
] }, `sku-rail-${segment.skuId}-${segment.start}-${index}`);
|
|
@@ -35569,14 +36385,17 @@ var HourlyOutputChartComponent = ({
|
|
|
35569
36385
|
);
|
|
35570
36386
|
}, [idleBarState.visible, idleBarState.key, idleBarState.shouldAnimate]);
|
|
35571
36387
|
const maxDataValue = Math.max(...data, 0);
|
|
35572
|
-
const
|
|
36388
|
+
const numericChartTargets = chartData.map((d) => d.target).filter((target) => target !== null && Number.isFinite(target));
|
|
36389
|
+
const maxTargetValue = Math.max(...numericChartTargets, pphThreshold, 0);
|
|
35573
36390
|
const maxYValue = Math.max(
|
|
35574
36391
|
Math.ceil(maxTargetValue * 1.5),
|
|
35575
36392
|
Math.ceil(maxDataValue * 1.15)
|
|
35576
36393
|
// Add 15% headroom above max value
|
|
35577
36394
|
);
|
|
35578
36395
|
const generateYAxisTicks = () => {
|
|
35579
|
-
const uniqueTargets = [...new Set(
|
|
36396
|
+
const uniqueTargets = [...new Set(
|
|
36397
|
+
chartData.map((d) => d.target).filter((target) => target !== null && Number.isFinite(target)).map((target) => Math.round(target))
|
|
36398
|
+
)].sort((a, b) => a - b);
|
|
35580
36399
|
const rawTicks = [0];
|
|
35581
36400
|
uniqueTargets.forEach((target) => {
|
|
35582
36401
|
if (target > 0) {
|
|
@@ -35611,14 +36430,54 @@ var HourlyOutputChartComponent = ({
|
|
|
35611
36430
|
};
|
|
35612
36431
|
const renderTargetLine = React144__default.useCallback((props) => {
|
|
35613
36432
|
const { offset, yAxisMap } = props;
|
|
35614
|
-
if (!offset || !yAxisMap || SHIFT_DURATION <= 0) return null;
|
|
36433
|
+
if (!offset || !yAxisMap || SHIFT_DURATION <= 0 || targetLineEndOffset <= 0) return null;
|
|
35615
36434
|
const { left, width } = offset;
|
|
35616
36435
|
const yAxis = yAxisMap["default"] || yAxisMap[0];
|
|
35617
36436
|
if (!yAxis || !yAxis.scale) return null;
|
|
35618
36437
|
const lines = [];
|
|
35619
36438
|
const offsetToX = (o) => left + o / SHIFT_DURATION * width;
|
|
35620
|
-
if (
|
|
35621
|
-
|
|
36439
|
+
if (hasHourlyTargetOutputProp && hourlyTargetSegments.length > 0) {
|
|
36440
|
+
hourlyTargetSegments.forEach((segment, index) => {
|
|
36441
|
+
const y = yAxis.scale(segment.value);
|
|
36442
|
+
const xStart = offsetToX(segment.start);
|
|
36443
|
+
const xEnd = offsetToX(segment.end);
|
|
36444
|
+
lines.push(
|
|
36445
|
+
/* @__PURE__ */ jsx(
|
|
36446
|
+
"line",
|
|
36447
|
+
{
|
|
36448
|
+
x1: xStart,
|
|
36449
|
+
y1: y,
|
|
36450
|
+
x2: xEnd,
|
|
36451
|
+
y2: y,
|
|
36452
|
+
stroke: "#E34329",
|
|
36453
|
+
strokeDasharray: "3 3",
|
|
36454
|
+
strokeWidth: 2
|
|
36455
|
+
},
|
|
36456
|
+
`target-hourly-h-${index}`
|
|
36457
|
+
)
|
|
36458
|
+
);
|
|
36459
|
+
const next = hourlyTargetSegments[index + 1];
|
|
36460
|
+
if (next && Math.abs(next.value - segment.value) > 1e-6) {
|
|
36461
|
+
const nextY = yAxis.scale(next.value);
|
|
36462
|
+
lines.push(
|
|
36463
|
+
/* @__PURE__ */ jsx(
|
|
36464
|
+
"line",
|
|
36465
|
+
{
|
|
36466
|
+
x1: xEnd,
|
|
36467
|
+
y1: y,
|
|
36468
|
+
x2: xEnd,
|
|
36469
|
+
y2: nextY,
|
|
36470
|
+
stroke: "#E34329",
|
|
36471
|
+
strokeDasharray: "3 3",
|
|
36472
|
+
strokeWidth: 2
|
|
36473
|
+
},
|
|
36474
|
+
`target-hourly-v-${index}`
|
|
36475
|
+
)
|
|
36476
|
+
);
|
|
36477
|
+
}
|
|
36478
|
+
});
|
|
36479
|
+
} else if (!hasHourlyTargetOutputProp && targetTimelineSegments.length > 0) {
|
|
36480
|
+
targetTimelineSegments.forEach((segment, index) => {
|
|
35622
36481
|
const target = segment.pphThreshold || pphThreshold;
|
|
35623
36482
|
const y = yAxis.scale(target);
|
|
35624
36483
|
const xStart = offsetToX(segment.start);
|
|
@@ -35638,7 +36497,7 @@ var HourlyOutputChartComponent = ({
|
|
|
35638
36497
|
`target-h-${index}`
|
|
35639
36498
|
)
|
|
35640
36499
|
);
|
|
35641
|
-
const next =
|
|
36500
|
+
const next = targetTimelineSegments[index + 1];
|
|
35642
36501
|
if (next) {
|
|
35643
36502
|
const nextTarget = next.pphThreshold || pphThreshold;
|
|
35644
36503
|
if (nextTarget !== target) {
|
|
@@ -35661,7 +36520,7 @@ var HourlyOutputChartComponent = ({
|
|
|
35661
36520
|
}
|
|
35662
36521
|
}
|
|
35663
36522
|
});
|
|
35664
|
-
} else {
|
|
36523
|
+
} else if (!hasHourlyTargetOutputProp) {
|
|
35665
36524
|
const y = yAxis.scale(pphThreshold);
|
|
35666
36525
|
lines.push(
|
|
35667
36526
|
/* @__PURE__ */ jsx(
|
|
@@ -35680,10 +36539,13 @@ var HourlyOutputChartComponent = ({
|
|
|
35680
36539
|
);
|
|
35681
36540
|
}
|
|
35682
36541
|
return /* @__PURE__ */ jsx("g", { children: lines });
|
|
35683
|
-
}, [
|
|
36542
|
+
}, [hourlyTargetSegments, targetTimelineSegments, SHIFT_DURATION, pphThreshold, targetLineEndOffset, hasHourlyTargetOutputProp]);
|
|
35684
36543
|
const renderLegend = () => {
|
|
35685
|
-
const uniqueTargets = [...new Set(
|
|
35686
|
-
|
|
36544
|
+
const uniqueTargets = [...new Set(
|
|
36545
|
+
chartData.map((d) => d.target).filter((target) => target !== null && Number.isFinite(target)).map((target) => Math.round(target))
|
|
36546
|
+
)].sort((a, b) => a - b);
|
|
36547
|
+
const unitLabel = hasHourlyTargetOutputProp ? "units" : "units/hr";
|
|
36548
|
+
const targetText = uniqueTargets.length === 0 ? `Target` : uniqueTargets.length === 1 ? `Target: ${uniqueTargets[0]} ${unitLabel}` : `Target: ${uniqueTargets[0]} - ${uniqueTargets[uniqueTargets.length - 1]} ${unitLabel}`;
|
|
35687
36549
|
return /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center text-sm text-gray-600 bg-white py-1", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border border-gray-100 rounded-full px-3 py-1", children: [
|
|
35688
36550
|
/* @__PURE__ */ jsx("div", { className: "w-8 flex items-center", children: /* @__PURE__ */ jsx("div", { className: "w-full border-t-2 border-[#E34329] border-dashed" }) }),
|
|
35689
36551
|
/* @__PURE__ */ jsx("span", { children: targetText })
|
|
@@ -35696,254 +36558,274 @@ var HourlyOutputChartComponent = ({
|
|
|
35696
36558
|
className: `w-full h-full min-w-0 flex flex-col ${className}`,
|
|
35697
36559
|
style: { minHeight: "200px", minWidth: 0 },
|
|
35698
36560
|
children: [
|
|
35699
|
-
containerReady ? /* @__PURE__ */
|
|
35700
|
-
|
|
35701
|
-
|
|
35702
|
-
|
|
35703
|
-
|
|
35704
|
-
|
|
35705
|
-
|
|
35706
|
-
|
|
35707
|
-
|
|
35708
|
-
|
|
35709
|
-
|
|
35710
|
-
|
|
35711
|
-
|
|
35712
|
-
|
|
35713
|
-
|
|
35714
|
-
|
|
35715
|
-
|
|
35716
|
-
|
|
35717
|
-
|
|
35718
|
-
|
|
35719
|
-
|
|
35720
|
-
|
|
35721
|
-
|
|
35722
|
-
|
|
35723
|
-
|
|
35724
|
-
|
|
35725
|
-
|
|
35726
|
-
|
|
35727
|
-
|
|
35728
|
-
),
|
|
35729
|
-
/* @__PURE__ */ jsx(
|
|
35730
|
-
XAxis,
|
|
35731
|
-
{
|
|
35732
|
-
xAxisId: "sku",
|
|
35733
|
-
type: "number",
|
|
35734
|
-
dataKey: "skuIndex",
|
|
35735
|
-
domain: [0, Math.max(SHIFT_DURATION, 0)],
|
|
35736
|
-
hide: true,
|
|
35737
|
-
allowDataOverflow: true
|
|
35738
|
-
}
|
|
35739
|
-
),
|
|
35740
|
-
/* @__PURE__ */ jsx(
|
|
35741
|
-
YAxis,
|
|
35742
|
-
{
|
|
35743
|
-
yAxisId: "default",
|
|
35744
|
-
tickMargin: 8,
|
|
35745
|
-
width: 48,
|
|
35746
|
-
domain: [0, maxYValue],
|
|
35747
|
-
ticks: generateYAxisTicks(),
|
|
35748
|
-
tickFormatter: (value) => value,
|
|
35749
|
-
tick: (props) => {
|
|
35750
|
-
const { x, y, payload } = props;
|
|
35751
|
-
return /* @__PURE__ */ jsx("g", { transform: `translate(${x},${y})`, children: /* @__PURE__ */ jsx(
|
|
35752
|
-
"text",
|
|
35753
|
-
{
|
|
35754
|
-
x: -2,
|
|
35755
|
-
y: 0,
|
|
35756
|
-
dy: 4,
|
|
35757
|
-
textAnchor: "end",
|
|
35758
|
-
fill: "#666",
|
|
35759
|
-
fontSize: 12,
|
|
35760
|
-
children: payload.value
|
|
35761
|
-
},
|
|
35762
|
-
`tick-${payload.value}-${x}-${y}`
|
|
35763
|
-
) });
|
|
36561
|
+
containerReady ? /* @__PURE__ */ jsxs("div", { className: "flex-1 min-h-0 relative", children: [
|
|
36562
|
+
/* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(
|
|
36563
|
+
BarChart$1,
|
|
36564
|
+
{
|
|
36565
|
+
data: chartData,
|
|
36566
|
+
margin: {
|
|
36567
|
+
// Reserve headroom for the SKU timeline rail + staggered
|
|
36568
|
+
// labels only when SKU segments are rendered. Non-SKU charts
|
|
36569
|
+
// keep the original 10px top so recharts has enough vertical
|
|
36570
|
+
// space to show the target (pph) tick label on the Y-axis.
|
|
36571
|
+
top: skuTimelineSegments.length > 0 ? 40 : 10,
|
|
36572
|
+
right: 10,
|
|
36573
|
+
bottom: 10,
|
|
36574
|
+
left: 6
|
|
36575
|
+
},
|
|
36576
|
+
barCategoryGap: "25%",
|
|
36577
|
+
children: [
|
|
36578
|
+
/* @__PURE__ */ jsx(CartesianGrid, { strokeDasharray: "3 3", vertical: false }),
|
|
36579
|
+
/* @__PURE__ */ jsx(
|
|
36580
|
+
XAxis,
|
|
36581
|
+
{
|
|
36582
|
+
xAxisId: "default",
|
|
36583
|
+
dataKey: "hour",
|
|
36584
|
+
tick: { fontSize: xAxisConfig.tickFont },
|
|
36585
|
+
interval: xAxisConfig.interval,
|
|
36586
|
+
angle: xAxisConfig.angle,
|
|
36587
|
+
textAnchor: "end",
|
|
36588
|
+
tickMargin: xAxisConfig.tickMargin,
|
|
36589
|
+
height: xAxisConfig.height
|
|
35764
36590
|
}
|
|
35765
|
-
|
|
35766
|
-
|
|
35767
|
-
|
|
35768
|
-
|
|
35769
|
-
|
|
35770
|
-
|
|
35771
|
-
|
|
35772
|
-
|
|
35773
|
-
|
|
35774
|
-
|
|
35775
|
-
|
|
35776
|
-
|
|
35777
|
-
|
|
35778
|
-
|
|
35779
|
-
|
|
35780
|
-
|
|
35781
|
-
|
|
35782
|
-
|
|
35783
|
-
|
|
35784
|
-
|
|
35785
|
-
|
|
35786
|
-
|
|
35787
|
-
|
|
35788
|
-
|
|
35789
|
-
|
|
35790
|
-
|
|
35791
|
-
|
|
35792
|
-
|
|
35793
|
-
|
|
35794
|
-
|
|
35795
|
-
|
|
35796
|
-
|
|
35797
|
-
|
|
35798
|
-
|
|
35799
|
-
|
|
35800
|
-
|
|
35801
|
-
|
|
35802
|
-
|
|
35803
|
-
|
|
35804
|
-
|
|
35805
|
-
|
|
35806
|
-
|
|
35807
|
-
|
|
35808
|
-
|
|
35809
|
-
|
|
35810
|
-
|
|
35811
|
-
|
|
36591
|
+
),
|
|
36592
|
+
/* @__PURE__ */ jsx(
|
|
36593
|
+
XAxis,
|
|
36594
|
+
{
|
|
36595
|
+
xAxisId: "sku",
|
|
36596
|
+
type: "number",
|
|
36597
|
+
dataKey: "skuIndex",
|
|
36598
|
+
domain: [0, Math.max(SHIFT_DURATION, 0)],
|
|
36599
|
+
hide: true,
|
|
36600
|
+
allowDataOverflow: true
|
|
36601
|
+
}
|
|
36602
|
+
),
|
|
36603
|
+
/* @__PURE__ */ jsx(
|
|
36604
|
+
YAxis,
|
|
36605
|
+
{
|
|
36606
|
+
yAxisId: "default",
|
|
36607
|
+
tickMargin: 8,
|
|
36608
|
+
width: 48,
|
|
36609
|
+
domain: [0, maxYValue],
|
|
36610
|
+
ticks: generateYAxisTicks(),
|
|
36611
|
+
tickFormatter: (value) => value,
|
|
36612
|
+
tick: (props) => {
|
|
36613
|
+
const { x, y, payload } = props;
|
|
36614
|
+
return /* @__PURE__ */ jsx("g", { transform: `translate(${x},${y})`, children: /* @__PURE__ */ jsx(
|
|
36615
|
+
"text",
|
|
36616
|
+
{
|
|
36617
|
+
x: -2,
|
|
36618
|
+
y: 0,
|
|
36619
|
+
dy: 4,
|
|
36620
|
+
textAnchor: "end",
|
|
36621
|
+
fill: "#666",
|
|
36622
|
+
fontSize: 12,
|
|
36623
|
+
children: payload.value
|
|
36624
|
+
},
|
|
36625
|
+
`tick-${payload.value}-${x}-${y}`
|
|
36626
|
+
) });
|
|
36627
|
+
}
|
|
36628
|
+
}
|
|
36629
|
+
),
|
|
36630
|
+
/* @__PURE__ */ jsx(YAxis, { yAxisId: "idle", domain: [0, 60], hide: true }),
|
|
36631
|
+
/* @__PURE__ */ jsx(
|
|
36632
|
+
Tooltip,
|
|
36633
|
+
{
|
|
36634
|
+
cursor: { fill: "#f1f5f9" },
|
|
36635
|
+
contentStyle: { backgroundColor: "transparent", border: "none", padding: 0 },
|
|
36636
|
+
content: (props) => {
|
|
36637
|
+
if (!props.active || !props.payload || props.payload.length === 0)
|
|
36638
|
+
return null;
|
|
36639
|
+
const data2 = props.payload[0].payload;
|
|
36640
|
+
const idlePeriods = showIdleTime ? getHourlyIdlePeriods({
|
|
36641
|
+
idleArray: data2.idleArray,
|
|
36642
|
+
shiftStart,
|
|
36643
|
+
hourIndex: Number.isFinite(data2.hourIndex) ? data2.hourIndex : 0
|
|
36644
|
+
}) : [];
|
|
36645
|
+
return /* @__PURE__ */ 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: [
|
|
36646
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-between mb-4 pb-3 border-b border-slate-100", children: /* @__PURE__ */ jsx("p", { className: "font-semibold text-slate-900 text-sm tracking-tight", children: data2.timeRange }) }),
|
|
36647
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
36648
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
36649
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm text-slate-500 font-medium tracking-wide", children: "Output" }),
|
|
36650
|
+
/* @__PURE__ */ jsxs("span", { className: "font-bold text-slate-900 text-sm", children: [
|
|
36651
|
+
Math.round(data2.output),
|
|
36652
|
+
" ",
|
|
36653
|
+
/* @__PURE__ */ jsx("span", { className: "text-slate-400 font-normal text-xs ml-0.5", children: "units" })
|
|
36654
|
+
] })
|
|
36655
|
+
] }),
|
|
36656
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
36657
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm text-slate-500 font-medium tracking-wide", children: "Target" }),
|
|
36658
|
+
/* @__PURE__ */ jsxs("span", { className: "font-bold text-slate-700 text-sm", children: [
|
|
36659
|
+
Math.round(data2.target),
|
|
36660
|
+
" ",
|
|
36661
|
+
/* @__PURE__ */ jsx("span", { className: "text-slate-400 font-normal text-xs ml-0.5", children: "units" })
|
|
36662
|
+
] })
|
|
36663
|
+
] }),
|
|
36664
|
+
showIdleTime && data2.idleMinutes > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
36665
|
+
/* @__PURE__ */ jsx("div", { className: "pt-3 mt-3 border-t border-slate-100", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
36666
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm text-slate-500 font-medium tracking-wide", children: "Idle Time" }),
|
|
36667
|
+
/* @__PURE__ */ jsxs("span", { className: "font-bold text-orange-600 text-sm flex items-center gap-1.5", children: [
|
|
36668
|
+
/* @__PURE__ */ 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" }),
|
|
36669
|
+
data2.idleMinutes,
|
|
36670
|
+
" ",
|
|
36671
|
+
/* @__PURE__ */ jsx("span", { className: "text-orange-500/70 font-normal text-xs ml-0.5", children: "min" })
|
|
36672
|
+
] })
|
|
36673
|
+
] }) }),
|
|
36674
|
+
idlePeriods.length > 0 && /* @__PURE__ */ jsxs("div", { className: "mt-3 bg-slate-50/80 rounded-lg p-3 border border-slate-100/50", children: [
|
|
36675
|
+
/* @__PURE__ */ jsx("p", { className: "font-semibold text-slate-400 text-[10px] mb-2.5 uppercase tracking-wider", children: "Idle Periods" }),
|
|
36676
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-2.5 max-h-32 overflow-y-auto pr-1 custom-scrollbar", children: idlePeriods.map((period, index) => {
|
|
36677
|
+
return /* @__PURE__ */ jsxs(
|
|
36678
|
+
"div",
|
|
36679
|
+
{
|
|
36680
|
+
className: "flex items-start gap-2.5 text-xs",
|
|
36681
|
+
children: [
|
|
36682
|
+
/* @__PURE__ */ 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)]" }),
|
|
36683
|
+
/* @__PURE__ */ jsx("span", { className: "text-slate-700 font-medium tracking-tight", children: period.duration === 1 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
36684
|
+
period.startTime,
|
|
36685
|
+
" ",
|
|
36686
|
+
/* @__PURE__ */ jsxs("span", { className: "text-slate-400 font-normal ml-1", children: [
|
|
36687
|
+
"(",
|
|
36688
|
+
period.duration,
|
|
36689
|
+
"m)"
|
|
36690
|
+
] })
|
|
36691
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
36692
|
+
period.startTime,
|
|
36693
|
+
" ",
|
|
36694
|
+
/* @__PURE__ */ jsx("span", { className: "text-slate-400 mx-0.5", children: "\u2192" }),
|
|
36695
|
+
" ",
|
|
36696
|
+
period.endTime,
|
|
36697
|
+
" ",
|
|
36698
|
+
/* @__PURE__ */ jsxs("span", { className: "text-slate-400 font-normal ml-1", children: [
|
|
36699
|
+
"(",
|
|
36700
|
+
period.duration,
|
|
36701
|
+
"m)"
|
|
36702
|
+
] })
|
|
36703
|
+
] }) })
|
|
36704
|
+
]
|
|
36705
|
+
},
|
|
36706
|
+
index
|
|
36707
|
+
);
|
|
36708
|
+
}) })
|
|
35812
36709
|
] })
|
|
35813
|
-
] }) }),
|
|
35814
|
-
idlePeriods.length > 0 && /* @__PURE__ */ jsxs("div", { className: "mt-3 bg-gray-50 rounded-lg p-2.5", children: [
|
|
35815
|
-
/* @__PURE__ */ jsx("p", { className: "font-medium text-gray-700 text-xs mb-2", children: "Idle periods:" }),
|
|
35816
|
-
/* @__PURE__ */ jsx("div", { className: "space-y-1 max-h-32 overflow-y-auto pr-1", children: idlePeriods.map((period, index) => {
|
|
35817
|
-
return /* @__PURE__ */ jsxs(
|
|
35818
|
-
"div",
|
|
35819
|
-
{
|
|
35820
|
-
className: "text-gray-600 flex items-center gap-2 text-xs",
|
|
35821
|
-
children: [
|
|
35822
|
-
/* @__PURE__ */ jsx("span", { className: "inline-block w-1.5 h-1.5 bg-orange-400 rounded-full flex-shrink-0" }),
|
|
35823
|
-
/* @__PURE__ */ jsx("span", { children: period.duration === 1 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
35824
|
-
period.startTime,
|
|
35825
|
-
" (",
|
|
35826
|
-
period.duration,
|
|
35827
|
-
" min)"
|
|
35828
|
-
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
35829
|
-
period.startTime,
|
|
35830
|
-
" -",
|
|
35831
|
-
" ",
|
|
35832
|
-
period.endTime,
|
|
35833
|
-
" (",
|
|
35834
|
-
period.duration,
|
|
35835
|
-
" mins)"
|
|
35836
|
-
] }) })
|
|
35837
|
-
]
|
|
35838
|
-
},
|
|
35839
|
-
index
|
|
35840
|
-
);
|
|
35841
|
-
}) })
|
|
35842
36710
|
] })
|
|
35843
36711
|
] })
|
|
35844
|
-
] })
|
|
35845
|
-
|
|
35846
|
-
|
|
35847
|
-
|
|
35848
|
-
|
|
35849
|
-
|
|
35850
|
-
|
|
35851
|
-
|
|
35852
|
-
|
|
35853
|
-
|
|
35854
|
-
|
|
35855
|
-
|
|
35856
|
-
|
|
35857
|
-
|
|
35858
|
-
|
|
35859
|
-
|
|
35860
|
-
|
|
35861
|
-
|
|
35862
|
-
|
|
35863
|
-
|
|
35864
|
-
|
|
35865
|
-
|
|
35866
|
-
|
|
35867
|
-
|
|
35868
|
-
|
|
35869
|
-
|
|
35870
|
-
|
|
35871
|
-
|
|
35872
|
-
|
|
35873
|
-
|
|
35874
|
-
|
|
35875
|
-
|
|
35876
|
-
|
|
35877
|
-
|
|
35878
|
-
|
|
35879
|
-
|
|
36712
|
+
] });
|
|
36713
|
+
},
|
|
36714
|
+
animationDuration: 200
|
|
36715
|
+
}
|
|
36716
|
+
),
|
|
36717
|
+
/* @__PURE__ */ jsx(Customized, { component: renderTargetLine }),
|
|
36718
|
+
/* @__PURE__ */ jsx(Customized, { component: renderSkuTimelineRail }),
|
|
36719
|
+
/* @__PURE__ */ jsxs(
|
|
36720
|
+
Bar,
|
|
36721
|
+
{
|
|
36722
|
+
xAxisId: "default",
|
|
36723
|
+
dataKey: "output",
|
|
36724
|
+
yAxisId: "default",
|
|
36725
|
+
maxBarSize: 35,
|
|
36726
|
+
radius: [10, 10, 0, 0],
|
|
36727
|
+
isAnimationActive: false,
|
|
36728
|
+
children: [
|
|
36729
|
+
chartData.map((entry, index) => /* @__PURE__ */ jsx(
|
|
36730
|
+
Cell,
|
|
36731
|
+
{
|
|
36732
|
+
fill: entry.color,
|
|
36733
|
+
stroke: "transparent",
|
|
36734
|
+
strokeWidth: 0,
|
|
36735
|
+
style: {
|
|
36736
|
+
filter: entry.isHighlighted ? "drop-shadow(0 4px 6px rgba(0, 0, 0, 0.12)) brightness(1.05)" : "brightness(1)",
|
|
36737
|
+
transform: entry.isHighlighted ? "translateY(-4px)" : "translateY(0)",
|
|
36738
|
+
opacity: entry.isDimmed ? 0.4 : 1,
|
|
36739
|
+
transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)",
|
|
36740
|
+
cursor: "pointer"
|
|
36741
|
+
},
|
|
36742
|
+
onMouseEnter: (e) => {
|
|
36743
|
+
const target = e.target;
|
|
36744
|
+
target.style.filter = "drop-shadow(0 4px 6px rgba(0, 0, 0, 0.12)) brightness(1.05)";
|
|
36745
|
+
target.style.transform = "translateY(-4px)";
|
|
36746
|
+
target.style.opacity = "1";
|
|
36747
|
+
},
|
|
36748
|
+
onMouseLeave: (e) => {
|
|
36749
|
+
const target = e.target;
|
|
36750
|
+
target.style.filter = entry.isHighlighted ? "drop-shadow(0 4px 6px rgba(0, 0, 0, 0.12)) brightness(1.05)" : "brightness(1)";
|
|
36751
|
+
target.style.transform = entry.isHighlighted ? "translateY(-4px)" : "translateY(0)";
|
|
36752
|
+
target.style.opacity = entry.isDimmed ? "0.4" : "1";
|
|
36753
|
+
}
|
|
35880
36754
|
},
|
|
35881
|
-
|
|
35882
|
-
|
|
35883
|
-
|
|
35884
|
-
|
|
35885
|
-
|
|
36755
|
+
`cell-${index}`
|
|
36756
|
+
)),
|
|
36757
|
+
/* @__PURE__ */ jsx(
|
|
36758
|
+
LabelList,
|
|
36759
|
+
{
|
|
36760
|
+
dataKey: "originalOutput",
|
|
36761
|
+
position: "top",
|
|
36762
|
+
content: (props) => {
|
|
36763
|
+
const { x, y, width, value, payload } = props;
|
|
36764
|
+
const actualValue = payload?.originalOutput || value;
|
|
36765
|
+
if (!actualValue || actualValue === 0) return null;
|
|
36766
|
+
return /* @__PURE__ */ jsx(
|
|
36767
|
+
"text",
|
|
36768
|
+
{
|
|
36769
|
+
x: x + width / 2,
|
|
36770
|
+
y: y - 8,
|
|
36771
|
+
textAnchor: "middle",
|
|
36772
|
+
fontSize: "12",
|
|
36773
|
+
fontWeight: "600",
|
|
36774
|
+
fill: "#374151",
|
|
36775
|
+
style: {
|
|
36776
|
+
opacity: 1,
|
|
36777
|
+
pointerEvents: "none",
|
|
36778
|
+
transition: "none"
|
|
36779
|
+
},
|
|
36780
|
+
children: Math.round(actualValue)
|
|
36781
|
+
}
|
|
36782
|
+
);
|
|
36783
|
+
}
|
|
35886
36784
|
}
|
|
35887
|
-
|
|
35888
|
-
|
|
35889
|
-
|
|
35890
|
-
|
|
35891
|
-
|
|
35892
|
-
|
|
35893
|
-
|
|
35894
|
-
|
|
35895
|
-
|
|
35896
|
-
|
|
35897
|
-
|
|
35898
|
-
|
|
35899
|
-
|
|
35900
|
-
|
|
35901
|
-
|
|
35902
|
-
|
|
35903
|
-
|
|
35904
|
-
|
|
35905
|
-
|
|
35906
|
-
|
|
35907
|
-
|
|
35908
|
-
style: {
|
|
35909
|
-
opacity: 1,
|
|
35910
|
-
pointerEvents: "none",
|
|
35911
|
-
transition: "none"
|
|
35912
|
-
},
|
|
35913
|
-
children: Math.round(actualValue)
|
|
35914
|
-
}
|
|
35915
|
-
);
|
|
36785
|
+
)
|
|
36786
|
+
]
|
|
36787
|
+
}
|
|
36788
|
+
),
|
|
36789
|
+
IdleBar,
|
|
36790
|
+
/* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs(
|
|
36791
|
+
"pattern",
|
|
36792
|
+
{
|
|
36793
|
+
id: "idlePattern",
|
|
36794
|
+
patternUnits: "userSpaceOnUse",
|
|
36795
|
+
width: "4",
|
|
36796
|
+
height: "4",
|
|
36797
|
+
children: [
|
|
36798
|
+
/* @__PURE__ */ jsx("rect", { width: "4", height: "4", fill: "#4b5563", opacity: "0.6" }),
|
|
36799
|
+
/* @__PURE__ */ jsx(
|
|
36800
|
+
"path",
|
|
36801
|
+
{
|
|
36802
|
+
d: "M 0,4 l 4,-4 M -1,1 l 2,-2 M 3,5 l 2,-2",
|
|
36803
|
+
stroke: "#374151",
|
|
36804
|
+
strokeWidth: "0.8",
|
|
36805
|
+
opacity: "0.8"
|
|
35916
36806
|
}
|
|
35917
|
-
|
|
35918
|
-
|
|
35919
|
-
|
|
35920
|
-
}
|
|
35921
|
-
|
|
35922
|
-
|
|
35923
|
-
|
|
35924
|
-
|
|
35925
|
-
|
|
35926
|
-
|
|
35927
|
-
|
|
35928
|
-
|
|
35929
|
-
|
|
35930
|
-
|
|
35931
|
-
|
|
35932
|
-
|
|
35933
|
-
|
|
35934
|
-
|
|
35935
|
-
|
|
35936
|
-
|
|
35937
|
-
|
|
35938
|
-
|
|
35939
|
-
}
|
|
35940
|
-
)
|
|
35941
|
-
]
|
|
35942
|
-
}
|
|
35943
|
-
) })
|
|
35944
|
-
]
|
|
35945
|
-
}
|
|
35946
|
-
) }) }) : /* @__PURE__ */ jsx("div", { className: "w-full h-full flex items-center justify-center bg-gray-50 rounded-lg", children: /* @__PURE__ */ jsx("div", { className: "text-gray-500 text-sm", children: "Loading chart..." }) }),
|
|
36807
|
+
)
|
|
36808
|
+
]
|
|
36809
|
+
}
|
|
36810
|
+
) })
|
|
36811
|
+
]
|
|
36812
|
+
}
|
|
36813
|
+
) }),
|
|
36814
|
+
hoveredSkuRailLabel && /* @__PURE__ */ jsx(
|
|
36815
|
+
"div",
|
|
36816
|
+
{
|
|
36817
|
+
className: "absolute z-50 pointer-events-none transform -translate-x-1/2 -translate-y-full transition-opacity duration-200",
|
|
36818
|
+
style: {
|
|
36819
|
+
left: hoveredSkuRailLabel.centerX,
|
|
36820
|
+
top: hoveredSkuRailLabel.railY - 12
|
|
36821
|
+
},
|
|
36822
|
+
children: /* @__PURE__ */ 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: [
|
|
36823
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] font-bold tracking-wider text-blue-500 uppercase leading-none mb-1", children: "SKU" }),
|
|
36824
|
+
/* @__PURE__ */ jsx("span", { className: "text-[13px] font-semibold text-slate-800 whitespace-normal break-words leading-snug", children: hoveredSkuRailLabel.label })
|
|
36825
|
+
] })
|
|
36826
|
+
}
|
|
36827
|
+
)
|
|
36828
|
+
] }) : /* @__PURE__ */ jsx("div", { className: "w-full h-full flex items-center justify-center bg-gray-50 rounded-lg", children: /* @__PURE__ */ jsx("div", { className: "text-gray-500 text-sm", children: "Loading chart..." }) }),
|
|
35947
36829
|
/* @__PURE__ */ jsx("div", { className: "flex-none pt-2", children: renderLegend() })
|
|
35948
36830
|
]
|
|
35949
36831
|
}
|
|
@@ -35961,6 +36843,38 @@ var HourlyOutputChart = React144__default.memo(
|
|
|
35961
36843
|
if (!prevProps.data.every((val, idx) => val === nextProps.data[idx])) {
|
|
35962
36844
|
return false;
|
|
35963
36845
|
}
|
|
36846
|
+
const prevHasHourlyTargetOutputProp = prevProps.hourlyTargetOutput !== void 0;
|
|
36847
|
+
const nextHasHourlyTargetOutputProp = nextProps.hourlyTargetOutput !== void 0;
|
|
36848
|
+
if (prevHasHourlyTargetOutputProp !== nextHasHourlyTargetOutputProp) {
|
|
36849
|
+
return false;
|
|
36850
|
+
}
|
|
36851
|
+
if (prevProps.hourlyTargetOutput === null || nextProps.hourlyTargetOutput === null) {
|
|
36852
|
+
if (prevProps.hourlyTargetOutput !== nextProps.hourlyTargetOutput) {
|
|
36853
|
+
return false;
|
|
36854
|
+
}
|
|
36855
|
+
}
|
|
36856
|
+
const prevHourlyTargets = prevProps.hourlyTargetOutput || [];
|
|
36857
|
+
const nextHourlyTargets = nextProps.hourlyTargetOutput || [];
|
|
36858
|
+
if (prevHourlyTargets.length !== nextHourlyTargets.length) {
|
|
36859
|
+
return false;
|
|
36860
|
+
}
|
|
36861
|
+
for (let i = 0; i < prevHourlyTargets.length; i += 1) {
|
|
36862
|
+
if (prevHourlyTargets[i] !== nextHourlyTargets[i]) {
|
|
36863
|
+
return false;
|
|
36864
|
+
}
|
|
36865
|
+
}
|
|
36866
|
+
const prevShiftBreaks = prevProps.shiftBreaks || [];
|
|
36867
|
+
const nextShiftBreaks = nextProps.shiftBreaks || [];
|
|
36868
|
+
if (prevShiftBreaks.length !== nextShiftBreaks.length) {
|
|
36869
|
+
return false;
|
|
36870
|
+
}
|
|
36871
|
+
for (let i = 0; i < prevShiftBreaks.length; i += 1) {
|
|
36872
|
+
const prevBreak = prevShiftBreaks[i] || {};
|
|
36873
|
+
const nextBreak = nextShiftBreaks[i] || {};
|
|
36874
|
+
if (prevBreak.startTime !== nextBreak.startTime || prevBreak.endTime !== nextBreak.endTime || prevBreak.duration !== nextBreak.duration || prevBreak.remarks !== nextBreak.remarks) {
|
|
36875
|
+
return false;
|
|
36876
|
+
}
|
|
36877
|
+
}
|
|
35964
36878
|
const prevIdle = prevProps.idleTimeHourly || {};
|
|
35965
36879
|
const nextIdle = nextProps.idleTimeHourly || {};
|
|
35966
36880
|
const prevKeys = Object.keys(prevIdle);
|
|
@@ -36148,7 +37062,6 @@ var VideoCard = React144__default.memo(({
|
|
|
36148
37062
|
shouldPlay,
|
|
36149
37063
|
onClick,
|
|
36150
37064
|
onFatalError,
|
|
36151
|
-
isVeryLowEfficiency = false,
|
|
36152
37065
|
legend,
|
|
36153
37066
|
cropping,
|
|
36154
37067
|
canvasFps = 30,
|
|
@@ -36220,11 +37133,6 @@ var VideoCard = React144__default.memo(({
|
|
|
36220
37133
|
}
|
|
36221
37134
|
},
|
|
36222
37135
|
children: [
|
|
36223
|
-
isVeryLowEfficiency && /* @__PURE__ */ jsx("div", { className: `absolute ${compact ? "top-0.5 left-1" : "top-1 left-2"} z-30`, children: /* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
36224
|
-
/* @__PURE__ */ jsx("div", { className: "absolute -inset-1 bg-red-400/50 rounded-full blur-sm animate-pulse" }),
|
|
36225
|
-
/* @__PURE__ */ jsx("div", { className: "absolute -inset-0.5 bg-red-500/30 rounded-full blur-md animate-ping [animation-duration:1.5s]" }),
|
|
36226
|
-
/* @__PURE__ */ 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: "!" })
|
|
36227
|
-
] }) }),
|
|
36228
37136
|
/* @__PURE__ */ jsxs("div", { className: "relative w-full h-full overflow-hidden bg-black", children: [
|
|
36229
37137
|
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-black z-0", children: /* @__PURE__ */ jsxs("div", { className: "animate-pulse flex flex-col items-center", children: [
|
|
36230
37138
|
/* @__PURE__ */ jsx(Camera, { className: `w-5 h-5 sm:${compact ? "w-4 h-4" : "w-6 h-6"} text-gray-500` }),
|
|
@@ -36634,7 +37542,6 @@ var VideoGridView = React144__default.memo(({
|
|
|
36634
37542
|
const workspaceId = workspace.workspace_uuid || workspace.workspace_name;
|
|
36635
37543
|
const workspaceKey = `${workspace.line_id || "unknown"}-${workspaceId}`;
|
|
36636
37544
|
const isVisible = visibleWorkspaces.has(workspaceId);
|
|
36637
|
-
const isVeryLowEfficiency = workspace.show_exclamation ?? (workspace.efficiency < 50 && workspace.efficiency >= 10);
|
|
36638
37545
|
const workspaceCropping = getWorkspaceCropping(workspaceId, workspace.workspace_name);
|
|
36639
37546
|
const workspaceStream = videoStreamsByWorkspaceId?.[workspaceId];
|
|
36640
37547
|
const lastSeenLabel = workspace.workspace_uuid ? lastSeenByWorkspaceId[workspace.workspace_uuid]?.timeSinceLastUpdate : void 0;
|
|
@@ -36651,7 +37558,6 @@ var VideoGridView = React144__default.memo(({
|
|
|
36651
37558
|
workspaceId,
|
|
36652
37559
|
workspaceKey,
|
|
36653
37560
|
isVisible,
|
|
36654
|
-
isVeryLowEfficiency,
|
|
36655
37561
|
workspaceCropping,
|
|
36656
37562
|
fallbackUrl,
|
|
36657
37563
|
hlsUrl,
|
|
@@ -36699,7 +37605,6 @@ var VideoGridView = React144__default.memo(({
|
|
|
36699
37605
|
isR2Stream: card.isR2Stream,
|
|
36700
37606
|
fallbackUrl: card.fallbackUrl
|
|
36701
37607
|
}),
|
|
36702
|
-
isVeryLowEfficiency: card.isVeryLowEfficiency,
|
|
36703
37608
|
legend: effectiveLegend,
|
|
36704
37609
|
cropping: card.workspaceCropping,
|
|
36705
37610
|
canvasFps: effectiveCanvasFps,
|
|
@@ -37619,223 +38524,6 @@ var UptimeLineChartComponent = ({ points, className = "" }) => {
|
|
|
37619
38524
|
] }) }) });
|
|
37620
38525
|
};
|
|
37621
38526
|
var UptimeLineChart = React144__default.memo(UptimeLineChartComponent);
|
|
37622
|
-
var padTime = (value) => value.toString().padStart(2, "0");
|
|
37623
|
-
var parseTime = (timeValue) => {
|
|
37624
|
-
if (!timeValue) return null;
|
|
37625
|
-
const [hourPart, minutePart] = timeValue.split(":");
|
|
37626
|
-
const hour = Number.parseInt(hourPart, 10);
|
|
37627
|
-
const minute = Number.parseInt(minutePart ?? "0", 10);
|
|
37628
|
-
if (!Number.isFinite(hour) || !Number.isFinite(minute)) return null;
|
|
37629
|
-
return { hour, minute };
|
|
37630
|
-
};
|
|
37631
|
-
var normalizeIdleTimeHourly = (idleTimeHourly) => {
|
|
37632
|
-
if (!idleTimeHourly || typeof idleTimeHourly !== "object") {
|
|
37633
|
-
return {};
|
|
37634
|
-
}
|
|
37635
|
-
return Object.fromEntries(
|
|
37636
|
-
Object.entries(idleTimeHourly).map(([key, value]) => {
|
|
37637
|
-
if (Array.isArray(value)) return [key, value];
|
|
37638
|
-
if (value && Array.isArray(value.values)) {
|
|
37639
|
-
return [key, value.values];
|
|
37640
|
-
}
|
|
37641
|
-
return [key, []];
|
|
37642
|
-
})
|
|
37643
|
-
);
|
|
37644
|
-
};
|
|
37645
|
-
var interpretIdleValue = (value) => {
|
|
37646
|
-
if (value === 1 || value === "1") return "idle";
|
|
37647
|
-
if (value === 0 || value === "0") return "active";
|
|
37648
|
-
if (value === "x" || value === null || value === void 0) return "unknown";
|
|
37649
|
-
return "unknown";
|
|
37650
|
-
};
|
|
37651
|
-
var getShiftDurationMinutes = (shiftStart, shiftEnd) => {
|
|
37652
|
-
const start = parseTime(shiftStart);
|
|
37653
|
-
const end = parseTime(shiftEnd);
|
|
37654
|
-
if (!start || !end) return null;
|
|
37655
|
-
let duration = end.hour * 60 + end.minute - (start.hour * 60 + start.minute);
|
|
37656
|
-
if (duration <= 0) {
|
|
37657
|
-
duration += 24 * 60;
|
|
37658
|
-
}
|
|
37659
|
-
return duration > 0 ? duration : null;
|
|
37660
|
-
};
|
|
37661
|
-
var getShiftElapsedMinutes = ({
|
|
37662
|
-
shiftStart,
|
|
37663
|
-
shiftEnd,
|
|
37664
|
-
shiftDate,
|
|
37665
|
-
timezone,
|
|
37666
|
-
now: now4 = /* @__PURE__ */ new Date()
|
|
37667
|
-
}) => {
|
|
37668
|
-
if (!shiftDate || !timezone) return null;
|
|
37669
|
-
const startTime = parseTime(shiftStart);
|
|
37670
|
-
const endTime = parseTime(shiftEnd);
|
|
37671
|
-
if (!startTime || !endTime) return null;
|
|
37672
|
-
const shiftStartDate = fromZonedTime(
|
|
37673
|
-
`${shiftDate}T${padTime(startTime.hour)}:${padTime(startTime.minute)}:00`,
|
|
37674
|
-
timezone
|
|
37675
|
-
);
|
|
37676
|
-
let shiftEndDate = fromZonedTime(
|
|
37677
|
-
`${shiftDate}T${padTime(endTime.hour)}:${padTime(endTime.minute)}:00`,
|
|
37678
|
-
timezone
|
|
37679
|
-
);
|
|
37680
|
-
if (shiftEndDate <= shiftStartDate) {
|
|
37681
|
-
shiftEndDate = addDays(shiftEndDate, 1);
|
|
37682
|
-
}
|
|
37683
|
-
const shiftMinutes = Math.max(differenceInMinutes(shiftEndDate, shiftStartDate), 0);
|
|
37684
|
-
if (shiftMinutes <= 0) return null;
|
|
37685
|
-
const elapsed = differenceInMinutes(now4, shiftStartDate);
|
|
37686
|
-
return Math.min(Math.max(elapsed, 0), shiftMinutes);
|
|
37687
|
-
};
|
|
37688
|
-
var maskFutureHourlySeries = ({
|
|
37689
|
-
data,
|
|
37690
|
-
shiftStart,
|
|
37691
|
-
shiftEnd,
|
|
37692
|
-
shiftDate,
|
|
37693
|
-
timezone,
|
|
37694
|
-
now: now4 = /* @__PURE__ */ new Date()
|
|
37695
|
-
}) => {
|
|
37696
|
-
if (!Array.isArray(data)) {
|
|
37697
|
-
return [];
|
|
37698
|
-
}
|
|
37699
|
-
const normalizedData = data.map((value) => typeof value === "number" && Number.isFinite(value) ? value : null);
|
|
37700
|
-
if (!normalizedData.length) {
|
|
37701
|
-
return normalizedData;
|
|
37702
|
-
}
|
|
37703
|
-
const shiftMinutes = getShiftDurationMinutes(shiftStart, shiftEnd);
|
|
37704
|
-
const elapsedMinutes = getShiftElapsedMinutes({
|
|
37705
|
-
shiftStart,
|
|
37706
|
-
shiftEnd,
|
|
37707
|
-
shiftDate,
|
|
37708
|
-
timezone,
|
|
37709
|
-
now: now4
|
|
37710
|
-
});
|
|
37711
|
-
if (shiftMinutes === null || elapsedMinutes === null || elapsedMinutes >= shiftMinutes) {
|
|
37712
|
-
return normalizedData;
|
|
37713
|
-
}
|
|
37714
|
-
return normalizedData.map((value, index) => {
|
|
37715
|
-
const slotStartMinutes = index * 60;
|
|
37716
|
-
return slotStartMinutes > elapsedMinutes ? null : value;
|
|
37717
|
-
});
|
|
37718
|
-
};
|
|
37719
|
-
var buildUptimeSeries = ({
|
|
37720
|
-
idleTimeHourly,
|
|
37721
|
-
shiftStart,
|
|
37722
|
-
shiftEnd,
|
|
37723
|
-
shiftDate,
|
|
37724
|
-
timezone,
|
|
37725
|
-
elapsedMinutes
|
|
37726
|
-
}) => {
|
|
37727
|
-
const normalizedIdle = normalizeIdleTimeHourly(idleTimeHourly || {});
|
|
37728
|
-
const hasIdleData = Object.keys(normalizedIdle).length > 0;
|
|
37729
|
-
if (!hasIdleData || !shiftDate || !timezone) {
|
|
37730
|
-
return {
|
|
37731
|
-
points: [],
|
|
37732
|
-
activeMinutes: 0,
|
|
37733
|
-
idleMinutes: 0,
|
|
37734
|
-
availableMinutes: 0,
|
|
37735
|
-
shiftMinutes: 0,
|
|
37736
|
-
elapsedMinutes: 0,
|
|
37737
|
-
hasData: false
|
|
37738
|
-
};
|
|
37739
|
-
}
|
|
37740
|
-
const startTime = parseTime(shiftStart);
|
|
37741
|
-
const endTime = parseTime(shiftEnd);
|
|
37742
|
-
if (!startTime || !endTime) {
|
|
37743
|
-
return {
|
|
37744
|
-
points: [],
|
|
37745
|
-
activeMinutes: 0,
|
|
37746
|
-
idleMinutes: 0,
|
|
37747
|
-
availableMinutes: 0,
|
|
37748
|
-
shiftMinutes: 0,
|
|
37749
|
-
elapsedMinutes: 0,
|
|
37750
|
-
hasData: false
|
|
37751
|
-
};
|
|
37752
|
-
}
|
|
37753
|
-
const shiftStartDate = fromZonedTime(
|
|
37754
|
-
`${shiftDate}T${padTime(startTime.hour)}:${padTime(startTime.minute)}:00`,
|
|
37755
|
-
timezone
|
|
37756
|
-
);
|
|
37757
|
-
let shiftEndDate = fromZonedTime(
|
|
37758
|
-
`${shiftDate}T${padTime(endTime.hour)}:${padTime(endTime.minute)}:00`,
|
|
37759
|
-
timezone
|
|
37760
|
-
);
|
|
37761
|
-
if (shiftEndDate <= shiftStartDate) {
|
|
37762
|
-
shiftEndDate = addDays(shiftEndDate, 1);
|
|
37763
|
-
}
|
|
37764
|
-
const shiftMinutes = Math.max(differenceInMinutes(shiftEndDate, shiftStartDate), 0);
|
|
37765
|
-
if (shiftMinutes <= 0) {
|
|
37766
|
-
return {
|
|
37767
|
-
points: [],
|
|
37768
|
-
activeMinutes: 0,
|
|
37769
|
-
idleMinutes: 0,
|
|
37770
|
-
availableMinutes: 0,
|
|
37771
|
-
shiftMinutes: 0,
|
|
37772
|
-
elapsedMinutes: 0,
|
|
37773
|
-
hasData: false
|
|
37774
|
-
};
|
|
37775
|
-
}
|
|
37776
|
-
const elapsedMinutesClamped = Number.isFinite(elapsedMinutes) ? Math.min(Math.max(Math.floor(elapsedMinutes ?? 0), 0), shiftMinutes) : shiftMinutes;
|
|
37777
|
-
const points = [];
|
|
37778
|
-
let activeMinutes = 0;
|
|
37779
|
-
let idleMinutes = 0;
|
|
37780
|
-
for (let minuteIndex = 0; minuteIndex < shiftMinutes; minuteIndex += 1) {
|
|
37781
|
-
const minuteDate = addMinutes(shiftStartDate, minuteIndex);
|
|
37782
|
-
const timeLabel = formatInTimeZone(minuteDate, timezone, "h:mm a");
|
|
37783
|
-
if (minuteIndex >= elapsedMinutesClamped) {
|
|
37784
|
-
points.push({
|
|
37785
|
-
minuteIndex,
|
|
37786
|
-
timeLabel,
|
|
37787
|
-
uptime: null,
|
|
37788
|
-
status: "unknown"
|
|
37789
|
-
});
|
|
37790
|
-
continue;
|
|
37791
|
-
}
|
|
37792
|
-
const hourKey = formatInTimeZone(minuteDate, timezone, "H");
|
|
37793
|
-
const minuteKey = Number.parseInt(formatInTimeZone(minuteDate, timezone, "m"), 10);
|
|
37794
|
-
const hourBucket = normalizedIdle[hourKey] || [];
|
|
37795
|
-
const value = Array.isArray(hourBucket) ? hourBucket[minuteKey] : void 0;
|
|
37796
|
-
const status = interpretIdleValue(value);
|
|
37797
|
-
if (status === "active") activeMinutes += 1;
|
|
37798
|
-
if (status === "idle") idleMinutes += 1;
|
|
37799
|
-
points.push({
|
|
37800
|
-
minuteIndex,
|
|
37801
|
-
timeLabel,
|
|
37802
|
-
uptime: status === "active" ? 1 : status === "idle" ? 0 : null,
|
|
37803
|
-
status
|
|
37804
|
-
});
|
|
37805
|
-
}
|
|
37806
|
-
return {
|
|
37807
|
-
points,
|
|
37808
|
-
activeMinutes,
|
|
37809
|
-
idleMinutes,
|
|
37810
|
-
availableMinutes: activeMinutes + idleMinutes,
|
|
37811
|
-
shiftMinutes,
|
|
37812
|
-
elapsedMinutes: elapsedMinutesClamped,
|
|
37813
|
-
hasData: activeMinutes + idleMinutes > 0
|
|
37814
|
-
};
|
|
37815
|
-
};
|
|
37816
|
-
var getUptimeUtilizationPercent = (shift) => {
|
|
37817
|
-
const efficiency = shift.efficiency;
|
|
37818
|
-
if (Number.isFinite(efficiency)) {
|
|
37819
|
-
return Math.round(Math.max(0, Math.min(100, Number(efficiency))));
|
|
37820
|
-
}
|
|
37821
|
-
const idleSeconds = Number.isFinite(shift.idleTime) ? Number(shift.idleTime) : 0;
|
|
37822
|
-
const activeSeconds = Number.isFinite(shift.activeTimeSeconds) ? Number(shift.activeTimeSeconds) : null;
|
|
37823
|
-
let availableSeconds = Number.isFinite(shift.availableTimeSeconds) ? Number(shift.availableTimeSeconds) : null;
|
|
37824
|
-
if (availableSeconds === null) {
|
|
37825
|
-
if ((activeSeconds ?? 0) > 0 || idleSeconds > 0) {
|
|
37826
|
-
availableSeconds = (activeSeconds ?? 0) + idleSeconds;
|
|
37827
|
-
} else {
|
|
37828
|
-
return 0;
|
|
37829
|
-
}
|
|
37830
|
-
}
|
|
37831
|
-
if (availableSeconds <= 0) return 0;
|
|
37832
|
-
const clampedIdleSeconds = Math.min(Math.max(idleSeconds, 0), availableSeconds);
|
|
37833
|
-
const productiveSeconds = Math.max(
|
|
37834
|
-
activeSeconds ?? availableSeconds - clampedIdleSeconds,
|
|
37835
|
-
0
|
|
37836
|
-
);
|
|
37837
|
-
return Math.round(productiveSeconds / availableSeconds * 100);
|
|
37838
|
-
};
|
|
37839
38527
|
var getTimeFromTimeString = (timeStr) => {
|
|
37840
38528
|
if (!timeStr) {
|
|
37841
38529
|
return { hour: 0, minute: 0, decimalHour: 0 };
|
|
@@ -49901,261 +50589,6 @@ Underperforming Workspaces: ${lineInfo.metrics.underperforming_workspaces} / ${l
|
|
|
49901
50589
|
}
|
|
49902
50590
|
);
|
|
49903
50591
|
};
|
|
49904
|
-
|
|
49905
|
-
// src/lib/utils/hourlyTargets.ts
|
|
49906
|
-
var stripSeconds2 = (timeStr) => timeStr ? timeStr.slice(0, 5) : timeStr;
|
|
49907
|
-
var MINUTES_PER_DAY = 24 * 60;
|
|
49908
|
-
var parseTimeToMinutes2 = (timeString) => {
|
|
49909
|
-
const normalized = stripSeconds2(timeString || "");
|
|
49910
|
-
if (!normalized || !/^[0-2]\d:[0-5]\d$/.test(normalized)) return Number.NaN;
|
|
49911
|
-
const [hours, minutes] = normalized.split(":").map(Number);
|
|
49912
|
-
return hours * 60 + minutes;
|
|
49913
|
-
};
|
|
49914
|
-
var normalizeBreaksOnShiftTimeline = (shiftStart, breaks) => {
|
|
49915
|
-
const shiftStartMinutes = parseTimeToMinutes2(shiftStart);
|
|
49916
|
-
if (!Number.isFinite(shiftStartMinutes)) return [];
|
|
49917
|
-
const normalizedBreaks = [];
|
|
49918
|
-
for (const entry of breaks) {
|
|
49919
|
-
const startRaw = parseTimeToMinutes2(entry.startTime);
|
|
49920
|
-
const endRaw = parseTimeToMinutes2(entry.endTime);
|
|
49921
|
-
if (!Number.isFinite(startRaw) || !Number.isFinite(endRaw)) continue;
|
|
49922
|
-
let start = startRaw;
|
|
49923
|
-
let end = endRaw;
|
|
49924
|
-
if (end <= start) {
|
|
49925
|
-
end += 24 * 60;
|
|
49926
|
-
}
|
|
49927
|
-
if (start < shiftStartMinutes) {
|
|
49928
|
-
start += 24 * 60;
|
|
49929
|
-
end += 24 * 60;
|
|
49930
|
-
}
|
|
49931
|
-
const label = entry.remarks?.trim() || "Break";
|
|
49932
|
-
normalizedBreaks.push({ start, end, label });
|
|
49933
|
-
}
|
|
49934
|
-
return normalizedBreaks;
|
|
49935
|
-
};
|
|
49936
|
-
var roundTarget = (value, mode) => {
|
|
49937
|
-
if (!Number.isFinite(value)) return 0;
|
|
49938
|
-
switch (mode) {
|
|
49939
|
-
case "floor":
|
|
49940
|
-
return Math.floor(value);
|
|
49941
|
-
case "ceil":
|
|
49942
|
-
return Math.ceil(value);
|
|
49943
|
-
case "round":
|
|
49944
|
-
default:
|
|
49945
|
-
return Math.round(value);
|
|
49946
|
-
}
|
|
49947
|
-
};
|
|
49948
|
-
var formatDateKey = (date) => {
|
|
49949
|
-
const year = date.getUTCFullYear();
|
|
49950
|
-
const month = `${date.getUTCMonth() + 1}`.padStart(2, "0");
|
|
49951
|
-
const day = `${date.getUTCDate()}`.padStart(2, "0");
|
|
49952
|
-
return `${year}-${month}-${day}`;
|
|
49953
|
-
};
|
|
49954
|
-
var shiftDateKey = (dateKey, deltaDays) => {
|
|
49955
|
-
const [yearPart, monthPart, dayPart] = dateKey.split("-").map(Number);
|
|
49956
|
-
const year = Number.isFinite(yearPart) ? yearPart : 1970;
|
|
49957
|
-
const month = Number.isFinite(monthPart) ? monthPart : 1;
|
|
49958
|
-
const day = Number.isFinite(dayPart) ? dayPart : 1;
|
|
49959
|
-
const date = new Date(Date.UTC(year, month - 1, day));
|
|
49960
|
-
date.setUTCDate(date.getUTCDate() + deltaDays);
|
|
49961
|
-
return formatDateKey(date);
|
|
49962
|
-
};
|
|
49963
|
-
var getZonedNowSnapshot = (timeZone, now4) => {
|
|
49964
|
-
const formatter = new Intl.DateTimeFormat("en-US", {
|
|
49965
|
-
timeZone,
|
|
49966
|
-
year: "numeric",
|
|
49967
|
-
month: "2-digit",
|
|
49968
|
-
day: "2-digit",
|
|
49969
|
-
hour: "2-digit",
|
|
49970
|
-
minute: "2-digit",
|
|
49971
|
-
hourCycle: "h23"
|
|
49972
|
-
});
|
|
49973
|
-
const parts = formatter.formatToParts(now4).reduce((acc, part) => {
|
|
49974
|
-
if (part.type !== "literal") {
|
|
49975
|
-
acc[part.type] = part.value;
|
|
49976
|
-
}
|
|
49977
|
-
return acc;
|
|
49978
|
-
}, {});
|
|
49979
|
-
const year = Number(parts.year);
|
|
49980
|
-
const month = Number(parts.month);
|
|
49981
|
-
const day = Number(parts.day);
|
|
49982
|
-
const hour = Number(parts.hour);
|
|
49983
|
-
const minute = Number(parts.minute);
|
|
49984
|
-
return {
|
|
49985
|
-
dateKey: `${String(year).padStart(4, "0")}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`,
|
|
49986
|
-
minutesOfDay: (Number.isFinite(hour) ? hour : 0) * 60 + (Number.isFinite(minute) ? minute : 0)
|
|
49987
|
-
};
|
|
49988
|
-
};
|
|
49989
|
-
var getDateKeyInTimeZone = (timeZone, now4 = /* @__PURE__ */ new Date()) => getZonedNowSnapshot(timeZone, now4).dateKey;
|
|
49990
|
-
var buildHourlyIntervals = ({
|
|
49991
|
-
shiftStart,
|
|
49992
|
-
shiftEnd,
|
|
49993
|
-
bucketMinutes = 60,
|
|
49994
|
-
fallbackHours = 11
|
|
49995
|
-
}) => {
|
|
49996
|
-
const startMinutes = parseTimeToMinutes2(shiftStart);
|
|
49997
|
-
if (!Number.isFinite(startMinutes)) return [];
|
|
49998
|
-
const bucket = Number.isFinite(bucketMinutes) && bucketMinutes > 0 ? Math.floor(bucketMinutes) : 60;
|
|
49999
|
-
let totalMinutes;
|
|
50000
|
-
const endRaw = shiftEnd ? parseTimeToMinutes2(shiftEnd) : Number.NaN;
|
|
50001
|
-
if (!Number.isFinite(endRaw)) {
|
|
50002
|
-
totalMinutes = Math.max(0, Math.round(fallbackHours * 60));
|
|
50003
|
-
} else {
|
|
50004
|
-
let endMinutes = endRaw;
|
|
50005
|
-
if (endMinutes <= startMinutes) {
|
|
50006
|
-
endMinutes += 24 * 60;
|
|
50007
|
-
}
|
|
50008
|
-
totalMinutes = endMinutes - startMinutes;
|
|
50009
|
-
}
|
|
50010
|
-
if (!Number.isFinite(totalMinutes) || totalMinutes <= 0) return [];
|
|
50011
|
-
const count = Math.ceil(totalMinutes / bucket);
|
|
50012
|
-
const shiftEndMinutes = startMinutes + totalMinutes;
|
|
50013
|
-
const intervals = [];
|
|
50014
|
-
for (let i = 0; i < count; i += 1) {
|
|
50015
|
-
const start = startMinutes + i * bucket;
|
|
50016
|
-
const end = Math.min(start + bucket, shiftEndMinutes);
|
|
50017
|
-
const minutes = Math.max(0, end - start);
|
|
50018
|
-
if (minutes <= 0) continue;
|
|
50019
|
-
intervals.push({ start, end, minutes });
|
|
50020
|
-
}
|
|
50021
|
-
return intervals;
|
|
50022
|
-
};
|
|
50023
|
-
var computeBreakMinutesByInterval = ({
|
|
50024
|
-
intervals,
|
|
50025
|
-
shiftStart,
|
|
50026
|
-
breaks
|
|
50027
|
-
}) => {
|
|
50028
|
-
if (!intervals.length || !breaks.length) return intervals.map(() => 0);
|
|
50029
|
-
const normalizedBreaks = normalizeBreaksOnShiftTimeline(shiftStart, breaks);
|
|
50030
|
-
return intervals.map((interval) => {
|
|
50031
|
-
if (!normalizedBreaks.length) return 0;
|
|
50032
|
-
let total = 0;
|
|
50033
|
-
for (const brk of normalizedBreaks) {
|
|
50034
|
-
const overlap = Math.max(0, Math.min(interval.end, brk.end) - Math.max(interval.start, brk.start));
|
|
50035
|
-
total += overlap;
|
|
50036
|
-
if (total >= interval.minutes) return interval.minutes;
|
|
50037
|
-
}
|
|
50038
|
-
return Math.min(interval.minutes, total);
|
|
50039
|
-
});
|
|
50040
|
-
};
|
|
50041
|
-
var computeBreakRemarksByInterval = ({
|
|
50042
|
-
intervals,
|
|
50043
|
-
shiftStart,
|
|
50044
|
-
breaks
|
|
50045
|
-
}) => {
|
|
50046
|
-
if (!intervals.length || !breaks.length) return intervals.map(() => "");
|
|
50047
|
-
const normalizedBreaks = normalizeBreaksOnShiftTimeline(shiftStart, breaks);
|
|
50048
|
-
return intervals.map((interval) => {
|
|
50049
|
-
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);
|
|
50050
|
-
return labels.join(", ");
|
|
50051
|
-
});
|
|
50052
|
-
};
|
|
50053
|
-
var computeEffectiveTargets = ({
|
|
50054
|
-
intervals,
|
|
50055
|
-
breakMinutes,
|
|
50056
|
-
pphThreshold,
|
|
50057
|
-
rounding = "round"
|
|
50058
|
-
}) => {
|
|
50059
|
-
return intervals.map((interval, idx) => {
|
|
50060
|
-
const intervalMinutes = Number(interval?.minutes) || 0;
|
|
50061
|
-
const breakMins = Number(breakMinutes?.[idx]) || 0;
|
|
50062
|
-
const plannedWorkMinutes = Math.max(0, intervalMinutes - breakMins);
|
|
50063
|
-
if (!Number.isFinite(pphThreshold) || pphThreshold <= 0) return 0;
|
|
50064
|
-
if (plannedWorkMinutes <= 0) return 0;
|
|
50065
|
-
return roundTarget(pphThreshold * plannedWorkMinutes / 60, rounding);
|
|
50066
|
-
});
|
|
50067
|
-
};
|
|
50068
|
-
var buildHourlyTargetPlan = ({
|
|
50069
|
-
shiftStart,
|
|
50070
|
-
shiftEnd,
|
|
50071
|
-
breaks = [],
|
|
50072
|
-
pphThreshold,
|
|
50073
|
-
bucketMinutes = 60,
|
|
50074
|
-
fallbackHours = 11,
|
|
50075
|
-
rounding = "round"
|
|
50076
|
-
}) => {
|
|
50077
|
-
const intervals = buildHourlyIntervals({
|
|
50078
|
-
shiftStart,
|
|
50079
|
-
shiftEnd,
|
|
50080
|
-
bucketMinutes,
|
|
50081
|
-
fallbackHours
|
|
50082
|
-
});
|
|
50083
|
-
const breakMinutes = computeBreakMinutesByInterval({
|
|
50084
|
-
intervals,
|
|
50085
|
-
shiftStart,
|
|
50086
|
-
breaks
|
|
50087
|
-
});
|
|
50088
|
-
const breakRemarks = computeBreakRemarksByInterval({
|
|
50089
|
-
intervals,
|
|
50090
|
-
shiftStart,
|
|
50091
|
-
breaks
|
|
50092
|
-
});
|
|
50093
|
-
const productiveMinutes = intervals.map((interval, idx) => Math.max(0, (Number(interval?.minutes) || 0) - (Number(breakMinutes[idx]) || 0)));
|
|
50094
|
-
const targets = computeEffectiveTargets({
|
|
50095
|
-
intervals,
|
|
50096
|
-
breakMinutes,
|
|
50097
|
-
pphThreshold,
|
|
50098
|
-
rounding
|
|
50099
|
-
});
|
|
50100
|
-
return {
|
|
50101
|
-
intervals,
|
|
50102
|
-
breakMinutes,
|
|
50103
|
-
breakRemarks,
|
|
50104
|
-
productiveMinutes,
|
|
50105
|
-
targets
|
|
50106
|
-
};
|
|
50107
|
-
};
|
|
50108
|
-
var isHourlyIntervalComplete = ({
|
|
50109
|
-
reportDate,
|
|
50110
|
-
shiftStart,
|
|
50111
|
-
shiftEnd,
|
|
50112
|
-
interval,
|
|
50113
|
-
timeZone = "Asia/Kolkata",
|
|
50114
|
-
now: now4 = /* @__PURE__ */ new Date()
|
|
50115
|
-
}) => {
|
|
50116
|
-
if (!reportDate) return true;
|
|
50117
|
-
const snapshot = getZonedNowSnapshot(timeZone, now4);
|
|
50118
|
-
const shiftStartMinutes = parseTimeToMinutes2(shiftStart);
|
|
50119
|
-
const shiftEndMinutes = shiftEnd ? parseTimeToMinutes2(shiftEnd) : Number.NaN;
|
|
50120
|
-
const wrapsMidnight = Number.isFinite(shiftStartMinutes) && Number.isFinite(shiftEndMinutes) && shiftEndMinutes <= shiftStartMinutes;
|
|
50121
|
-
if (reportDate === snapshot.dateKey) {
|
|
50122
|
-
return interval.end <= snapshot.minutesOfDay;
|
|
50123
|
-
}
|
|
50124
|
-
if (wrapsMidnight && reportDate === shiftDateKey(snapshot.dateKey, -1)) {
|
|
50125
|
-
return interval.end <= snapshot.minutesOfDay + MINUTES_PER_DAY;
|
|
50126
|
-
}
|
|
50127
|
-
return reportDate < snapshot.dateKey;
|
|
50128
|
-
};
|
|
50129
|
-
var isShiftInProgressForReportDate = ({
|
|
50130
|
-
reportDate,
|
|
50131
|
-
shiftStart,
|
|
50132
|
-
shiftEnd,
|
|
50133
|
-
timeZone = "Asia/Kolkata",
|
|
50134
|
-
now: now4 = /* @__PURE__ */ new Date()
|
|
50135
|
-
}) => {
|
|
50136
|
-
if (!reportDate || !shiftStart || !shiftEnd) return false;
|
|
50137
|
-
const shiftStartMinutes = parseTimeToMinutes2(shiftStart);
|
|
50138
|
-
const shiftEndMinutesRaw = parseTimeToMinutes2(shiftEnd);
|
|
50139
|
-
if (!Number.isFinite(shiftStartMinutes) || !Number.isFinite(shiftEndMinutesRaw)) {
|
|
50140
|
-
return false;
|
|
50141
|
-
}
|
|
50142
|
-
let shiftEndMinutes = shiftEndMinutesRaw;
|
|
50143
|
-
const wrapsMidnight = shiftEndMinutes <= shiftStartMinutes;
|
|
50144
|
-
if (wrapsMidnight) {
|
|
50145
|
-
shiftEndMinutes += MINUTES_PER_DAY;
|
|
50146
|
-
}
|
|
50147
|
-
const snapshot = getZonedNowSnapshot(timeZone, now4);
|
|
50148
|
-
let currentMinutes = null;
|
|
50149
|
-
if (reportDate === snapshot.dateKey) {
|
|
50150
|
-
currentMinutes = snapshot.minutesOfDay;
|
|
50151
|
-
} else if (wrapsMidnight && reportDate === shiftDateKey(snapshot.dateKey, -1)) {
|
|
50152
|
-
currentMinutes = snapshot.minutesOfDay + MINUTES_PER_DAY;
|
|
50153
|
-
}
|
|
50154
|
-
if (currentMinutes === null) {
|
|
50155
|
-
return false;
|
|
50156
|
-
}
|
|
50157
|
-
return shiftStartMinutes <= currentMinutes && currentMinutes < shiftEndMinutes;
|
|
50158
|
-
};
|
|
50159
50592
|
var formatOperationalDateKey = (dateKey, options) => {
|
|
50160
50593
|
const [yearPart, monthPart, dayPart] = dateKey.split("-").map(Number);
|
|
50161
50594
|
const year = Number.isFinite(yearPart) ? yearPart : 1970;
|
|
@@ -50166,7 +50599,7 @@ var formatOperationalDateKey = (dateKey, options) => {
|
|
|
50166
50599
|
timeZone: "UTC"
|
|
50167
50600
|
});
|
|
50168
50601
|
};
|
|
50169
|
-
var
|
|
50602
|
+
var getSegmentMinutes2 = (isoString, timeZone, reportDate) => {
|
|
50170
50603
|
const date = new Date(isoString);
|
|
50171
50604
|
if (isNaN(date.getTime())) return NaN;
|
|
50172
50605
|
const formatter = new Intl.DateTimeFormat("en-US", {
|
|
@@ -50187,13 +50620,13 @@ var getSegmentMinutes = (isoString, timeZone, reportDate) => {
|
|
|
50187
50620
|
if (dateKey > reportDate) {
|
|
50188
50621
|
const d1 = new Date(dateKey);
|
|
50189
50622
|
const d2 = new Date(reportDate);
|
|
50190
|
-
const
|
|
50191
|
-
minutes +=
|
|
50623
|
+
const diffDays2 = Math.round((d1.getTime() - d2.getTime()) / (1e3 * 3600 * 24));
|
|
50624
|
+
minutes += diffDays2 * 24 * 60;
|
|
50192
50625
|
} else if (dateKey < reportDate) {
|
|
50193
50626
|
const d1 = new Date(dateKey);
|
|
50194
50627
|
const d2 = new Date(reportDate);
|
|
50195
|
-
const
|
|
50196
|
-
minutes -=
|
|
50628
|
+
const diffDays2 = Math.round((d2.getTime() - d1.getTime()) / (1e3 * 3600 * 24));
|
|
50629
|
+
minutes -= diffDays2 * 24 * 60;
|
|
50197
50630
|
}
|
|
50198
50631
|
return minutes;
|
|
50199
50632
|
};
|
|
@@ -50603,7 +51036,7 @@ var LinePdfGenerator = ({
|
|
|
50603
51036
|
const skuRemarksByIndex = {};
|
|
50604
51037
|
if (lineInfo.metrics.sku_segments && lineInfo.metrics.sku_segments.length > 0) {
|
|
50605
51038
|
lineInfo.metrics.sku_segments.forEach((segment, segmentIndex) => {
|
|
50606
|
-
const segmentMinutes =
|
|
51039
|
+
const segmentMinutes = getSegmentMinutes2(segment.start_time, reportTimezone, lineInfo.date);
|
|
50607
51040
|
if (!isNaN(segmentMinutes)) {
|
|
50608
51041
|
const intervalIndex = hourlyTimeRanges.findIndex(
|
|
50609
51042
|
(inv) => segmentMinutes >= inv.start && segmentMinutes < inv.end
|
|
@@ -52338,7 +52771,7 @@ var formatOperationalDateKey2 = (dateKey, options) => {
|
|
|
52338
52771
|
timeZone: "UTC"
|
|
52339
52772
|
});
|
|
52340
52773
|
};
|
|
52341
|
-
var
|
|
52774
|
+
var getSegmentMinutes3 = (isoString, timeZone, reportDate) => {
|
|
52342
52775
|
const date = new Date(isoString);
|
|
52343
52776
|
if (isNaN(date.getTime())) return NaN;
|
|
52344
52777
|
const formatter = new Intl.DateTimeFormat("en-US", {
|
|
@@ -52359,13 +52792,13 @@ var getSegmentMinutes2 = (isoString, timeZone, reportDate) => {
|
|
|
52359
52792
|
if (dateKey > reportDate) {
|
|
52360
52793
|
const d1 = new Date(dateKey);
|
|
52361
52794
|
const d2 = new Date(reportDate);
|
|
52362
|
-
const
|
|
52363
|
-
minutes +=
|
|
52795
|
+
const diffDays2 = Math.round((d1.getTime() - d2.getTime()) / (1e3 * 3600 * 24));
|
|
52796
|
+
minutes += diffDays2 * 24 * 60;
|
|
52364
52797
|
} else if (dateKey < reportDate) {
|
|
52365
52798
|
const d1 = new Date(dateKey);
|
|
52366
52799
|
const d2 = new Date(reportDate);
|
|
52367
|
-
const
|
|
52368
|
-
minutes -=
|
|
52800
|
+
const diffDays2 = Math.round((d2.getTime() - d1.getTime()) / (1e3 * 3600 * 24));
|
|
52801
|
+
minutes -= diffDays2 * 24 * 60;
|
|
52369
52802
|
}
|
|
52370
52803
|
return minutes;
|
|
52371
52804
|
};
|
|
@@ -52597,7 +53030,7 @@ var WorkspacePdfGenerator = ({
|
|
|
52597
53030
|
const skuRemarksByIndex = {};
|
|
52598
53031
|
if (workspace.sku_segments && workspace.sku_segments.length > 0) {
|
|
52599
53032
|
workspace.sku_segments.forEach((segment, segmentIndex) => {
|
|
52600
|
-
const segmentMinutes =
|
|
53033
|
+
const segmentMinutes = getSegmentMinutes3(segment.start_time, reportTimezone, workspace.date);
|
|
52601
53034
|
if (!isNaN(segmentMinutes)) {
|
|
52602
53035
|
const intervalIndex = hourlyIntervals.findIndex(
|
|
52603
53036
|
(inv) => segmentMinutes >= inv.start && segmentMinutes < inv.end
|
|
@@ -53439,12 +53872,10 @@ var formatPercentRange = (min, max) => {
|
|
|
53439
53872
|
return `${format10(min)}-${format10(max)}%`;
|
|
53440
53873
|
};
|
|
53441
53874
|
var Legend5 = ({
|
|
53442
|
-
useBottleneckLabel = false,
|
|
53443
53875
|
legend,
|
|
53444
53876
|
metricLabel = "Efficiency"
|
|
53445
53877
|
}) => {
|
|
53446
53878
|
const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;
|
|
53447
|
-
const exclamationLabel = useBottleneckLabel ? "Bottleneck" : "<50% efficiency";
|
|
53448
53879
|
return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2 sm:gap-4 text-xs font-medium text-slate-600", children: [
|
|
53449
53880
|
/* @__PURE__ */ jsxs("div", { className: "font-medium text-gray-700 hidden sm:block", children: [
|
|
53450
53881
|
metricLabel,
|
|
@@ -53463,11 +53894,6 @@ var Legend5 = ({
|
|
|
53463
53894
|
/* @__PURE__ */ jsx("div", { className: "w-2 h-2 sm:w-2.5 sm:h-2.5 rounded-full bg-[#E34329]" }),
|
|
53464
53895
|
/* @__PURE__ */ jsx("span", { children: formatPercentRange(effectiveLegend.red_min, effectiveLegend.red_max) })
|
|
53465
53896
|
] })
|
|
53466
|
-
] }),
|
|
53467
|
-
/* @__PURE__ */ jsx("div", { className: "hidden sm:block w-px h-4 bg-slate-200 mx-1" }),
|
|
53468
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
53469
|
-
/* @__PURE__ */ 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: "!" }),
|
|
53470
|
-
/* @__PURE__ */ jsx("span", { children: exclamationLabel })
|
|
53471
53897
|
] })
|
|
53472
53898
|
] });
|
|
53473
53899
|
};
|
|
@@ -53581,7 +54007,6 @@ var WorkspaceGrid = React144__default.memo(({
|
|
|
53581
54007
|
factoryView = "factory",
|
|
53582
54008
|
line2Uuid = "line-2",
|
|
53583
54009
|
className = "",
|
|
53584
|
-
hasFlowBuffers = false,
|
|
53585
54010
|
legend = DEFAULT_EFFICIENCY_LEGEND,
|
|
53586
54011
|
videoSources = {},
|
|
53587
54012
|
videoStreamsByWorkspaceId = {},
|
|
@@ -53625,7 +54050,7 @@ var WorkspaceGrid = React144__default.memo(({
|
|
|
53625
54050
|
);
|
|
53626
54051
|
return /* @__PURE__ */ jsxs("div", { className: `flex flex-col w-full h-full overflow-hidden bg-slate-50/50 ${className}`, children: [
|
|
53627
54052
|
/* @__PURE__ */ jsxs("div", { className: "flex-none px-4 py-3 z-20 flex flex-row items-center justify-between gap-4", children: [
|
|
53628
|
-
/* @__PURE__ */ jsx("div", { className: "hidden sm:block", children: /* @__PURE__ */ 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__ */ jsx(Legend5, { legend,
|
|
54053
|
+
/* @__PURE__ */ jsx("div", { className: "hidden sm:block", children: /* @__PURE__ */ 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__ */ jsx(Legend5, { legend, metricLabel: legendMetricLabel }) }) }),
|
|
53629
54054
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-3 shrink-0", children: [
|
|
53630
54055
|
toolbarRightContent,
|
|
53631
54056
|
mapViewEnabled && /* @__PURE__ */ jsx(
|
|
@@ -53645,7 +54070,7 @@ var WorkspaceGrid = React144__default.memo(({
|
|
|
53645
54070
|
)
|
|
53646
54071
|
] })
|
|
53647
54072
|
] }),
|
|
53648
|
-
/* @__PURE__ */ jsx("div", { className: "sm:hidden px-3 py-2 bg-white border-b border-slate-200/60 z-10", children: /* @__PURE__ */ jsx(Legend5, { legend,
|
|
54073
|
+
/* @__PURE__ */ jsx("div", { className: "sm:hidden px-3 py-2 bg-white border-b border-slate-200/60 z-10", children: /* @__PURE__ */ jsx(Legend5, { legend, metricLabel: legendMetricLabel }) }),
|
|
53649
54074
|
/* @__PURE__ */ jsx("div", { className: "flex-1 relative overflow-hidden", children: /* @__PURE__ */ jsx(AnimatePresence, { mode: "wait", children: viewMode === "video" ? /* @__PURE__ */ jsx(
|
|
53650
54075
|
motion.div,
|
|
53651
54076
|
{
|
|
@@ -64306,6 +64731,7 @@ var buildLineInfoSnapshot = (lineDetails, metrics2) => {
|
|
|
64306
64731
|
underperforming_workspace_uuids: metrics2.underperforming_workspace_uuids || [],
|
|
64307
64732
|
output_array: metrics2.output_array || [],
|
|
64308
64733
|
output_hourly: metrics2.output_hourly,
|
|
64734
|
+
hourly_target_output: metrics2.hourly_target_output,
|
|
64309
64735
|
line_threshold: metrics2.line_threshold ?? 0,
|
|
64310
64736
|
threshold_pph: metrics2.threshold_pph ?? 0,
|
|
64311
64737
|
shift_start: metrics2.shift_start || "06:00",
|
|
@@ -64392,6 +64818,7 @@ var transformLineMetrics = (lineId, detailResponse, queryDate, queryShiftId) =>
|
|
|
64392
64818
|
underperforming_workspace_names: [],
|
|
64393
64819
|
underperforming_workspace_uuids: [],
|
|
64394
64820
|
output_array: [],
|
|
64821
|
+
hourly_target_output: void 0,
|
|
64395
64822
|
line_threshold: 0,
|
|
64396
64823
|
threshold_pph: 0,
|
|
64397
64824
|
shift_start: "06:00",
|
|
@@ -65429,6 +65856,10 @@ var BottomSection = memo$1(({
|
|
|
65429
65856
|
workspaceDisplayNames,
|
|
65430
65857
|
hourlyOutputData,
|
|
65431
65858
|
hourlyThreshold,
|
|
65859
|
+
hourlyTargetOutput,
|
|
65860
|
+
shiftBreaks,
|
|
65861
|
+
idleTimeHourly,
|
|
65862
|
+
timezone,
|
|
65432
65863
|
urlDate,
|
|
65433
65864
|
urlShift,
|
|
65434
65865
|
navigate,
|
|
@@ -65599,8 +66030,13 @@ var BottomSection = memo$1(({
|
|
|
65599
66030
|
{
|
|
65600
66031
|
data: hourlyOutputData,
|
|
65601
66032
|
pphThreshold: hourlyThreshold,
|
|
66033
|
+
hourlyTargetOutput,
|
|
65602
66034
|
shiftStart: lineInfo.metrics.shift_start || "06:00",
|
|
65603
66035
|
shiftEnd: lineInfo.metrics.shift_end,
|
|
66036
|
+
shiftBreaks,
|
|
66037
|
+
idleTimeHourly,
|
|
66038
|
+
shiftDate: lineInfo.date,
|
|
66039
|
+
timezone,
|
|
65604
66040
|
skuSegments: skuAware ? skuSegments : void 0,
|
|
65605
66041
|
activeSkuId
|
|
65606
66042
|
}
|
|
@@ -65621,6 +66057,12 @@ var BottomSection = memo$1(({
|
|
|
65621
66057
|
if (prevProps.lineInfo.monitoring_mode !== nextProps.lineInfo.monitoring_mode) return false;
|
|
65622
66058
|
if (prevProps.skuAware !== nextProps.skuAware) return false;
|
|
65623
66059
|
if (prevProps.activeSkuId !== nextProps.activeSkuId) return false;
|
|
66060
|
+
if (JSON.stringify(prevProps.shiftBreaks || []) !== JSON.stringify(nextProps.shiftBreaks || [])) {
|
|
66061
|
+
return false;
|
|
66062
|
+
}
|
|
66063
|
+
if (JSON.stringify(prevProps.hourlyTargetOutput || []) !== JSON.stringify(nextProps.hourlyTargetOutput || [])) {
|
|
66064
|
+
return false;
|
|
66065
|
+
}
|
|
65624
66066
|
const prevSkuSegmentsSignature = JSON.stringify(
|
|
65625
66067
|
(prevProps.skuSegments || []).map((segment) => ({
|
|
65626
66068
|
sku_id: segment.sku_id,
|
|
@@ -65693,7 +66135,9 @@ var BottomSection = memo$1(({
|
|
|
65693
66135
|
);
|
|
65694
66136
|
if (prevWorkspaceSignature !== nextWorkspaceSignature) return false;
|
|
65695
66137
|
if (prevProps.lineInfo.metrics.shift_start !== nextProps.lineInfo.metrics.shift_start) return false;
|
|
66138
|
+
if (prevProps.lineInfo.metrics.shift_end !== nextProps.lineInfo.metrics.shift_end) return false;
|
|
65696
66139
|
if (prevProps.hourlyThreshold !== nextProps.hourlyThreshold) return false;
|
|
66140
|
+
if (JSON.stringify(prevProps.idleTimeHourly || {}) !== JSON.stringify(nextProps.idleTimeHourly || {})) return false;
|
|
65697
66141
|
if (prevProps.urlDate !== nextProps.urlDate || prevProps.urlShift !== nextProps.urlShift) return false;
|
|
65698
66142
|
if (prevProps.workspaceDisplayNames !== nextProps.workspaceDisplayNames) return false;
|
|
65699
66143
|
return true;
|
|
@@ -65961,6 +66405,7 @@ var KPIDetailView = ({
|
|
|
65961
66405
|
}
|
|
65962
66406
|
}, [urlDate, urlShift, urlTab]);
|
|
65963
66407
|
const { shiftConfig, isLoading: isShiftConfigLoading } = useDynamicShiftConfig(lineId);
|
|
66408
|
+
const lineTimezone = shiftConfig?.timezone || configuredTimezone;
|
|
65964
66409
|
const getShiftName = useCallback((shiftId) => {
|
|
65965
66410
|
return getShiftNameById(shiftId, configuredTimezone, shiftConfig);
|
|
65966
66411
|
}, [configuredTimezone, shiftConfig]);
|
|
@@ -66001,12 +66446,12 @@ var KPIDetailView = ({
|
|
|
66001
66446
|
operationalTodayDate.setHours(0, 0, 0, 0);
|
|
66002
66447
|
compareDateInZone.setHours(0, 0, 0, 0);
|
|
66003
66448
|
const diffTime = compareDateInZone.getTime() - operationalTodayDate.getTime();
|
|
66004
|
-
const
|
|
66005
|
-
if (
|
|
66006
|
-
if (
|
|
66007
|
-
if (
|
|
66008
|
-
if (
|
|
66009
|
-
if (
|
|
66449
|
+
const diffDays2 = Math.round(diffTime / (1e3 * 60 * 60 * 24));
|
|
66450
|
+
if (diffDays2 === 0) return "Today";
|
|
66451
|
+
if (diffDays2 === -1) return "Yesterday";
|
|
66452
|
+
if (diffDays2 === 1) return "Tomorrow";
|
|
66453
|
+
if (diffDays2 < -1) return `${Math.abs(diffDays2)} days ago`;
|
|
66454
|
+
if (diffDays2 > 1) return `${diffDays2} days ahead`;
|
|
66010
66455
|
return "Today";
|
|
66011
66456
|
}, [configuredTimezone, shiftConfig]);
|
|
66012
66457
|
const {
|
|
@@ -66216,6 +66661,7 @@ var KPIDetailView = ({
|
|
|
66216
66661
|
underperforming_workspace_uuids: metrics2.underperforming_workspace_uuids || [],
|
|
66217
66662
|
output_array: metrics2.output_array || [],
|
|
66218
66663
|
output_hourly: metrics2.output_hourly,
|
|
66664
|
+
hourly_target_output: metrics2.hourly_target_output,
|
|
66219
66665
|
line_threshold: metrics2.line_threshold ?? 0,
|
|
66220
66666
|
threshold_pph: metrics2.threshold_pph ?? 0,
|
|
66221
66667
|
shift_start: metrics2.shift_start || "06:00",
|
|
@@ -66225,7 +66671,7 @@ var KPIDetailView = ({
|
|
|
66225
66671
|
idle_time_hourly: metrics2.idle_time_hourly || null,
|
|
66226
66672
|
// Multi-SKU additive fields (Phase 6) — propagated from
|
|
66227
66673
|
// `useLineDetailPageData`. Backend authoritative; we never recompute.
|
|
66228
|
-
// The
|
|
66674
|
+
// The output-card selection below uses these to swap header KPIs per selected SKU.
|
|
66229
66675
|
sku_aware: Boolean(metrics2.sku_aware),
|
|
66230
66676
|
real_sku_count: metrics2.real_sku_count ?? 0,
|
|
66231
66677
|
sku_breakdown: Array.isArray(metrics2.sku_breakdown) ? metrics2.sku_breakdown : [],
|
|
@@ -66322,7 +66768,7 @@ var KPIDetailView = ({
|
|
|
66322
66768
|
}, [lineSkuSegments, outputChartSkuBreakdown, hasUrlDate, hasUrlShift]);
|
|
66323
66769
|
const normalizedSelectedSkuId = selectedSkuId !== "all" ? selectedSkuId : null;
|
|
66324
66770
|
const isLineSkuAware = Boolean(resolvedLineInfo?.metrics.sku_aware);
|
|
66325
|
-
const showSkuSelector = isLineSkuAware && realSkuOptions.length > 0;
|
|
66771
|
+
const showSkuSelector = isLineSkuAware && realSkuOptions.length > 0 && !isUptimeMode;
|
|
66326
66772
|
useEffect(() => {
|
|
66327
66773
|
if (selectedSkuId === "all") return;
|
|
66328
66774
|
const stillPresent = realSkuOptions.some((item) => item.sku_id === selectedSkuId);
|
|
@@ -66349,15 +66795,11 @@ var KPIDetailView = ({
|
|
|
66349
66795
|
...resolvedLineInfo.metrics,
|
|
66350
66796
|
current_output: selectedSkuRow.current_output ?? 0,
|
|
66351
66797
|
ideal_output: selectedSkuRow.ideal_output ?? 0,
|
|
66352
|
-
avg_efficiency: selectedSkuRow.avg_efficiency ?? resolvedLineInfo.metrics.avg_efficiency,
|
|
66353
66798
|
total_workspaces: selectedSkuRow.total_workspaces ?? resolvedLineInfo.metrics.total_workspaces,
|
|
66354
66799
|
underperforming_workspaces: selectedSkuRow.underperforming_workspaces ?? resolvedLineInfo.metrics.underperforming_workspaces,
|
|
66355
66800
|
underperforming_workspace_names: selectedSkuRow.underperforming_workspace_names ?? resolvedLineInfo.metrics.underperforming_workspace_names,
|
|
66356
66801
|
underperforming_workspace_uuids: selectedSkuRow.underperforming_workspace_uuids ?? resolvedLineInfo.metrics.underperforming_workspace_uuids,
|
|
66357
|
-
|
|
66358
|
-
output_hourly: selectedSkuRow.output_hourly ?? resolvedLineInfo.metrics.output_hourly,
|
|
66359
|
-
line_threshold: selectedSkuRow.line_threshold ?? resolvedLineInfo.metrics.line_threshold,
|
|
66360
|
-
poorest_performing_workspaces: selectedSkuRow.poorest_performing_workspaces ?? resolvedLineInfo.metrics.poorest_performing_workspaces
|
|
66802
|
+
line_threshold: selectedSkuRow.line_threshold ?? resolvedLineInfo.metrics.line_threshold
|
|
66361
66803
|
}
|
|
66362
66804
|
};
|
|
66363
66805
|
}, [resolvedLineInfo, selectedSkuRow]);
|
|
@@ -67136,11 +67578,9 @@ var KPIDetailView = ({
|
|
|
67136
67578
|
showIdleTime: idleTimeVlmEnabled
|
|
67137
67579
|
}
|
|
67138
67580
|
) : (
|
|
67139
|
-
//
|
|
67140
|
-
//
|
|
67141
|
-
//
|
|
67142
|
-
// cards. When `selectedSkuId === 'all'`, this is exactly
|
|
67143
|
-
// `resolvedLineInfo` (aggregate path preserved).
|
|
67581
|
+
// Keep the line output + underperforming cards SKU-aware,
|
|
67582
|
+
// while Average Efficiency stays on the aggregate line
|
|
67583
|
+
// metrics even when a specific SKU is selected.
|
|
67144
67584
|
/* @__PURE__ */ jsx(
|
|
67145
67585
|
MetricCards,
|
|
67146
67586
|
{
|
|
@@ -67185,6 +67625,10 @@ var KPIDetailView = ({
|
|
|
67185
67625
|
workspaceDisplayNames,
|
|
67186
67626
|
hourlyOutputData,
|
|
67187
67627
|
hourlyThreshold,
|
|
67628
|
+
hourlyTargetOutput: chartMetrics?.hourly_target_output,
|
|
67629
|
+
shiftBreaks: shiftConfig?.shifts?.find((shift) => shift.shiftId === resolvedLineInfo.shift_id)?.breaks || [],
|
|
67630
|
+
idleTimeHourly: chartMetrics?.idle_time_hourly,
|
|
67631
|
+
timezone: lineTimezone,
|
|
67188
67632
|
urlDate,
|
|
67189
67633
|
urlShift,
|
|
67190
67634
|
navigate,
|
|
@@ -67250,8 +67694,8 @@ var KPIDetailView = ({
|
|
|
67250
67694
|
showIdleTime: idleTimeVlmEnabled
|
|
67251
67695
|
}
|
|
67252
67696
|
) : (
|
|
67253
|
-
//
|
|
67254
|
-
//
|
|
67697
|
+
// Keep the line output + underperforming cards SKU-aware,
|
|
67698
|
+
// while Average Efficiency stays aggregate.
|
|
67255
67699
|
/* @__PURE__ */ jsx(
|
|
67256
67700
|
MetricCards,
|
|
67257
67701
|
{
|
|
@@ -67296,6 +67740,8 @@ var KPIDetailView = ({
|
|
|
67296
67740
|
workspaceDisplayNames,
|
|
67297
67741
|
hourlyOutputData,
|
|
67298
67742
|
hourlyThreshold,
|
|
67743
|
+
idleTimeHourly: chartMetrics?.idle_time_hourly,
|
|
67744
|
+
timezone: lineTimezone,
|
|
67299
67745
|
urlDate,
|
|
67300
67746
|
urlShift,
|
|
67301
67747
|
navigate,
|
|
@@ -73532,12 +73978,12 @@ var getDaysDifference = (date, timezone = "UTC", shiftStartTime = "06:00") => {
|
|
|
73532
73978
|
operationalTodayDate.setHours(0, 0, 0, 0);
|
|
73533
73979
|
compareDateInTz.setHours(0, 0, 0, 0);
|
|
73534
73980
|
const diffTime = compareDateInTz.getTime() - operationalTodayDate.getTime();
|
|
73535
|
-
const
|
|
73536
|
-
if (
|
|
73537
|
-
if (
|
|
73538
|
-
if (
|
|
73539
|
-
if (
|
|
73540
|
-
if (
|
|
73981
|
+
const diffDays2 = Math.round(diffTime / (1e3 * 60 * 60 * 24));
|
|
73982
|
+
if (diffDays2 === 0) return "Today";
|
|
73983
|
+
if (diffDays2 === -1) return "Yesterday";
|
|
73984
|
+
if (diffDays2 === 1) return "Tomorrow";
|
|
73985
|
+
if (diffDays2 < -1) return `${Math.abs(diffDays2)} days ago`;
|
|
73986
|
+
if (diffDays2 > 1) return `${diffDays2} days ahead`;
|
|
73541
73987
|
return "Today";
|
|
73542
73988
|
};
|
|
73543
73989
|
var getInitialTab = (sourceType, defaultTab, fromMonthly, urlDate) => {
|
|
@@ -73973,7 +74419,7 @@ var WorkspaceDetailView = ({
|
|
|
73973
74419
|
() => resolveLiveSkuId(workspace?.sku_segments),
|
|
73974
74420
|
[workspace?.sku_segments]
|
|
73975
74421
|
);
|
|
73976
|
-
const activeSkuId = selectedSkuId
|
|
74422
|
+
const activeSkuId = selectedSkuId;
|
|
73977
74423
|
const resolvedLineId = effectiveLineId || workspace?.line_id || cachedDetailedMetrics?.line_id || cachedOverviewMetrics?.line_id || overviewFallback?.line_id;
|
|
73978
74424
|
const { timezone: cycleTimeTimezone } = useTimezone({
|
|
73979
74425
|
lineId: resolvedLineId || void 0,
|
|
@@ -74941,14 +75387,16 @@ var WorkspaceDetailView = ({
|
|
|
74941
75387
|
{
|
|
74942
75388
|
data: workspace.hourly_action_counts || [],
|
|
74943
75389
|
pphThreshold: workspace.pph_threshold || 0,
|
|
75390
|
+
hourlyTargetOutput: workspace.hourly_target_output,
|
|
74944
75391
|
shiftStart: workspace.shift_start || "06:00",
|
|
74945
75392
|
shiftEnd: workspace.shift_end,
|
|
75393
|
+
shiftBreaks: shiftConfig?.shifts?.find((shift2) => shift2.shiftId === workspace.shift_id)?.breaks || [],
|
|
74946
75394
|
showIdleTime: showChartIdleTime,
|
|
74947
75395
|
idleTimeHourly: workspace.idle_time_hourly,
|
|
74948
75396
|
idleTimeClips,
|
|
74949
75397
|
idleTimeClipClassifications,
|
|
74950
75398
|
shiftDate: idleClipDate,
|
|
74951
|
-
timezone,
|
|
75399
|
+
timezone: effectiveCycleTimeTimezone,
|
|
74952
75400
|
skuSegments: isSkuAware ? skuSegments : void 0,
|
|
74953
75401
|
activeSkuId
|
|
74954
75402
|
}
|
|
@@ -75087,14 +75535,16 @@ var WorkspaceDetailView = ({
|
|
|
75087
75535
|
{
|
|
75088
75536
|
data: workspace.hourly_action_counts || [],
|
|
75089
75537
|
pphThreshold: workspace.pph_threshold || 0,
|
|
75538
|
+
hourlyTargetOutput: workspace.hourly_target_output,
|
|
75090
75539
|
shiftStart: workspace.shift_start || "06:00",
|
|
75091
75540
|
shiftEnd: workspace.shift_end,
|
|
75541
|
+
shiftBreaks: shiftConfig?.shifts?.find((shift2) => shift2.shiftId === workspace.shift_id)?.breaks || [],
|
|
75092
75542
|
showIdleTime: showChartIdleTime,
|
|
75093
75543
|
idleTimeHourly: workspace.idle_time_hourly,
|
|
75094
75544
|
idleTimeClips,
|
|
75095
75545
|
idleTimeClipClassifications,
|
|
75096
75546
|
shiftDate: idleClipDate,
|
|
75097
|
-
timezone,
|
|
75547
|
+
timezone: effectiveCycleTimeTimezone,
|
|
75098
75548
|
skuSegments: isSkuAware ? skuSegments : void 0,
|
|
75099
75549
|
activeSkuId
|
|
75100
75550
|
}
|
|
@@ -78371,8 +78821,8 @@ var ImprovementCenterView = () => {
|
|
|
78371
78821
|
const firstSeen = new Date(new Date(openedAt).toLocaleString("en-US", { timeZone: timezone }));
|
|
78372
78822
|
if (Number.isNaN(firstSeen.getTime())) return void 0;
|
|
78373
78823
|
const now4 = new Date((/* @__PURE__ */ new Date()).toLocaleString("en-US", { timeZone: timezone }));
|
|
78374
|
-
const
|
|
78375
|
-
return Math.max(1, Math.ceil(
|
|
78824
|
+
const diffDays2 = Math.max(0, Math.floor((now4.getTime() - firstSeen.getTime()) / (1e3 * 60 * 60 * 24)));
|
|
78825
|
+
return Math.max(1, Math.ceil(diffDays2 / 7));
|
|
78376
78826
|
};
|
|
78377
78827
|
const toZonedDate = (value) => {
|
|
78378
78828
|
if (!value) return void 0;
|
|
@@ -79053,12 +79503,12 @@ var ThreadSidebar = ({
|
|
|
79053
79503
|
const date = new Date(dateString);
|
|
79054
79504
|
const now4 = /* @__PURE__ */ new Date();
|
|
79055
79505
|
const diffMs = now4.getTime() - date.getTime();
|
|
79056
|
-
const
|
|
79057
|
-
if (
|
|
79506
|
+
const diffDays2 = Math.floor(diffMs / (1e3 * 60 * 60 * 24));
|
|
79507
|
+
if (diffDays2 === 0) {
|
|
79058
79508
|
return "Today";
|
|
79059
|
-
} else if (
|
|
79509
|
+
} else if (diffDays2 === 1) {
|
|
79060
79510
|
return "Yesterday";
|
|
79061
|
-
} else if (
|
|
79511
|
+
} else if (diffDays2 < 7) {
|
|
79062
79512
|
return date.toLocaleDateString("en-US", { weekday: "short" });
|
|
79063
79513
|
} else {
|
|
79064
79514
|
return date.toLocaleDateString("en-US", { month: "short", day: "numeric" });
|