@optifye/dashboard-core 6.12.0 → 6.12.1

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.js CHANGED
@@ -4234,6 +4234,22 @@ var isDummyRow = (row, dummySet) => {
4234
4234
  if (typeof skuId !== "string" || skuId.length === 0) return false;
4235
4235
  return dummySet.has(skuId);
4236
4236
  };
4237
+ var isActiveOutputRow = (row) => {
4238
+ const currentOutput = coerceOptionalNumber(row.current_output) ?? 0;
4239
+ const idealOutput = coerceOptionalNumber(row.ideal_output) ?? 0;
4240
+ return currentOutput > 0 || idealOutput > 0;
4241
+ };
4242
+ var roundHalfUpInt = (value) => Math.floor(value + 0.5);
4243
+ var dedupeStringsPreserveOrder = (values) => {
4244
+ const seen = /* @__PURE__ */ new Set();
4245
+ const ordered = [];
4246
+ for (const value of values) {
4247
+ if (!value || seen.has(value)) continue;
4248
+ seen.add(value);
4249
+ ordered.push(value);
4250
+ }
4251
+ return ordered;
4252
+ };
4237
4253
  var emptyAggregate = () => ({
4238
4254
  current_output: 0,
4239
4255
  ideal_output: 0,
@@ -4352,10 +4368,7 @@ var combineLineMetricsRows = (rows, dummySkuId) => {
4352
4368
  );
4353
4369
  const dummyLineMetricsRow = rows.find((row) => isDummyRow(row, dummySet));
4354
4370
  const lineThresholdValue = dummyLineMetricsRow ? safeFloat(dummyLineMetricsRow.line_threshold) : safeFloat(rowsForAggregation[0]?.line_threshold ?? 0);
4355
- const underperformingWorkspacesSum = rowsForAggregation.reduce(
4356
- (acc, row) => acc + safeInt(row.underperforming_workspaces),
4357
- 0
4358
- );
4371
+ const activeRealRows = rowsForAggregation.filter(isActiveOutputRow);
4359
4372
  const weighted = (field) => {
4360
4373
  const pairs = [];
4361
4374
  rowsForAggregation.forEach((row, idx) => {
@@ -4377,6 +4390,12 @@ var combineLineMetricsRows = (rows, dummySkuId) => {
4377
4390
  const avgEfficiency = weighted("avg_efficiency");
4378
4391
  const avgCycleTime = weighted("avg_cycle_time");
4379
4392
  const thresholdPph = weighted("threshold_pph");
4393
+ const underperformingWorkspaces = activeRealRows.length > 0 ? roundHalfUpInt(
4394
+ activeRealRows.reduce(
4395
+ (acc, row) => acc + safeInt(row.underperforming_workspaces),
4396
+ 0
4397
+ ) / activeRealRows.length
4398
+ ) : 0;
4380
4399
  const merged = mergeHourlyFields(rows);
4381
4400
  const outputArrays = [];
4382
4401
  for (const row of rowsForAggregation) {
@@ -4399,7 +4418,7 @@ var combineLineMetricsRows = (rows, dummySkuId) => {
4399
4418
  }
4400
4419
  const underperformingNames = [];
4401
4420
  const underperformingUuids = [];
4402
- for (const row of rowsForAggregation) {
4421
+ for (const row of activeRealRows) {
4403
4422
  const names = row.underperforming_workspace_names;
4404
4423
  if (Array.isArray(names)) {
4405
4424
  for (const n of names) {
@@ -4413,6 +4432,8 @@ var combineLineMetricsRows = (rows, dummySkuId) => {
4413
4432
  }
4414
4433
  }
4415
4434
  }
4435
+ const dedupedUnderperformingNames = dedupeStringsPreserveOrder(underperformingNames);
4436
+ const dedupedUnderperformingUuids = dedupeStringsPreserveOrder(underperformingUuids);
4416
4437
  let totalWorkspacesValue = null;
4417
4438
  const primaryTotal = coerceOptionalNumber(primary.total_workspaces);
4418
4439
  if (primaryTotal !== null) {
@@ -4434,9 +4455,9 @@ var combineLineMetricsRows = (rows, dummySkuId) => {
4434
4455
  line_threshold: lineThresholdValue,
4435
4456
  threshold_pph: thresholdPph,
4436
4457
  total_workspaces: safeInt(totalWorkspacesValue ?? 0),
4437
- underperforming_workspaces: underperformingWorkspacesSum,
4438
- underperforming_workspace_names: underperformingNames,
4439
- underperforming_workspace_uuids: underperformingUuids,
4458
+ underperforming_workspaces: underperformingWorkspaces,
4459
+ underperforming_workspace_names: dedupedUnderperformingNames,
4460
+ underperforming_workspace_uuids: dedupedUnderperformingUuids,
4440
4461
  output_array: outputArray,
4441
4462
  output_hourly: merged.output_hourly,
4442
4463
  idle_time_hourly: merged.idle_time_hourly,
@@ -5561,11 +5582,11 @@ var getDaysDifferenceInZone = (compareDate, timezone) => {
5561
5582
  todayInZone.setHours(0, 0, 0, 0);
5562
5583
  compareDateInZone.setHours(0, 0, 0, 0);
5563
5584
  const diffTime = todayInZone.getTime() - compareDateInZone.getTime();
5564
- const diffDays = Math.ceil(diffTime / (1e3 * 60 * 60 * 24));
5565
- if (diffDays < 0) return "In the future";
5566
- if (diffDays === 0) return "Today";
5567
- if (diffDays === 1) return "Yesterday";
5568
- return `${diffDays} days ago`;
5585
+ const diffDays2 = Math.ceil(diffTime / (1e3 * 60 * 60 * 24));
5586
+ if (diffDays2 < 0) return "In the future";
5587
+ if (diffDays2 === 0) return "Today";
5588
+ if (diffDays2 === 1) return "Yesterday";
5589
+ return `${diffDays2} days ago`;
5569
5590
  };
5570
5591
  var getDashboardHeaderTimeInZone = (date = /* @__PURE__ */ new Date(), timezone, timeOptions, locale = DEFAULT_LOCALE) => {
5571
5592
  const defaultOptions = {
@@ -13012,6 +13033,7 @@ var toWorkspaceDetailedMetrics = ({
13012
13033
  const targetOutput = coerceNumber(data.target_output ?? data.total_day_output, 0);
13013
13034
  const idealOutput = coerceNumber(data.ideal_output ?? data.ideal_output_until_now, 0);
13014
13035
  const outputDifference = totalActions - idealOutput;
13036
+ const hourlyTargetOutput = Array.isArray(data.hourly_target_output) ? data.hourly_target_output.map((value) => value === null || value === void 0 ? null : coerceNumber(value, 0)) : null;
13015
13037
  const hourlyCycleTimes = Array.isArray(data.hourly_cycle_times) ? data.hourly_cycle_times.map((value) => coerceNumber(value, 0)) : [];
13016
13038
  const cycleCompletionClipCount = data.cycle_completion_clip_count === null || data.cycle_completion_clip_count === void 0 ? null : coerceNumber(data.cycle_completion_clip_count, 0);
13017
13039
  const cycleTimeDataStatus = data.cycle_time_data_status === "missing_clips" ? "missing_clips" : data.cycle_time_data_status === "available" ? "available" : null;
@@ -13063,6 +13085,7 @@ var toWorkspaceDetailedMetrics = ({
13063
13085
  avg_efficiency: coerceNumber(data.efficiency ?? data.avg_efficiency, 0),
13064
13086
  total_actions: totalActions,
13065
13087
  hourly_action_counts: hourlyActionCounts,
13088
+ hourly_target_output: hourlyTargetOutput,
13066
13089
  hourly_cycle_times: hourlyCycleTimes,
13067
13090
  cycle_completion_clip_count: cycleCompletionClipCount,
13068
13091
  cycle_time_data_status: cycleTimeDataStatus,
@@ -19679,7 +19702,7 @@ function formatRelativeTime(timestamp) {
19679
19702
  const diffSeconds = Math.floor(diffMs / 1e3);
19680
19703
  const diffMinutes = Math.floor(diffSeconds / 60);
19681
19704
  const diffHours = Math.floor(diffMinutes / 60);
19682
- const diffDays = Math.floor(diffHours / 24);
19705
+ const diffDays2 = Math.floor(diffHours / 24);
19683
19706
  if (diffSeconds < 60) {
19684
19707
  return "Less than a minute ago";
19685
19708
  }
@@ -19691,8 +19714,8 @@ function formatRelativeTime(timestamp) {
19691
19714
  const hourLabel = diffHours === 1 ? "hour" : "hours";
19692
19715
  return `${diffHours} ${hourLabel} ago`;
19693
19716
  }
19694
- const dayLabel = diffDays === 1 ? "day" : "days";
19695
- return `${diffDays} ${dayLabel} ago`;
19717
+ const dayLabel = diffDays2 === 1 ? "day" : "days";
19718
+ return `${diffDays2} ${dayLabel} ago`;
19696
19719
  } catch (error) {
19697
19720
  console.error("[formatRelativeTime] Error formatting timestamp:", error);
19698
19721
  return "Unknown";
@@ -35191,6 +35214,223 @@ var Button = React144__namespace.forwardRef(
35191
35214
  }
35192
35215
  );
35193
35216
  Button.displayName = "Button";
35217
+ var padTime = (value) => value.toString().padStart(2, "0");
35218
+ var parseTime = (timeValue) => {
35219
+ if (!timeValue) return null;
35220
+ const [hourPart, minutePart] = timeValue.split(":");
35221
+ const hour = Number.parseInt(hourPart, 10);
35222
+ const minute = Number.parseInt(minutePart ?? "0", 10);
35223
+ if (!Number.isFinite(hour) || !Number.isFinite(minute)) return null;
35224
+ return { hour, minute };
35225
+ };
35226
+ var normalizeIdleTimeHourly = (idleTimeHourly) => {
35227
+ if (!idleTimeHourly || typeof idleTimeHourly !== "object") {
35228
+ return {};
35229
+ }
35230
+ return Object.fromEntries(
35231
+ Object.entries(idleTimeHourly).map(([key, value]) => {
35232
+ if (Array.isArray(value)) return [key, value];
35233
+ if (value && Array.isArray(value.values)) {
35234
+ return [key, value.values];
35235
+ }
35236
+ return [key, []];
35237
+ })
35238
+ );
35239
+ };
35240
+ var interpretIdleValue = (value) => {
35241
+ if (value === 1 || value === "1") return "idle";
35242
+ if (value === 0 || value === "0") return "active";
35243
+ if (value === "x" || value === null || value === void 0) return "unknown";
35244
+ return "unknown";
35245
+ };
35246
+ var getShiftDurationMinutes = (shiftStart, shiftEnd) => {
35247
+ const start = parseTime(shiftStart);
35248
+ const end = parseTime(shiftEnd);
35249
+ if (!start || !end) return null;
35250
+ let duration = end.hour * 60 + end.minute - (start.hour * 60 + start.minute);
35251
+ if (duration <= 0) {
35252
+ duration += 24 * 60;
35253
+ }
35254
+ return duration > 0 ? duration : null;
35255
+ };
35256
+ var getShiftElapsedMinutes = ({
35257
+ shiftStart,
35258
+ shiftEnd,
35259
+ shiftDate,
35260
+ timezone,
35261
+ now: now4 = /* @__PURE__ */ new Date()
35262
+ }) => {
35263
+ if (!shiftDate || !timezone) return null;
35264
+ const startTime = parseTime(shiftStart);
35265
+ const endTime = parseTime(shiftEnd);
35266
+ if (!startTime || !endTime) return null;
35267
+ const shiftStartDate = dateFnsTz.fromZonedTime(
35268
+ `${shiftDate}T${padTime(startTime.hour)}:${padTime(startTime.minute)}:00`,
35269
+ timezone
35270
+ );
35271
+ let shiftEndDate = dateFnsTz.fromZonedTime(
35272
+ `${shiftDate}T${padTime(endTime.hour)}:${padTime(endTime.minute)}:00`,
35273
+ timezone
35274
+ );
35275
+ if (shiftEndDate <= shiftStartDate) {
35276
+ shiftEndDate = dateFns.addDays(shiftEndDate, 1);
35277
+ }
35278
+ const shiftMinutes = Math.max(dateFns.differenceInMinutes(shiftEndDate, shiftStartDate), 0);
35279
+ if (shiftMinutes <= 0) return null;
35280
+ const elapsed = dateFns.differenceInMinutes(now4, shiftStartDate);
35281
+ return Math.min(Math.max(elapsed, 0), shiftMinutes);
35282
+ };
35283
+ var maskFutureHourlySeries = ({
35284
+ data,
35285
+ shiftStart,
35286
+ shiftEnd,
35287
+ shiftDate,
35288
+ timezone,
35289
+ now: now4 = /* @__PURE__ */ new Date()
35290
+ }) => {
35291
+ if (!Array.isArray(data)) {
35292
+ return [];
35293
+ }
35294
+ const normalizedData = data.map((value) => typeof value === "number" && Number.isFinite(value) ? value : null);
35295
+ if (!normalizedData.length) {
35296
+ return normalizedData;
35297
+ }
35298
+ const shiftMinutes = getShiftDurationMinutes(shiftStart, shiftEnd);
35299
+ const elapsedMinutes = getShiftElapsedMinutes({
35300
+ shiftStart,
35301
+ shiftEnd,
35302
+ shiftDate,
35303
+ timezone,
35304
+ now: now4
35305
+ });
35306
+ if (shiftMinutes === null || elapsedMinutes === null || elapsedMinutes >= shiftMinutes) {
35307
+ return normalizedData;
35308
+ }
35309
+ return normalizedData.map((value, index) => {
35310
+ const slotStartMinutes = index * 60;
35311
+ return slotStartMinutes > elapsedMinutes ? null : value;
35312
+ });
35313
+ };
35314
+ var buildUptimeSeries = ({
35315
+ idleTimeHourly,
35316
+ shiftStart,
35317
+ shiftEnd,
35318
+ shiftDate,
35319
+ timezone,
35320
+ elapsedMinutes
35321
+ }) => {
35322
+ const normalizedIdle = normalizeIdleTimeHourly(idleTimeHourly || {});
35323
+ const hasIdleData = Object.keys(normalizedIdle).length > 0;
35324
+ if (!hasIdleData || !shiftDate || !timezone) {
35325
+ return {
35326
+ points: [],
35327
+ activeMinutes: 0,
35328
+ idleMinutes: 0,
35329
+ availableMinutes: 0,
35330
+ shiftMinutes: 0,
35331
+ elapsedMinutes: 0,
35332
+ hasData: false
35333
+ };
35334
+ }
35335
+ const startTime = parseTime(shiftStart);
35336
+ const endTime = parseTime(shiftEnd);
35337
+ if (!startTime || !endTime) {
35338
+ return {
35339
+ points: [],
35340
+ activeMinutes: 0,
35341
+ idleMinutes: 0,
35342
+ availableMinutes: 0,
35343
+ shiftMinutes: 0,
35344
+ elapsedMinutes: 0,
35345
+ hasData: false
35346
+ };
35347
+ }
35348
+ const shiftStartDate = dateFnsTz.fromZonedTime(
35349
+ `${shiftDate}T${padTime(startTime.hour)}:${padTime(startTime.minute)}:00`,
35350
+ timezone
35351
+ );
35352
+ let shiftEndDate = dateFnsTz.fromZonedTime(
35353
+ `${shiftDate}T${padTime(endTime.hour)}:${padTime(endTime.minute)}:00`,
35354
+ timezone
35355
+ );
35356
+ if (shiftEndDate <= shiftStartDate) {
35357
+ shiftEndDate = dateFns.addDays(shiftEndDate, 1);
35358
+ }
35359
+ const shiftMinutes = Math.max(dateFns.differenceInMinutes(shiftEndDate, shiftStartDate), 0);
35360
+ if (shiftMinutes <= 0) {
35361
+ return {
35362
+ points: [],
35363
+ activeMinutes: 0,
35364
+ idleMinutes: 0,
35365
+ availableMinutes: 0,
35366
+ shiftMinutes: 0,
35367
+ elapsedMinutes: 0,
35368
+ hasData: false
35369
+ };
35370
+ }
35371
+ const elapsedMinutesClamped = Number.isFinite(elapsedMinutes) ? Math.min(Math.max(Math.floor(elapsedMinutes ?? 0), 0), shiftMinutes) : shiftMinutes;
35372
+ const points = [];
35373
+ let activeMinutes = 0;
35374
+ let idleMinutes = 0;
35375
+ for (let minuteIndex = 0; minuteIndex < shiftMinutes; minuteIndex += 1) {
35376
+ const minuteDate = dateFns.addMinutes(shiftStartDate, minuteIndex);
35377
+ const timeLabel = dateFnsTz.formatInTimeZone(minuteDate, timezone, "h:mm a");
35378
+ if (minuteIndex >= elapsedMinutesClamped) {
35379
+ points.push({
35380
+ minuteIndex,
35381
+ timeLabel,
35382
+ uptime: null,
35383
+ status: "unknown"
35384
+ });
35385
+ continue;
35386
+ }
35387
+ const hourKey = dateFnsTz.formatInTimeZone(minuteDate, timezone, "H");
35388
+ const minuteKey = Number.parseInt(dateFnsTz.formatInTimeZone(minuteDate, timezone, "m"), 10);
35389
+ const hourBucket = normalizedIdle[hourKey] || [];
35390
+ const value = Array.isArray(hourBucket) ? hourBucket[minuteKey] : void 0;
35391
+ const status = interpretIdleValue(value);
35392
+ if (status === "active") activeMinutes += 1;
35393
+ if (status === "idle") idleMinutes += 1;
35394
+ points.push({
35395
+ minuteIndex,
35396
+ timeLabel,
35397
+ uptime: status === "active" ? 1 : status === "idle" ? 0 : null,
35398
+ status
35399
+ });
35400
+ }
35401
+ return {
35402
+ points,
35403
+ activeMinutes,
35404
+ idleMinutes,
35405
+ availableMinutes: activeMinutes + idleMinutes,
35406
+ shiftMinutes,
35407
+ elapsedMinutes: elapsedMinutesClamped,
35408
+ hasData: activeMinutes + idleMinutes > 0
35409
+ };
35410
+ };
35411
+ var getUptimeUtilizationPercent = (shift) => {
35412
+ const efficiency = shift.efficiency;
35413
+ if (Number.isFinite(efficiency)) {
35414
+ return Math.round(Math.max(0, Math.min(100, Number(efficiency))));
35415
+ }
35416
+ const idleSeconds = Number.isFinite(shift.idleTime) ? Number(shift.idleTime) : 0;
35417
+ const activeSeconds = Number.isFinite(shift.activeTimeSeconds) ? Number(shift.activeTimeSeconds) : null;
35418
+ let availableSeconds = Number.isFinite(shift.availableTimeSeconds) ? Number(shift.availableTimeSeconds) : null;
35419
+ if (availableSeconds === null) {
35420
+ if ((activeSeconds ?? 0) > 0 || idleSeconds > 0) {
35421
+ availableSeconds = (activeSeconds ?? 0) + idleSeconds;
35422
+ } else {
35423
+ return 0;
35424
+ }
35425
+ }
35426
+ if (availableSeconds <= 0) return 0;
35427
+ const clampedIdleSeconds = Math.min(Math.max(idleSeconds, 0), availableSeconds);
35428
+ const productiveSeconds = Math.max(
35429
+ activeSeconds ?? availableSeconds - clampedIdleSeconds,
35430
+ 0
35431
+ );
35432
+ return Math.round(productiveSeconds / availableSeconds * 100);
35433
+ };
35194
35434
 
35195
35435
  // src/components/charts/skuDividerUtils.ts
35196
35436
  var HOURLY_TIME_RE = /^(\d{1,2}):(\d{2})/;
@@ -35203,37 +35443,162 @@ var parseTimeOfDay = (timeValue) => {
35203
35443
  if (hour < 0 || hour > 23 || minute < 0 || minute > 59) return null;
35204
35444
  return { hour, minute };
35205
35445
  };
35206
- var computeSegmentOffset = (segment, shift) => {
35207
- if (!segment?.start_time) return null;
35446
+ var parseDateKey = (value) => {
35447
+ const [yearPart, monthPart, dayPart] = value.split("-").map(Number);
35448
+ if (!Number.isFinite(yearPart) || !Number.isFinite(monthPart) || !Number.isFinite(dayPart)) {
35449
+ return null;
35450
+ }
35451
+ return Date.UTC(yearPart, monthPart - 1, dayPart);
35452
+ };
35453
+ var diffDays = (left, right) => {
35454
+ const leftUtc = parseDateKey(left);
35455
+ const rightUtc = parseDateKey(right);
35456
+ if (leftUtc === null || rightUtc === null) return null;
35457
+ return Math.round((leftUtc - rightUtc) / (24 * 60 * 60 * 1e3));
35458
+ };
35459
+ var getSegmentMinutes = (isoString, timeZone, reportDate) => {
35460
+ const date = new Date(isoString);
35461
+ if (Number.isNaN(date.getTime())) return Number.NaN;
35462
+ const formatter = new Intl.DateTimeFormat("en-US", {
35463
+ timeZone,
35464
+ year: "numeric",
35465
+ month: "2-digit",
35466
+ day: "2-digit",
35467
+ hour: "2-digit",
35468
+ minute: "2-digit",
35469
+ hourCycle: "h23"
35470
+ });
35471
+ const parts = formatter.formatToParts(date).reduce((acc, part) => {
35472
+ if (part.type !== "literal") {
35473
+ acc[part.type] = part.value;
35474
+ }
35475
+ return acc;
35476
+ }, {});
35477
+ const dateKey = `${parts.year}-${parts.month}-${parts.day}`;
35478
+ let minutes = Number(parts.hour) * 60 + Number(parts.minute);
35479
+ const dayDelta = diffDays(dateKey, reportDate);
35480
+ if (dayDelta === null) return Number.NaN;
35481
+ minutes += dayDelta * 24 * 60;
35482
+ return minutes;
35483
+ };
35484
+ var computeSegmentOffsetUtc = (segment, shift) => {
35208
35485
  const parsedMs = Date.parse(segment.start_time);
35209
35486
  if (Number.isNaN(parsedMs)) return null;
35210
35487
  const date = new Date(parsedMs);
35211
35488
  const segHour = date.getUTCHours();
35212
35489
  const segMinute = date.getUTCMinutes();
35213
35490
  const segMinutes = segHour * 60 + segMinute;
35214
- let shiftStartMinutes = shift.startHour * 60 + shift.startMinute;
35491
+ const shiftStartMinutes = shift.startHour * 60 + shift.startMinute;
35215
35492
  let deltaMinutes = segMinutes - shiftStartMinutes;
35216
35493
  if (deltaMinutes < 0) deltaMinutes += 24 * 60;
35217
35494
  const offsetHours = deltaMinutes / 60;
35218
35495
  if (offsetHours < 0 || offsetHours > shift.slotCount) return null;
35219
35496
  return offsetHours;
35220
35497
  };
35498
+ var computeSegmentOffset = (segment, shift) => {
35499
+ if (!segment?.start_time) return null;
35500
+ if (shift.shiftDate && shift.timezone) {
35501
+ const segmentMinutes = getSegmentMinutes(
35502
+ segment.start_time,
35503
+ shift.timezone,
35504
+ shift.shiftDate
35505
+ );
35506
+ if (!Number.isFinite(segmentMinutes)) return null;
35507
+ const shiftStartMinutes = shift.startHour * 60 + shift.startMinute;
35508
+ const deltaMinutes = segmentMinutes - shiftStartMinutes;
35509
+ const offsetHours = deltaMinutes / 60;
35510
+ if (offsetHours < 0 || offsetHours > shift.slotCount) return null;
35511
+ return offsetHours;
35512
+ }
35513
+ return computeSegmentOffsetUtc(segment, shift);
35514
+ };
35221
35515
  var resolveShiftWindow2 = (params) => {
35222
35516
  const startTime = parseTimeOfDay(params.shiftStart);
35223
35517
  if (!startTime) return null;
35224
35518
  return {
35225
35519
  startHour: startTime.hour,
35226
35520
  startMinute: startTime.minute,
35227
- slotCount: params.slotCount
35521
+ slotCount: params.slotCount,
35522
+ ...params.shiftDate ? { shiftDate: params.shiftDate } : {},
35523
+ ...params.timezone ? { timezone: params.timezone } : {}
35228
35524
  };
35229
35525
  };
35526
+ var resolveTimelineEndOffset = ({
35527
+ shiftStart,
35528
+ shiftEnd,
35529
+ slotCount,
35530
+ shiftDate,
35531
+ timezone,
35532
+ now: now4
35533
+ }) => {
35534
+ const normalizedSlotCount = Number.isFinite(slotCount) && slotCount > 0 ? slotCount : 0;
35535
+ if (!shiftDate || !timezone) {
35536
+ return normalizedSlotCount;
35537
+ }
35538
+ const shiftMinutes = getShiftDurationMinutes(shiftStart, shiftEnd);
35539
+ const elapsedMinutes = getShiftElapsedMinutes({
35540
+ shiftStart,
35541
+ shiftEnd,
35542
+ shiftDate,
35543
+ timezone,
35544
+ now: now4
35545
+ });
35546
+ if (shiftMinutes === null || elapsedMinutes === null) {
35547
+ return normalizedSlotCount;
35548
+ }
35549
+ if (elapsedMinutes >= shiftMinutes) {
35550
+ return normalizedSlotCount;
35551
+ }
35552
+ return Math.max(0, Math.min(elapsedMinutes / 60, normalizedSlotCount));
35553
+ };
35554
+ var formatSkuRailLabel = (label, segmentWidth, options = {}) => {
35555
+ const horizontalPadding = options.horizontalPadding ?? 8;
35556
+ const minVisibleChars = options.minVisibleChars ?? 4;
35557
+ const averageCharacterWidth = options.averageCharacterWidth ?? 6.6;
35558
+ const compactAverageCharacterWidth = options.compactAverageCharacterWidth ?? 5;
35559
+ const cssTruncation = options.cssTruncation ?? false;
35560
+ const fullLabelThreshold = 6;
35561
+ if (!label) return null;
35562
+ if (!Number.isFinite(segmentWidth) || segmentWidth <= horizontalPadding * 2) return null;
35563
+ const usableWidth = Math.max(segmentWidth - horizontalPadding * 2, 0);
35564
+ const maxChars = Math.floor(usableWidth / averageCharacterWidth);
35565
+ if (maxChars >= Math.max(minVisibleChars, fullLabelThreshold)) {
35566
+ if (label.length <= maxChars || cssTruncation) return label;
35567
+ if (maxChars <= 1) return null;
35568
+ return `${label.slice(0, Math.max(maxChars - 1, 1)).trimEnd()}\u2026`;
35569
+ }
35570
+ const compactLabel = buildCompactSkuRailLabel(label, Math.max(maxChars, 1));
35571
+ const compactMaxChars = Math.floor(usableWidth / compactAverageCharacterWidth);
35572
+ if (compactMaxChars < 1) return null;
35573
+ if (compactLabel.length <= compactMaxChars || cssTruncation) return compactLabel;
35574
+ if (compactMaxChars <= 2) {
35575
+ return compactLabel.slice(0, compactMaxChars);
35576
+ }
35577
+ return `${compactLabel.slice(0, Math.max(compactMaxChars - 1, 1)).trimEnd()}\u2026`;
35578
+ };
35579
+ var buildCompactSkuRailLabel = (label, maxChars) => {
35580
+ const words = label.trim().split(/\s+/).filter(Boolean);
35581
+ if (words.length === 0) return label;
35582
+ const [firstWord] = words;
35583
+ const acronym = words.map((word) => word[0]).join("").toUpperCase();
35584
+ if (maxChars <= 3 && acronym) {
35585
+ return acronym;
35586
+ }
35587
+ if (firstWord.length >= 4) {
35588
+ return firstWord;
35589
+ }
35590
+ return acronym || firstWord;
35591
+ };
35230
35592
  var HourlyOutputChartComponent = ({
35231
35593
  data,
35232
35594
  pphThreshold,
35595
+ hourlyTargetOutput,
35233
35596
  shiftStart,
35234
35597
  shiftEnd,
35235
35598
  showIdleTime = false,
35236
35599
  idleTimeHourly,
35600
+ shiftDate,
35601
+ timezone,
35237
35602
  skuSegments,
35238
35603
  activeSkuId,
35239
35604
  className = ""
@@ -35241,6 +35606,7 @@ var HourlyOutputChartComponent = ({
35241
35606
  const containerRef = React144__namespace.default.useRef(null);
35242
35607
  const [containerReady, setContainerReady] = React144__namespace.default.useState(false);
35243
35608
  const [containerWidth, setContainerWidth] = React144__namespace.default.useState(0);
35609
+ const [hoveredSkuRailLabel, setHoveredSkuRailLabel] = React144__namespace.default.useState(null);
35244
35610
  const idleSlots = React144__namespace.default.useMemo(
35245
35611
  () => buildHourlyIdleSlots({
35246
35612
  idleTimeHourly,
@@ -35386,14 +35752,54 @@ var HourlyOutputChartComponent = ({
35386
35752
  }, [containerWidth]);
35387
35753
  const shiftWindow = React144__namespace.default.useMemo(() => resolveShiftWindow2({
35388
35754
  shiftStart,
35389
- slotCount: SHIFT_DURATION
35390
- }), [shiftStart, shiftEnd, SHIFT_DURATION]);
35755
+ slotCount: SHIFT_DURATION,
35756
+ shiftDate,
35757
+ timezone
35758
+ }), [shiftStart, shiftEnd, SHIFT_DURATION, shiftDate, timezone]);
35759
+ const fallbackTimelineEndOffset = React144__namespace.default.useMemo(
35760
+ () => resolveTimelineEndOffset({
35761
+ shiftStart,
35762
+ shiftEnd,
35763
+ slotCount: SHIFT_DURATION,
35764
+ shiftDate,
35765
+ timezone
35766
+ }),
35767
+ [shiftStart, shiftEnd, SHIFT_DURATION, shiftDate, timezone]
35768
+ );
35769
+ const observedTimelineEndOffset = React144__namespace.default.useMemo(() => {
35770
+ let lastObservedMinute = -1;
35771
+ idleSlots.forEach((slot) => {
35772
+ slot.idleArray.forEach((value, minuteIndex) => {
35773
+ if (value !== "x" && value !== null && value !== void 0) {
35774
+ lastObservedMinute = Math.max(
35775
+ lastObservedMinute,
35776
+ slot.hourIndex * 60 + minuteIndex
35777
+ );
35778
+ }
35779
+ });
35780
+ });
35781
+ if (lastObservedMinute < 0) return null;
35782
+ return Math.min((lastObservedMinute + 1) / 60, SHIFT_DURATION);
35783
+ }, [idleSlots, SHIFT_DURATION]);
35784
+ const timelineEndOffset = observedTimelineEndOffset ?? fallbackTimelineEndOffset;
35785
+ const targetLineEndOffset = React144__namespace.default.useMemo(() => {
35786
+ if (timelineEndOffset >= SHIFT_DURATION) {
35787
+ return SHIFT_DURATION;
35788
+ }
35789
+ if (Number.isInteger(timelineEndOffset)) {
35790
+ return timelineEndOffset;
35791
+ }
35792
+ return Math.min(Math.floor(timelineEndOffset) + 1, SHIFT_DURATION);
35793
+ }, [timelineEndOffset, SHIFT_DURATION]);
35391
35794
  const skuTimelineSegments = React144__namespace.default.useMemo(() => {
35392
- if (!skuSegments || skuSegments.length === 0 || !shiftWindow) return [];
35393
- const withOffsets = skuSegments.map((segment) => ({
35394
- segment,
35395
- offset: computeSegmentOffset(segment, shiftWindow) ?? 0
35396
- })).sort((a, b) => a.offset - b.offset);
35795
+ if (!skuSegments || skuSegments.length === 0 || !shiftWindow || timelineEndOffset <= 0) {
35796
+ return [];
35797
+ }
35798
+ const withOffsets = skuSegments.flatMap((segment) => {
35799
+ const offset = computeSegmentOffset(segment, shiftWindow);
35800
+ if (offset === null) return [];
35801
+ return [{ segment, offset }];
35802
+ }).sort((a, b) => a.offset - b.offset);
35397
35803
  if (withOffsets.length === 0) return [];
35398
35804
  const deduped = [];
35399
35805
  const DUPLICATE_OFFSET_THRESHOLD = 1 / 60;
@@ -35407,9 +35813,9 @@ var HourlyOutputChartComponent = ({
35407
35813
  deduped[0] = { ...deduped[0], offset: 0 };
35408
35814
  }
35409
35815
  return deduped.map((entry, index) => {
35410
- const nextOffset = index < deduped.length - 1 ? deduped[index + 1].offset : SHIFT_DURATION;
35411
- const start = Math.max(0, Math.min(entry.offset, SHIFT_DURATION));
35412
- const end = Math.max(start, Math.min(nextOffset, SHIFT_DURATION));
35816
+ const nextOffset = index < deduped.length - 1 ? deduped[index + 1].offset : timelineEndOffset;
35817
+ const start = Math.max(0, Math.min(entry.offset, timelineEndOffset));
35818
+ const end = Math.max(start, Math.min(nextOffset, timelineEndOffset));
35413
35819
  return {
35414
35820
  skuId: entry.segment.sku_id,
35415
35821
  label: entry.segment.sku_code,
@@ -35418,15 +35824,66 @@ var HourlyOutputChartComponent = ({
35418
35824
  pphThreshold: entry.segment.pph_threshold ?? pphThreshold
35419
35825
  };
35420
35826
  }).filter((segment) => segment.end > segment.start);
35421
- }, [skuSegments, shiftWindow, SHIFT_DURATION, pphThreshold]);
35827
+ }, [skuSegments, shiftWindow, timelineEndOffset, pphThreshold]);
35828
+ const targetTimelineSegments = React144__namespace.default.useMemo(() => {
35829
+ if (skuTimelineSegments.length === 0) return [];
35830
+ return skuTimelineSegments.map((segment, index) => ({
35831
+ ...segment,
35832
+ end: index === skuTimelineSegments.length - 1 ? Math.max(segment.start, targetLineEndOffset) : segment.end
35833
+ })).filter((segment) => segment.end > segment.start);
35834
+ }, [skuTimelineSegments, targetLineEndOffset]);
35835
+ const hasHourlyTargetOutputProp = React144__namespace.default.useMemo(
35836
+ () => hourlyTargetOutput !== void 0,
35837
+ [hourlyTargetOutput]
35838
+ );
35839
+ const hasExplicitHourlyTargets = React144__namespace.default.useMemo(
35840
+ () => Array.isArray(hourlyTargetOutput) && hourlyTargetOutput.some((value) => value !== null && value !== void 0),
35841
+ [hourlyTargetOutput]
35842
+ );
35843
+ const hourlyTargetSegments = React144__namespace.default.useMemo(() => {
35844
+ if (!hasExplicitHourlyTargets) return [];
35845
+ const segments = [];
35846
+ let runStart = null;
35847
+ let runValue = null;
35848
+ const flush = (endIndex) => {
35849
+ if (runStart === null || runValue === null) return;
35850
+ segments.push({ start: runStart, end: endIndex, value: runValue });
35851
+ runStart = null;
35852
+ runValue = null;
35853
+ };
35854
+ for (let i = 0; i < SHIFT_DURATION; i += 1) {
35855
+ const rawValue = Array.isArray(hourlyTargetOutput) ? hourlyTargetOutput[i] : null;
35856
+ const value = rawValue === null || rawValue === void 0 ? null : Number(rawValue);
35857
+ if (value === null || !Number.isFinite(value)) {
35858
+ flush(i);
35859
+ continue;
35860
+ }
35861
+ if (runStart === null || runValue === null) {
35862
+ runStart = i;
35863
+ runValue = value;
35864
+ continue;
35865
+ }
35866
+ if (Math.abs(runValue - value) > 1e-6) {
35867
+ flush(i);
35868
+ runStart = i;
35869
+ runValue = value;
35870
+ }
35871
+ }
35872
+ flush(SHIFT_DURATION);
35873
+ return segments.filter((segment) => segment.end > segment.start);
35874
+ }, [SHIFT_DURATION, hasExplicitHourlyTargets, hourlyTargetOutput]);
35422
35875
  const activeSkuHourIndices = React144__namespace.default.useMemo(() => {
35423
35876
  const indices = /* @__PURE__ */ new Set();
35424
35877
  const targets = Array(SHIFT_DURATION).fill(pphThreshold);
35425
- if (!skuSegments || !shiftWindow) return { indices, targets };
35426
- const segmentsWithOffsets = skuSegments.map((seg) => ({
35427
- ...seg,
35428
- offset: computeSegmentOffset(seg, shiftWindow) || 0
35429
- })).sort((a, b) => a.offset - b.offset);
35878
+ if (!skuSegments || !shiftWindow) return { indices, targets, hasTimeline: false };
35879
+ const segmentsWithOffsets = skuSegments.flatMap((segment) => {
35880
+ const offset = computeSegmentOffset(segment, shiftWindow);
35881
+ if (offset === null) return [];
35882
+ return [{ ...segment, offset }];
35883
+ }).sort((a, b) => a.offset - b.offset);
35884
+ if (segmentsWithOffsets.length === 0) {
35885
+ return { indices, targets, hasTimeline: false };
35886
+ }
35430
35887
  for (let i = 0; i < SHIFT_DURATION; i++) {
35431
35888
  const midpoint = i + 0.5;
35432
35889
  let activeSeg = segmentsWithOffsets[0];
@@ -35446,13 +35903,15 @@ var HourlyOutputChartComponent = ({
35446
35903
  }
35447
35904
  }
35448
35905
  }
35449
- return { indices, targets };
35906
+ return { indices, targets, hasTimeline: true };
35450
35907
  }, [skuSegments, activeSkuId, shiftWindow, SHIFT_DURATION, pphThreshold]);
35451
35908
  const chartData = React144__namespace.default.useMemo(() => {
35452
- const { indices, targets } = activeSkuHourIndices;
35909
+ const { indices, targets, hasTimeline } = activeSkuHourIndices;
35453
35910
  return Array.from({ length: SHIFT_DURATION }, (_, i) => {
35454
35911
  const idleSlot = idleSlots[i];
35455
- const currentTarget = targets[i] || pphThreshold;
35912
+ const explicitTarget = hasHourlyTargetOutputProp ? hourlyTargetOutput?.[i] ?? null : void 0;
35913
+ const currentTarget = hasHourlyTargetOutputProp ? explicitTarget : targets[i] || pphThreshold;
35914
+ const comparisonTarget = currentTarget === null || currentTarget === void 0 ? targets[i] || pphThreshold : currentTarget;
35456
35915
  return {
35457
35916
  hourIndex: idleSlot?.hourIndex ?? i,
35458
35917
  hour: idleSlot?.hour || "",
@@ -35460,16 +35919,16 @@ var HourlyOutputChartComponent = ({
35460
35919
  output: animatedData[i] || 0,
35461
35920
  originalOutput: data[i] || 0,
35462
35921
  // Keep original data for labels
35463
- target: currentTarget,
35464
- color: (animatedData[i] || 0) >= Math.round(currentTarget) ? "#00AB45" : "#E34329",
35922
+ target: currentTarget ?? null,
35923
+ color: (animatedData[i] || 0) >= comparisonTarget ? "#00AB45" : "#E34329",
35465
35924
  idleMinutes: idleSlot?.idleMinutes || 0,
35466
35925
  idleArray: idleSlot?.idleArray || [],
35467
35926
  skuIndex: i,
35468
35927
  isHighlighted: indices.has(i),
35469
- isDimmed: !!activeSkuId && !indices.has(i)
35928
+ isDimmed: hasTimeline && !!activeSkuId && !indices.has(i)
35470
35929
  };
35471
35930
  });
35472
- }, [animatedData, data, pphThreshold, idleSlots, SHIFT_DURATION, activeSkuHourIndices, activeSkuId]);
35931
+ }, [animatedData, data, pphThreshold, idleSlots, SHIFT_DURATION, activeSkuHourIndices, activeSkuId, hourlyTargetOutput, hasHourlyTargetOutputProp]);
35473
35932
  const renderSkuTimelineRail = React144__namespace.default.useCallback((props) => {
35474
35933
  if (!skuTimelineSegments.length || SHIFT_DURATION <= 0) return null;
35475
35934
  const offset = props?.offset;
@@ -35481,8 +35940,9 @@ var HourlyOutputChartComponent = ({
35481
35940
  const railHeight = 3;
35482
35941
  const railY = top - 10;
35483
35942
  const baselineY = railY + railHeight / 2;
35484
- const labelYHigh = railY - 20;
35485
- const labelYLow = railY - 6;
35943
+ const showHoverLabel = (label, centerX, rY) => {
35944
+ setHoveredSkuRailLabel({ label, centerX, railY: rY });
35945
+ };
35486
35946
  return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
35487
35947
  /* @__PURE__ */ jsxRuntime.jsx(
35488
35948
  "line",
@@ -35500,9 +35960,43 @@ var HourlyOutputChartComponent = ({
35500
35960
  const xStart = left + segment.start / SHIFT_DURATION * width;
35501
35961
  const xEnd = left + segment.end / SHIFT_DURATION * width;
35502
35962
  const segmentWidth = Math.max(1, xEnd - xStart);
35963
+ const labelPadding = segmentWidth < 48 ? 4 : 8;
35964
+ const inlineLabelText = formatSkuRailLabel(segment.label, segmentWidth, {
35965
+ horizontalPadding: labelPadding,
35966
+ cssTruncation: true
35967
+ });
35968
+ const badgeLabelText = formatSkuRailLabel(segment.label, 28, {
35969
+ horizontalPadding: 4,
35970
+ cssTruncation: true
35971
+ });
35972
+ const isMicroSegment = segmentWidth < 18;
35973
+ const labelText = isMicroSegment ? badgeLabelText : inlineLabelText;
35974
+ const labelClipWidth = Math.max(segmentWidth - labelPadding * 2, 0);
35503
35975
  const isActive = !!activeSkuId && segment.skuId === activeSkuId;
35504
35976
  const isDimmed = !!activeSkuId && segment.skuId !== activeSkuId;
35977
+ const hoverHitWidth = Math.max(segmentWidth + 12, isMicroSegment ? 20 : segmentWidth);
35978
+ const hoverHitX = Math.max(
35979
+ left,
35980
+ Math.min(xStart - (hoverHitWidth - segmentWidth) / 2, left + width - hoverHitWidth)
35981
+ );
35982
+ const hoverHitHeight = isMicroSegment ? 34 : 28;
35983
+ const hoverHitY = railY - (isMicroSegment ? 30 : 24);
35984
+ const microBadgeWidth = labelText ? Math.max(labelText.length * 6.2 + 10, 18) : 0;
35985
+ const microBadgeX = xStart + segmentWidth / 2 - microBadgeWidth / 2;
35986
+ const microBadgeY = railY - 24;
35505
35987
  return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
35988
+ /* @__PURE__ */ jsxRuntime.jsx(
35989
+ "rect",
35990
+ {
35991
+ x: hoverHitX,
35992
+ y: hoverHitY,
35993
+ width: hoverHitWidth,
35994
+ height: hoverHitHeight,
35995
+ fill: "transparent",
35996
+ onMouseEnter: () => showHoverLabel(segment.label, xStart + segmentWidth / 2, railY),
35997
+ onMouseLeave: () => setHoveredSkuRailLabel((current) => current?.label === segment.label ? null : current)
35998
+ }
35999
+ ),
35506
36000
  /* @__PURE__ */ jsxRuntime.jsx(
35507
36001
  "rect",
35508
36002
  {
@@ -35513,7 +36007,7 @@ var HourlyOutputChartComponent = ({
35513
36007
  rx: 1.5,
35514
36008
  fill: isActive ? "#3b82f6" : "#cbd5e1",
35515
36009
  opacity: isDimmed ? 0.3 : 1,
35516
- style: { transition: "all 0.3s ease" }
36010
+ style: { transition: "all 0.3s ease", pointerEvents: "none" }
35517
36011
  }
35518
36012
  ),
35519
36013
  index > 0 && /* @__PURE__ */ jsxRuntime.jsx(
@@ -35525,22 +36019,59 @@ var HourlyOutputChartComponent = ({
35525
36019
  y2: railY + railHeight + 3,
35526
36020
  stroke: "#94a3b8",
35527
36021
  strokeWidth: 1,
35528
- opacity: 0.6
36022
+ opacity: 0.6,
36023
+ style: { pointerEvents: "none" }
35529
36024
  }
35530
36025
  ),
35531
- segmentWidth >= 36 && /* @__PURE__ */ jsxRuntime.jsx(
35532
- "text",
36026
+ isMicroSegment && labelText && /* @__PURE__ */ jsxRuntime.jsxs("g", { style: { pointerEvents: "none" }, children: [
36027
+ /* @__PURE__ */ jsxRuntime.jsx(
36028
+ "rect",
36029
+ {
36030
+ x: microBadgeX,
36031
+ y: microBadgeY,
36032
+ width: microBadgeWidth,
36033
+ height: 14,
36034
+ rx: 7,
36035
+ fill: "white",
36036
+ stroke: isActive ? "#3b82f6" : "#cbd5e1",
36037
+ strokeWidth: 1,
36038
+ opacity: isDimmed ? 0.55 : 0.98
36039
+ }
36040
+ ),
36041
+ /* @__PURE__ */ jsxRuntime.jsx(
36042
+ "text",
36043
+ {
36044
+ x: xStart + segmentWidth / 2,
36045
+ y: microBadgeY + 10,
36046
+ textAnchor: "middle",
36047
+ fontSize: 8,
36048
+ fontWeight: 700,
36049
+ fill: isActive ? "#2563eb" : "#64748b",
36050
+ opacity: isDimmed ? 0.55 : 1,
36051
+ style: { transition: "all 0.3s ease" },
36052
+ children: labelText
36053
+ }
36054
+ )
36055
+ ] }),
36056
+ !isMicroSegment && labelText && labelClipWidth > 0 && /* @__PURE__ */ jsxRuntime.jsx(
36057
+ "foreignObject",
35533
36058
  {
35534
- x: xStart + segmentWidth / 2,
35535
- y: index % 2 === 0 ? labelYHigh : labelYLow,
35536
- textAnchor: "middle",
35537
- fontSize: 10,
35538
- fontWeight: 700,
35539
- letterSpacing: "0.02em",
35540
- fill: isActive ? "#2563eb" : "#64748b",
35541
- opacity: isDimmed ? 0.4 : 1,
35542
- style: { transition: "all 0.3s ease", pointerEvents: "none" },
35543
- children: segment.label
36059
+ x: xStart + labelPadding,
36060
+ y: railY - 24,
36061
+ width: labelClipWidth,
36062
+ height: 18,
36063
+ style: { pointerEvents: "none", overflow: "visible" },
36064
+ children: /* @__PURE__ */ jsxRuntime.jsx(
36065
+ "div",
36066
+ {
36067
+ 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"}`,
36068
+ style: {
36069
+ opacity: isDimmed ? 0.4 : 1,
36070
+ transition: "all 0.3s ease"
36071
+ },
36072
+ children: labelText
36073
+ }
36074
+ )
35544
36075
  }
35545
36076
  )
35546
36077
  ] }, `sku-rail-${segment.skuId}-${segment.start}-${index}`);
@@ -35598,14 +36129,17 @@ var HourlyOutputChartComponent = ({
35598
36129
  );
35599
36130
  }, [idleBarState.visible, idleBarState.key, idleBarState.shouldAnimate]);
35600
36131
  const maxDataValue = Math.max(...data, 0);
35601
- const maxTargetValue = Math.max(...chartData.map((d) => d.target), pphThreshold, 0);
36132
+ const numericChartTargets = chartData.map((d) => d.target).filter((target) => target !== null && Number.isFinite(target));
36133
+ const maxTargetValue = Math.max(...numericChartTargets, pphThreshold, 0);
35602
36134
  const maxYValue = Math.max(
35603
36135
  Math.ceil(maxTargetValue * 1.5),
35604
36136
  Math.ceil(maxDataValue * 1.15)
35605
36137
  // Add 15% headroom above max value
35606
36138
  );
35607
36139
  const generateYAxisTicks = () => {
35608
- const uniqueTargets = [...new Set(chartData.map((d) => Math.round(d.target)))].sort((a, b) => a - b);
36140
+ const uniqueTargets = [...new Set(
36141
+ chartData.map((d) => d.target).filter((target) => target !== null && Number.isFinite(target)).map((target) => Math.round(target))
36142
+ )].sort((a, b) => a - b);
35609
36143
  const rawTicks = [0];
35610
36144
  uniqueTargets.forEach((target) => {
35611
36145
  if (target > 0) {
@@ -35640,14 +36174,54 @@ var HourlyOutputChartComponent = ({
35640
36174
  };
35641
36175
  const renderTargetLine = React144__namespace.default.useCallback((props) => {
35642
36176
  const { offset, yAxisMap } = props;
35643
- if (!offset || !yAxisMap || SHIFT_DURATION <= 0) return null;
36177
+ if (!offset || !yAxisMap || SHIFT_DURATION <= 0 || targetLineEndOffset <= 0) return null;
35644
36178
  const { left, width } = offset;
35645
36179
  const yAxis = yAxisMap["default"] || yAxisMap[0];
35646
36180
  if (!yAxis || !yAxis.scale) return null;
35647
36181
  const lines = [];
35648
36182
  const offsetToX = (o) => left + o / SHIFT_DURATION * width;
35649
- if (skuTimelineSegments.length > 0) {
35650
- skuTimelineSegments.forEach((segment, index) => {
36183
+ if (hasHourlyTargetOutputProp && hourlyTargetSegments.length > 0) {
36184
+ hourlyTargetSegments.forEach((segment, index) => {
36185
+ const y = yAxis.scale(segment.value);
36186
+ const xStart = offsetToX(segment.start);
36187
+ const xEnd = offsetToX(segment.end);
36188
+ lines.push(
36189
+ /* @__PURE__ */ jsxRuntime.jsx(
36190
+ "line",
36191
+ {
36192
+ x1: xStart,
36193
+ y1: y,
36194
+ x2: xEnd,
36195
+ y2: y,
36196
+ stroke: "#E34329",
36197
+ strokeDasharray: "3 3",
36198
+ strokeWidth: 2
36199
+ },
36200
+ `target-hourly-h-${index}`
36201
+ )
36202
+ );
36203
+ const next = hourlyTargetSegments[index + 1];
36204
+ if (next && Math.abs(next.value - segment.value) > 1e-6) {
36205
+ const nextY = yAxis.scale(next.value);
36206
+ lines.push(
36207
+ /* @__PURE__ */ jsxRuntime.jsx(
36208
+ "line",
36209
+ {
36210
+ x1: xEnd,
36211
+ y1: y,
36212
+ x2: xEnd,
36213
+ y2: nextY,
36214
+ stroke: "#E34329",
36215
+ strokeDasharray: "3 3",
36216
+ strokeWidth: 2
36217
+ },
36218
+ `target-hourly-v-${index}`
36219
+ )
36220
+ );
36221
+ }
36222
+ });
36223
+ } else if (!hasHourlyTargetOutputProp && targetTimelineSegments.length > 0) {
36224
+ targetTimelineSegments.forEach((segment, index) => {
35651
36225
  const target = segment.pphThreshold || pphThreshold;
35652
36226
  const y = yAxis.scale(target);
35653
36227
  const xStart = offsetToX(segment.start);
@@ -35667,7 +36241,7 @@ var HourlyOutputChartComponent = ({
35667
36241
  `target-h-${index}`
35668
36242
  )
35669
36243
  );
35670
- const next = skuTimelineSegments[index + 1];
36244
+ const next = targetTimelineSegments[index + 1];
35671
36245
  if (next) {
35672
36246
  const nextTarget = next.pphThreshold || pphThreshold;
35673
36247
  if (nextTarget !== target) {
@@ -35690,7 +36264,7 @@ var HourlyOutputChartComponent = ({
35690
36264
  }
35691
36265
  }
35692
36266
  });
35693
- } else {
36267
+ } else if (!hasHourlyTargetOutputProp) {
35694
36268
  const y = yAxis.scale(pphThreshold);
35695
36269
  lines.push(
35696
36270
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -35709,10 +36283,13 @@ var HourlyOutputChartComponent = ({
35709
36283
  );
35710
36284
  }
35711
36285
  return /* @__PURE__ */ jsxRuntime.jsx("g", { children: lines });
35712
- }, [skuTimelineSegments, SHIFT_DURATION, pphThreshold]);
36286
+ }, [hourlyTargetSegments, targetTimelineSegments, SHIFT_DURATION, pphThreshold, targetLineEndOffset, hasHourlyTargetOutputProp]);
35713
36287
  const renderLegend = () => {
35714
- const uniqueTargets = [...new Set(chartData.map((d) => Math.round(d.target)))].sort((a, b) => a - b);
35715
- const targetText = uniqueTargets.length === 1 ? `Target: ${uniqueTargets[0]} units/hr` : `Target: ${uniqueTargets[0]} - ${uniqueTargets[uniqueTargets.length - 1]} units/hr`;
36288
+ const uniqueTargets = [...new Set(
36289
+ chartData.map((d) => d.target).filter((target) => target !== null && Number.isFinite(target)).map((target) => Math.round(target))
36290
+ )].sort((a, b) => a - b);
36291
+ const unitLabel = hasHourlyTargetOutputProp ? "units" : "units/hr";
36292
+ const targetText = uniqueTargets.length === 0 ? `Target` : uniqueTargets.length === 1 ? `Target: ${uniqueTargets[0]} ${unitLabel}` : `Target: ${uniqueTargets[0]} - ${uniqueTargets[uniqueTargets.length - 1]} ${unitLabel}`;
35716
36293
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center text-sm text-gray-600 bg-white py-1", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 border border-gray-100 rounded-full px-3 py-1", children: [
35717
36294
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-8 flex items-center", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full border-t-2 border-[#E34329] border-dashed" }) }),
35718
36295
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: targetText })
@@ -35725,254 +36302,274 @@ var HourlyOutputChartComponent = ({
35725
36302
  className: `w-full h-full min-w-0 flex flex-col ${className}`,
35726
36303
  style: { minHeight: "200px", minWidth: 0 },
35727
36304
  children: [
35728
- containerReady ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-0", children: /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(
35729
- recharts.BarChart,
35730
- {
35731
- data: chartData,
35732
- margin: {
35733
- // Reserve headroom for the SKU timeline rail + staggered
35734
- // labels only when SKU segments are rendered. Non-SKU charts
35735
- // keep the original 10px top so recharts has enough vertical
35736
- // space to show the target (pph) tick label on the Y-axis.
35737
- top: skuTimelineSegments.length > 0 ? 40 : 10,
35738
- right: 10,
35739
- bottom: 10,
35740
- left: 6
35741
- },
35742
- barCategoryGap: "25%",
35743
- children: [
35744
- /* @__PURE__ */ jsxRuntime.jsx(recharts.CartesianGrid, { strokeDasharray: "3 3", vertical: false }),
35745
- /* @__PURE__ */ jsxRuntime.jsx(
35746
- recharts.XAxis,
35747
- {
35748
- xAxisId: "default",
35749
- dataKey: "hour",
35750
- tick: { fontSize: xAxisConfig.tickFont },
35751
- interval: xAxisConfig.interval,
35752
- angle: xAxisConfig.angle,
35753
- textAnchor: "end",
35754
- tickMargin: xAxisConfig.tickMargin,
35755
- height: xAxisConfig.height
35756
- }
35757
- ),
35758
- /* @__PURE__ */ jsxRuntime.jsx(
35759
- recharts.XAxis,
35760
- {
35761
- xAxisId: "sku",
35762
- type: "number",
35763
- dataKey: "skuIndex",
35764
- domain: [0, Math.max(SHIFT_DURATION, 0)],
35765
- hide: true,
35766
- allowDataOverflow: true
35767
- }
35768
- ),
35769
- /* @__PURE__ */ jsxRuntime.jsx(
35770
- recharts.YAxis,
35771
- {
35772
- yAxisId: "default",
35773
- tickMargin: 8,
35774
- width: 48,
35775
- domain: [0, maxYValue],
35776
- ticks: generateYAxisTicks(),
35777
- tickFormatter: (value) => value,
35778
- tick: (props) => {
35779
- const { x, y, payload } = props;
35780
- return /* @__PURE__ */ jsxRuntime.jsx("g", { transform: `translate(${x},${y})`, children: /* @__PURE__ */ jsxRuntime.jsx(
35781
- "text",
35782
- {
35783
- x: -2,
35784
- y: 0,
35785
- dy: 4,
35786
- textAnchor: "end",
35787
- fill: "#666",
35788
- fontSize: 12,
35789
- children: payload.value
35790
- },
35791
- `tick-${payload.value}-${x}-${y}`
35792
- ) });
36305
+ containerReady ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-h-0 relative", children: [
36306
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(
36307
+ recharts.BarChart,
36308
+ {
36309
+ data: chartData,
36310
+ margin: {
36311
+ // Reserve headroom for the SKU timeline rail + staggered
36312
+ // labels only when SKU segments are rendered. Non-SKU charts
36313
+ // keep the original 10px top so recharts has enough vertical
36314
+ // space to show the target (pph) tick label on the Y-axis.
36315
+ top: skuTimelineSegments.length > 0 ? 40 : 10,
36316
+ right: 10,
36317
+ bottom: 10,
36318
+ left: 6
36319
+ },
36320
+ barCategoryGap: "25%",
36321
+ children: [
36322
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.CartesianGrid, { strokeDasharray: "3 3", vertical: false }),
36323
+ /* @__PURE__ */ jsxRuntime.jsx(
36324
+ recharts.XAxis,
36325
+ {
36326
+ xAxisId: "default",
36327
+ dataKey: "hour",
36328
+ tick: { fontSize: xAxisConfig.tickFont },
36329
+ interval: xAxisConfig.interval,
36330
+ angle: xAxisConfig.angle,
36331
+ textAnchor: "end",
36332
+ tickMargin: xAxisConfig.tickMargin,
36333
+ height: xAxisConfig.height
35793
36334
  }
35794
- }
35795
- ),
35796
- /* @__PURE__ */ jsxRuntime.jsx(recharts.YAxis, { yAxisId: "idle", domain: [0, 60], hide: true }),
35797
- /* @__PURE__ */ jsxRuntime.jsx(
35798
- recharts.Tooltip,
35799
- {
35800
- cursor: false,
35801
- contentStyle: {
35802
- backgroundColor: "white",
35803
- border: "none",
35804
- borderRadius: "12px",
35805
- boxShadow: "0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)",
35806
- padding: "0",
35807
- fontSize: "14px"
35808
- },
35809
- content: (props) => {
35810
- if (!props.active || !props.payload || props.payload.length === 0)
35811
- return null;
35812
- const data2 = props.payload[0].payload;
35813
- const idlePeriods = showIdleTime ? getHourlyIdlePeriods({
35814
- idleArray: data2.idleArray,
35815
- shiftStart,
35816
- hourIndex: Number.isFinite(data2.hourIndex) ? data2.hourIndex : 0
35817
- }) : [];
35818
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-xl shadow-xl border border-gray-100 p-4 min-w-[220px]", children: [
35819
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between mb-3", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-semibold text-gray-900 text-sm", children: data2.timeRange }) }),
35820
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
35821
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
35822
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-gray-500", children: "Output" }),
35823
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-semibold text-gray-900 text-sm", children: [
35824
- Math.round(data2.output),
35825
- " units"
35826
- ] })
35827
- ] }),
35828
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
35829
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-gray-500", children: "Target" }),
35830
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-semibold text-gray-600 text-sm", children: [
35831
- Math.round(data2.target),
35832
- " units"
35833
- ] })
35834
- ] }),
35835
- showIdleTime && data2.idleMinutes > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
35836
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t border-gray-100 pt-2", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
35837
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-gray-500", children: "Idle Time" }),
35838
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-semibold text-orange-600 text-sm", children: [
35839
- data2.idleMinutes,
35840
- " minutes"
36335
+ ),
36336
+ /* @__PURE__ */ jsxRuntime.jsx(
36337
+ recharts.XAxis,
36338
+ {
36339
+ xAxisId: "sku",
36340
+ type: "number",
36341
+ dataKey: "skuIndex",
36342
+ domain: [0, Math.max(SHIFT_DURATION, 0)],
36343
+ hide: true,
36344
+ allowDataOverflow: true
36345
+ }
36346
+ ),
36347
+ /* @__PURE__ */ jsxRuntime.jsx(
36348
+ recharts.YAxis,
36349
+ {
36350
+ yAxisId: "default",
36351
+ tickMargin: 8,
36352
+ width: 48,
36353
+ domain: [0, maxYValue],
36354
+ ticks: generateYAxisTicks(),
36355
+ tickFormatter: (value) => value,
36356
+ tick: (props) => {
36357
+ const { x, y, payload } = props;
36358
+ return /* @__PURE__ */ jsxRuntime.jsx("g", { transform: `translate(${x},${y})`, children: /* @__PURE__ */ jsxRuntime.jsx(
36359
+ "text",
36360
+ {
36361
+ x: -2,
36362
+ y: 0,
36363
+ dy: 4,
36364
+ textAnchor: "end",
36365
+ fill: "#666",
36366
+ fontSize: 12,
36367
+ children: payload.value
36368
+ },
36369
+ `tick-${payload.value}-${x}-${y}`
36370
+ ) });
36371
+ }
36372
+ }
36373
+ ),
36374
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.YAxis, { yAxisId: "idle", domain: [0, 60], hide: true }),
36375
+ /* @__PURE__ */ jsxRuntime.jsx(
36376
+ recharts.Tooltip,
36377
+ {
36378
+ cursor: { fill: "#f1f5f9" },
36379
+ contentStyle: { backgroundColor: "transparent", border: "none", padding: 0 },
36380
+ content: (props) => {
36381
+ if (!props.active || !props.payload || props.payload.length === 0)
36382
+ return null;
36383
+ const data2 = props.payload[0].payload;
36384
+ const idlePeriods = showIdleTime ? getHourlyIdlePeriods({
36385
+ idleArray: data2.idleArray,
36386
+ shiftStart,
36387
+ hourIndex: Number.isFinite(data2.hourIndex) ? data2.hourIndex : 0
36388
+ }) : [];
36389
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white/95 backdrop-blur-md border border-slate-200/60 shadow-xl shadow-slate-200/40 rounded-xl p-4 min-w-[240px] text-slate-700", children: [
36390
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between mb-4 pb-3 border-b border-slate-100", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-semibold text-slate-900 text-sm tracking-tight", children: data2.timeRange }) }),
36391
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
36392
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
36393
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-slate-500 font-medium tracking-wide", children: "Output" }),
36394
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-bold text-slate-900 text-sm", children: [
36395
+ Math.round(data2.output),
36396
+ " ",
36397
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-slate-400 font-normal text-xs ml-0.5", children: "units" })
36398
+ ] })
36399
+ ] }),
36400
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
36401
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-slate-500 font-medium tracking-wide", children: "Target" }),
36402
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-bold text-slate-700 text-sm", children: [
36403
+ Math.round(data2.target),
36404
+ " ",
36405
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-slate-400 font-normal text-xs ml-0.5", children: "units" })
36406
+ ] })
36407
+ ] }),
36408
+ showIdleTime && data2.idleMinutes > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
36409
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pt-3 mt-3 border-t border-slate-100", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
36410
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-slate-500 font-medium tracking-wide", children: "Idle Time" }),
36411
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-bold text-orange-600 text-sm flex items-center gap-1.5", children: [
36412
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-1.5 h-1.5 rounded-full bg-orange-500 shadow-[0_0_6px_rgba(249,115,22,0.6)] animate-pulse" }),
36413
+ data2.idleMinutes,
36414
+ " ",
36415
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-orange-500/70 font-normal text-xs ml-0.5", children: "min" })
36416
+ ] })
36417
+ ] }) }),
36418
+ idlePeriods.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3 bg-slate-50/80 rounded-lg p-3 border border-slate-100/50", children: [
36419
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-semibold text-slate-400 text-[10px] mb-2.5 uppercase tracking-wider", children: "Idle Periods" }),
36420
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2.5 max-h-32 overflow-y-auto pr-1 custom-scrollbar", children: idlePeriods.map((period, index) => {
36421
+ return /* @__PURE__ */ jsxRuntime.jsxs(
36422
+ "div",
36423
+ {
36424
+ className: "flex items-start gap-2.5 text-xs",
36425
+ children: [
36426
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-[5px] w-1.5 h-1.5 bg-orange-400 rounded-full flex-shrink-0 shadow-[0_0_4px_rgba(251,146,60,0.5)]" }),
36427
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-slate-700 font-medium tracking-tight", children: period.duration === 1 ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
36428
+ period.startTime,
36429
+ " ",
36430
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-slate-400 font-normal ml-1", children: [
36431
+ "(",
36432
+ period.duration,
36433
+ "m)"
36434
+ ] })
36435
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
36436
+ period.startTime,
36437
+ " ",
36438
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-slate-400 mx-0.5", children: "\u2192" }),
36439
+ " ",
36440
+ period.endTime,
36441
+ " ",
36442
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-slate-400 font-normal ml-1", children: [
36443
+ "(",
36444
+ period.duration,
36445
+ "m)"
36446
+ ] })
36447
+ ] }) })
36448
+ ]
36449
+ },
36450
+ index
36451
+ );
36452
+ }) })
35841
36453
  ] })
35842
- ] }) }),
35843
- idlePeriods.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3 bg-gray-50 rounded-lg p-2.5", children: [
35844
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-medium text-gray-700 text-xs mb-2", children: "Idle periods:" }),
35845
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-1 max-h-32 overflow-y-auto pr-1", children: idlePeriods.map((period, index) => {
35846
- return /* @__PURE__ */ jsxRuntime.jsxs(
35847
- "div",
35848
- {
35849
- className: "text-gray-600 flex items-center gap-2 text-xs",
35850
- children: [
35851
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block w-1.5 h-1.5 bg-orange-400 rounded-full flex-shrink-0" }),
35852
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: period.duration === 1 ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
35853
- period.startTime,
35854
- " (",
35855
- period.duration,
35856
- " min)"
35857
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
35858
- period.startTime,
35859
- " -",
35860
- " ",
35861
- period.endTime,
35862
- " (",
35863
- period.duration,
35864
- " mins)"
35865
- ] }) })
35866
- ]
35867
- },
35868
- index
35869
- );
35870
- }) })
35871
36454
  ] })
35872
36455
  ] })
35873
- ] })
35874
- ] });
35875
- },
35876
- animationDuration: 200
35877
- }
35878
- ),
35879
- /* @__PURE__ */ jsxRuntime.jsx(recharts.Customized, { component: renderTargetLine }),
35880
- /* @__PURE__ */ jsxRuntime.jsx(recharts.Customized, { component: renderSkuTimelineRail }),
35881
- /* @__PURE__ */ jsxRuntime.jsxs(
35882
- recharts.Bar,
35883
- {
35884
- xAxisId: "default",
35885
- dataKey: "output",
35886
- yAxisId: "default",
35887
- maxBarSize: 35,
35888
- radius: [10, 10, 0, 0],
35889
- isAnimationActive: false,
35890
- children: [
35891
- chartData.map((entry, index) => /* @__PURE__ */ jsxRuntime.jsx(
35892
- recharts.Cell,
35893
- {
35894
- fill: entry.color,
35895
- stroke: "transparent",
35896
- strokeWidth: 0,
35897
- style: {
35898
- filter: entry.isHighlighted ? "drop-shadow(0 4px 6px rgba(0, 0, 0, 0.12)) brightness(1.05)" : "brightness(1)",
35899
- transform: entry.isHighlighted ? "translateY(-4px)" : "translateY(0)",
35900
- opacity: entry.isDimmed ? 0.4 : 1,
35901
- transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)",
35902
- cursor: "pointer"
35903
- },
35904
- onMouseEnter: (e) => {
35905
- const target = e.target;
35906
- target.style.filter = "drop-shadow(0 4px 6px rgba(0, 0, 0, 0.12)) brightness(1.05)";
35907
- target.style.transform = "translateY(-4px)";
35908
- target.style.opacity = "1";
36456
+ ] });
36457
+ },
36458
+ animationDuration: 200
36459
+ }
36460
+ ),
36461
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.Customized, { component: renderTargetLine }),
36462
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.Customized, { component: renderSkuTimelineRail }),
36463
+ /* @__PURE__ */ jsxRuntime.jsxs(
36464
+ recharts.Bar,
36465
+ {
36466
+ xAxisId: "default",
36467
+ dataKey: "output",
36468
+ yAxisId: "default",
36469
+ maxBarSize: 35,
36470
+ radius: [10, 10, 0, 0],
36471
+ isAnimationActive: false,
36472
+ children: [
36473
+ chartData.map((entry, index) => /* @__PURE__ */ jsxRuntime.jsx(
36474
+ recharts.Cell,
36475
+ {
36476
+ fill: entry.color,
36477
+ stroke: "transparent",
36478
+ strokeWidth: 0,
36479
+ style: {
36480
+ filter: entry.isHighlighted ? "drop-shadow(0 4px 6px rgba(0, 0, 0, 0.12)) brightness(1.05)" : "brightness(1)",
36481
+ transform: entry.isHighlighted ? "translateY(-4px)" : "translateY(0)",
36482
+ opacity: entry.isDimmed ? 0.4 : 1,
36483
+ transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)",
36484
+ cursor: "pointer"
36485
+ },
36486
+ onMouseEnter: (e) => {
36487
+ const target = e.target;
36488
+ target.style.filter = "drop-shadow(0 4px 6px rgba(0, 0, 0, 0.12)) brightness(1.05)";
36489
+ target.style.transform = "translateY(-4px)";
36490
+ target.style.opacity = "1";
36491
+ },
36492
+ onMouseLeave: (e) => {
36493
+ const target = e.target;
36494
+ target.style.filter = entry.isHighlighted ? "drop-shadow(0 4px 6px rgba(0, 0, 0, 0.12)) brightness(1.05)" : "brightness(1)";
36495
+ target.style.transform = entry.isHighlighted ? "translateY(-4px)" : "translateY(0)";
36496
+ target.style.opacity = entry.isDimmed ? "0.4" : "1";
36497
+ }
35909
36498
  },
35910
- onMouseLeave: (e) => {
35911
- const target = e.target;
35912
- target.style.filter = entry.isHighlighted ? "drop-shadow(0 4px 6px rgba(0, 0, 0, 0.12)) brightness(1.05)" : "brightness(1)";
35913
- target.style.transform = entry.isHighlighted ? "translateY(-4px)" : "translateY(0)";
35914
- target.style.opacity = entry.isDimmed ? "0.4" : "1";
36499
+ `cell-${index}`
36500
+ )),
36501
+ /* @__PURE__ */ jsxRuntime.jsx(
36502
+ recharts.LabelList,
36503
+ {
36504
+ dataKey: "originalOutput",
36505
+ position: "top",
36506
+ content: (props) => {
36507
+ const { x, y, width, value, payload } = props;
36508
+ const actualValue = payload?.originalOutput || value;
36509
+ if (!actualValue || actualValue === 0) return null;
36510
+ return /* @__PURE__ */ jsxRuntime.jsx(
36511
+ "text",
36512
+ {
36513
+ x: x + width / 2,
36514
+ y: y - 8,
36515
+ textAnchor: "middle",
36516
+ fontSize: "12",
36517
+ fontWeight: "600",
36518
+ fill: "#374151",
36519
+ style: {
36520
+ opacity: 1,
36521
+ pointerEvents: "none",
36522
+ transition: "none"
36523
+ },
36524
+ children: Math.round(actualValue)
36525
+ }
36526
+ );
36527
+ }
35915
36528
  }
35916
- },
35917
- `cell-${index}`
35918
- )),
35919
- /* @__PURE__ */ jsxRuntime.jsx(
35920
- recharts.LabelList,
35921
- {
35922
- dataKey: "originalOutput",
35923
- position: "top",
35924
- content: (props) => {
35925
- const { x, y, width, value, payload } = props;
35926
- const actualValue = payload?.originalOutput || value;
35927
- if (!actualValue || actualValue === 0) return null;
35928
- return /* @__PURE__ */ jsxRuntime.jsx(
35929
- "text",
35930
- {
35931
- x: x + width / 2,
35932
- y: y - 8,
35933
- textAnchor: "middle",
35934
- fontSize: "12",
35935
- fontWeight: "600",
35936
- fill: "#374151",
35937
- style: {
35938
- opacity: 1,
35939
- pointerEvents: "none",
35940
- transition: "none"
35941
- },
35942
- children: Math.round(actualValue)
35943
- }
35944
- );
36529
+ )
36530
+ ]
36531
+ }
36532
+ ),
36533
+ IdleBar,
36534
+ /* @__PURE__ */ jsxRuntime.jsx("defs", { children: /* @__PURE__ */ jsxRuntime.jsxs(
36535
+ "pattern",
36536
+ {
36537
+ id: "idlePattern",
36538
+ patternUnits: "userSpaceOnUse",
36539
+ width: "4",
36540
+ height: "4",
36541
+ children: [
36542
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { width: "4", height: "4", fill: "#4b5563", opacity: "0.6" }),
36543
+ /* @__PURE__ */ jsxRuntime.jsx(
36544
+ "path",
36545
+ {
36546
+ d: "M 0,4 l 4,-4 M -1,1 l 2,-2 M 3,5 l 2,-2",
36547
+ stroke: "#374151",
36548
+ strokeWidth: "0.8",
36549
+ opacity: "0.8"
35945
36550
  }
35946
- }
35947
- )
35948
- ]
35949
- }
35950
- ),
35951
- IdleBar,
35952
- /* @__PURE__ */ jsxRuntime.jsx("defs", { children: /* @__PURE__ */ jsxRuntime.jsxs(
35953
- "pattern",
35954
- {
35955
- id: "idlePattern",
35956
- patternUnits: "userSpaceOnUse",
35957
- width: "4",
35958
- height: "4",
35959
- children: [
35960
- /* @__PURE__ */ jsxRuntime.jsx("rect", { width: "4", height: "4", fill: "#4b5563", opacity: "0.6" }),
35961
- /* @__PURE__ */ jsxRuntime.jsx(
35962
- "path",
35963
- {
35964
- d: "M 0,4 l 4,-4 M -1,1 l 2,-2 M 3,5 l 2,-2",
35965
- stroke: "#374151",
35966
- strokeWidth: "0.8",
35967
- opacity: "0.8"
35968
- }
35969
- )
35970
- ]
35971
- }
35972
- ) })
35973
- ]
35974
- }
35975
- ) }) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full flex items-center justify-center bg-gray-50 rounded-lg", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-500 text-sm", children: "Loading chart..." }) }),
36551
+ )
36552
+ ]
36553
+ }
36554
+ ) })
36555
+ ]
36556
+ }
36557
+ ) }),
36558
+ hoveredSkuRailLabel && /* @__PURE__ */ jsxRuntime.jsx(
36559
+ "div",
36560
+ {
36561
+ className: "absolute z-50 pointer-events-none transform -translate-x-1/2 -translate-y-full transition-opacity duration-200",
36562
+ style: {
36563
+ left: hoveredSkuRailLabel.centerX,
36564
+ top: hoveredSkuRailLabel.railY - 12
36565
+ },
36566
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white/95 backdrop-blur-md border border-slate-200/80 shadow-xl shadow-slate-200/50 rounded-lg px-3 py-2 flex flex-col min-w-[100px] max-w-[280px]", children: [
36567
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-bold tracking-wider text-blue-500 uppercase leading-none mb-1", children: "SKU" }),
36568
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[13px] font-semibold text-slate-800 whitespace-normal break-words leading-snug", children: hoveredSkuRailLabel.label })
36569
+ ] })
36570
+ }
36571
+ )
36572
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full flex items-center justify-center bg-gray-50 rounded-lg", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-500 text-sm", children: "Loading chart..." }) }),
35976
36573
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-none pt-2", children: renderLegend() })
35977
36574
  ]
35978
36575
  }
@@ -35990,6 +36587,16 @@ var HourlyOutputChart = React144__namespace.default.memo(
35990
36587
  if (!prevProps.data.every((val, idx) => val === nextProps.data[idx])) {
35991
36588
  return false;
35992
36589
  }
36590
+ const prevHourlyTargets = prevProps.hourlyTargetOutput || [];
36591
+ const nextHourlyTargets = nextProps.hourlyTargetOutput || [];
36592
+ if (prevHourlyTargets.length !== nextHourlyTargets.length) {
36593
+ return false;
36594
+ }
36595
+ for (let i = 0; i < prevHourlyTargets.length; i += 1) {
36596
+ if (prevHourlyTargets[i] !== nextHourlyTargets[i]) {
36597
+ return false;
36598
+ }
36599
+ }
35993
36600
  const prevIdle = prevProps.idleTimeHourly || {};
35994
36601
  const nextIdle = nextProps.idleTimeHourly || {};
35995
36602
  const prevKeys = Object.keys(prevIdle);
@@ -36177,7 +36784,6 @@ var VideoCard = React144__namespace.default.memo(({
36177
36784
  shouldPlay,
36178
36785
  onClick,
36179
36786
  onFatalError,
36180
- isVeryLowEfficiency = false,
36181
36787
  legend,
36182
36788
  cropping,
36183
36789
  canvasFps = 30,
@@ -36249,11 +36855,6 @@ var VideoCard = React144__namespace.default.memo(({
36249
36855
  }
36250
36856
  },
36251
36857
  children: [
36252
- isVeryLowEfficiency && /* @__PURE__ */ jsxRuntime.jsx("div", { className: `absolute ${compact ? "top-0.5 left-1" : "top-1 left-2"} z-30`, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
36253
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute -inset-1 bg-red-400/50 rounded-full blur-sm animate-pulse" }),
36254
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute -inset-0.5 bg-red-500/30 rounded-full blur-md animate-ping [animation-duration:1.5s]" }),
36255
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: `bg-[#E34329] ${compact ? "w-4 h-4" : "w-5 h-5"} rounded-full flex items-center justify-center text-white font-bold ${compact ? "text-[10px]" : "text-xs"} shadow-lg ring-2 ring-red-400/40 border border-red-400/80 animate-pulse`, children: "!" })
36256
- ] }) }),
36257
36858
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full h-full overflow-hidden bg-black", children: [
36258
36859
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-black z-0", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "animate-pulse flex flex-col items-center", children: [
36259
36860
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Camera, { className: `w-5 h-5 sm:${compact ? "w-4 h-4" : "w-6 h-6"} text-gray-500` }),
@@ -36663,7 +37264,6 @@ var VideoGridView = React144__namespace.default.memo(({
36663
37264
  const workspaceId = workspace.workspace_uuid || workspace.workspace_name;
36664
37265
  const workspaceKey = `${workspace.line_id || "unknown"}-${workspaceId}`;
36665
37266
  const isVisible = visibleWorkspaces.has(workspaceId);
36666
- const isVeryLowEfficiency = workspace.show_exclamation ?? (workspace.efficiency < 50 && workspace.efficiency >= 10);
36667
37267
  const workspaceCropping = getWorkspaceCropping(workspaceId, workspace.workspace_name);
36668
37268
  const workspaceStream = videoStreamsByWorkspaceId?.[workspaceId];
36669
37269
  const lastSeenLabel = workspace.workspace_uuid ? lastSeenByWorkspaceId[workspace.workspace_uuid]?.timeSinceLastUpdate : void 0;
@@ -36680,7 +37280,6 @@ var VideoGridView = React144__namespace.default.memo(({
36680
37280
  workspaceId,
36681
37281
  workspaceKey,
36682
37282
  isVisible,
36683
- isVeryLowEfficiency,
36684
37283
  workspaceCropping,
36685
37284
  fallbackUrl,
36686
37285
  hlsUrl,
@@ -36728,7 +37327,6 @@ var VideoGridView = React144__namespace.default.memo(({
36728
37327
  isR2Stream: card.isR2Stream,
36729
37328
  fallbackUrl: card.fallbackUrl
36730
37329
  }),
36731
- isVeryLowEfficiency: card.isVeryLowEfficiency,
36732
37330
  legend: effectiveLegend,
36733
37331
  cropping: card.workspaceCropping,
36734
37332
  canvasFps: effectiveCanvasFps,
@@ -37648,223 +38246,6 @@ var UptimeLineChartComponent = ({ points, className = "" }) => {
37648
38246
  ] }) }) });
37649
38247
  };
37650
38248
  var UptimeLineChart = React144__namespace.default.memo(UptimeLineChartComponent);
37651
- var padTime = (value) => value.toString().padStart(2, "0");
37652
- var parseTime = (timeValue) => {
37653
- if (!timeValue) return null;
37654
- const [hourPart, minutePart] = timeValue.split(":");
37655
- const hour = Number.parseInt(hourPart, 10);
37656
- const minute = Number.parseInt(minutePart ?? "0", 10);
37657
- if (!Number.isFinite(hour) || !Number.isFinite(minute)) return null;
37658
- return { hour, minute };
37659
- };
37660
- var normalizeIdleTimeHourly = (idleTimeHourly) => {
37661
- if (!idleTimeHourly || typeof idleTimeHourly !== "object") {
37662
- return {};
37663
- }
37664
- return Object.fromEntries(
37665
- Object.entries(idleTimeHourly).map(([key, value]) => {
37666
- if (Array.isArray(value)) return [key, value];
37667
- if (value && Array.isArray(value.values)) {
37668
- return [key, value.values];
37669
- }
37670
- return [key, []];
37671
- })
37672
- );
37673
- };
37674
- var interpretIdleValue = (value) => {
37675
- if (value === 1 || value === "1") return "idle";
37676
- if (value === 0 || value === "0") return "active";
37677
- if (value === "x" || value === null || value === void 0) return "unknown";
37678
- return "unknown";
37679
- };
37680
- var getShiftDurationMinutes = (shiftStart, shiftEnd) => {
37681
- const start = parseTime(shiftStart);
37682
- const end = parseTime(shiftEnd);
37683
- if (!start || !end) return null;
37684
- let duration = end.hour * 60 + end.minute - (start.hour * 60 + start.minute);
37685
- if (duration <= 0) {
37686
- duration += 24 * 60;
37687
- }
37688
- return duration > 0 ? duration : null;
37689
- };
37690
- var getShiftElapsedMinutes = ({
37691
- shiftStart,
37692
- shiftEnd,
37693
- shiftDate,
37694
- timezone,
37695
- now: now4 = /* @__PURE__ */ new Date()
37696
- }) => {
37697
- if (!shiftDate || !timezone) return null;
37698
- const startTime = parseTime(shiftStart);
37699
- const endTime = parseTime(shiftEnd);
37700
- if (!startTime || !endTime) return null;
37701
- const shiftStartDate = dateFnsTz.fromZonedTime(
37702
- `${shiftDate}T${padTime(startTime.hour)}:${padTime(startTime.minute)}:00`,
37703
- timezone
37704
- );
37705
- let shiftEndDate = dateFnsTz.fromZonedTime(
37706
- `${shiftDate}T${padTime(endTime.hour)}:${padTime(endTime.minute)}:00`,
37707
- timezone
37708
- );
37709
- if (shiftEndDate <= shiftStartDate) {
37710
- shiftEndDate = dateFns.addDays(shiftEndDate, 1);
37711
- }
37712
- const shiftMinutes = Math.max(dateFns.differenceInMinutes(shiftEndDate, shiftStartDate), 0);
37713
- if (shiftMinutes <= 0) return null;
37714
- const elapsed = dateFns.differenceInMinutes(now4, shiftStartDate);
37715
- return Math.min(Math.max(elapsed, 0), shiftMinutes);
37716
- };
37717
- var maskFutureHourlySeries = ({
37718
- data,
37719
- shiftStart,
37720
- shiftEnd,
37721
- shiftDate,
37722
- timezone,
37723
- now: now4 = /* @__PURE__ */ new Date()
37724
- }) => {
37725
- if (!Array.isArray(data)) {
37726
- return [];
37727
- }
37728
- const normalizedData = data.map((value) => typeof value === "number" && Number.isFinite(value) ? value : null);
37729
- if (!normalizedData.length) {
37730
- return normalizedData;
37731
- }
37732
- const shiftMinutes = getShiftDurationMinutes(shiftStart, shiftEnd);
37733
- const elapsedMinutes = getShiftElapsedMinutes({
37734
- shiftStart,
37735
- shiftEnd,
37736
- shiftDate,
37737
- timezone,
37738
- now: now4
37739
- });
37740
- if (shiftMinutes === null || elapsedMinutes === null || elapsedMinutes >= shiftMinutes) {
37741
- return normalizedData;
37742
- }
37743
- return normalizedData.map((value, index) => {
37744
- const slotStartMinutes = index * 60;
37745
- return slotStartMinutes > elapsedMinutes ? null : value;
37746
- });
37747
- };
37748
- var buildUptimeSeries = ({
37749
- idleTimeHourly,
37750
- shiftStart,
37751
- shiftEnd,
37752
- shiftDate,
37753
- timezone,
37754
- elapsedMinutes
37755
- }) => {
37756
- const normalizedIdle = normalizeIdleTimeHourly(idleTimeHourly || {});
37757
- const hasIdleData = Object.keys(normalizedIdle).length > 0;
37758
- if (!hasIdleData || !shiftDate || !timezone) {
37759
- return {
37760
- points: [],
37761
- activeMinutes: 0,
37762
- idleMinutes: 0,
37763
- availableMinutes: 0,
37764
- shiftMinutes: 0,
37765
- elapsedMinutes: 0,
37766
- hasData: false
37767
- };
37768
- }
37769
- const startTime = parseTime(shiftStart);
37770
- const endTime = parseTime(shiftEnd);
37771
- if (!startTime || !endTime) {
37772
- return {
37773
- points: [],
37774
- activeMinutes: 0,
37775
- idleMinutes: 0,
37776
- availableMinutes: 0,
37777
- shiftMinutes: 0,
37778
- elapsedMinutes: 0,
37779
- hasData: false
37780
- };
37781
- }
37782
- const shiftStartDate = dateFnsTz.fromZonedTime(
37783
- `${shiftDate}T${padTime(startTime.hour)}:${padTime(startTime.minute)}:00`,
37784
- timezone
37785
- );
37786
- let shiftEndDate = dateFnsTz.fromZonedTime(
37787
- `${shiftDate}T${padTime(endTime.hour)}:${padTime(endTime.minute)}:00`,
37788
- timezone
37789
- );
37790
- if (shiftEndDate <= shiftStartDate) {
37791
- shiftEndDate = dateFns.addDays(shiftEndDate, 1);
37792
- }
37793
- const shiftMinutes = Math.max(dateFns.differenceInMinutes(shiftEndDate, shiftStartDate), 0);
37794
- if (shiftMinutes <= 0) {
37795
- return {
37796
- points: [],
37797
- activeMinutes: 0,
37798
- idleMinutes: 0,
37799
- availableMinutes: 0,
37800
- shiftMinutes: 0,
37801
- elapsedMinutes: 0,
37802
- hasData: false
37803
- };
37804
- }
37805
- const elapsedMinutesClamped = Number.isFinite(elapsedMinutes) ? Math.min(Math.max(Math.floor(elapsedMinutes ?? 0), 0), shiftMinutes) : shiftMinutes;
37806
- const points = [];
37807
- let activeMinutes = 0;
37808
- let idleMinutes = 0;
37809
- for (let minuteIndex = 0; minuteIndex < shiftMinutes; minuteIndex += 1) {
37810
- const minuteDate = dateFns.addMinutes(shiftStartDate, minuteIndex);
37811
- const timeLabel = dateFnsTz.formatInTimeZone(minuteDate, timezone, "h:mm a");
37812
- if (minuteIndex >= elapsedMinutesClamped) {
37813
- points.push({
37814
- minuteIndex,
37815
- timeLabel,
37816
- uptime: null,
37817
- status: "unknown"
37818
- });
37819
- continue;
37820
- }
37821
- const hourKey = dateFnsTz.formatInTimeZone(minuteDate, timezone, "H");
37822
- const minuteKey = Number.parseInt(dateFnsTz.formatInTimeZone(minuteDate, timezone, "m"), 10);
37823
- const hourBucket = normalizedIdle[hourKey] || [];
37824
- const value = Array.isArray(hourBucket) ? hourBucket[minuteKey] : void 0;
37825
- const status = interpretIdleValue(value);
37826
- if (status === "active") activeMinutes += 1;
37827
- if (status === "idle") idleMinutes += 1;
37828
- points.push({
37829
- minuteIndex,
37830
- timeLabel,
37831
- uptime: status === "active" ? 1 : status === "idle" ? 0 : null,
37832
- status
37833
- });
37834
- }
37835
- return {
37836
- points,
37837
- activeMinutes,
37838
- idleMinutes,
37839
- availableMinutes: activeMinutes + idleMinutes,
37840
- shiftMinutes,
37841
- elapsedMinutes: elapsedMinutesClamped,
37842
- hasData: activeMinutes + idleMinutes > 0
37843
- };
37844
- };
37845
- var getUptimeUtilizationPercent = (shift) => {
37846
- const efficiency = shift.efficiency;
37847
- if (Number.isFinite(efficiency)) {
37848
- return Math.round(Math.max(0, Math.min(100, Number(efficiency))));
37849
- }
37850
- const idleSeconds = Number.isFinite(shift.idleTime) ? Number(shift.idleTime) : 0;
37851
- const activeSeconds = Number.isFinite(shift.activeTimeSeconds) ? Number(shift.activeTimeSeconds) : null;
37852
- let availableSeconds = Number.isFinite(shift.availableTimeSeconds) ? Number(shift.availableTimeSeconds) : null;
37853
- if (availableSeconds === null) {
37854
- if ((activeSeconds ?? 0) > 0 || idleSeconds > 0) {
37855
- availableSeconds = (activeSeconds ?? 0) + idleSeconds;
37856
- } else {
37857
- return 0;
37858
- }
37859
- }
37860
- if (availableSeconds <= 0) return 0;
37861
- const clampedIdleSeconds = Math.min(Math.max(idleSeconds, 0), availableSeconds);
37862
- const productiveSeconds = Math.max(
37863
- activeSeconds ?? availableSeconds - clampedIdleSeconds,
37864
- 0
37865
- );
37866
- return Math.round(productiveSeconds / availableSeconds * 100);
37867
- };
37868
38249
  var getTimeFromTimeString = (timeStr) => {
37869
38250
  if (!timeStr) {
37870
38251
  return { hour: 0, minute: 0, decimalHour: 0 };
@@ -50195,7 +50576,7 @@ var formatOperationalDateKey = (dateKey, options) => {
50195
50576
  timeZone: "UTC"
50196
50577
  });
50197
50578
  };
50198
- var getSegmentMinutes = (isoString, timeZone, reportDate) => {
50579
+ var getSegmentMinutes2 = (isoString, timeZone, reportDate) => {
50199
50580
  const date = new Date(isoString);
50200
50581
  if (isNaN(date.getTime())) return NaN;
50201
50582
  const formatter = new Intl.DateTimeFormat("en-US", {
@@ -50216,13 +50597,13 @@ var getSegmentMinutes = (isoString, timeZone, reportDate) => {
50216
50597
  if (dateKey > reportDate) {
50217
50598
  const d1 = new Date(dateKey);
50218
50599
  const d2 = new Date(reportDate);
50219
- const diffDays = Math.round((d1.getTime() - d2.getTime()) / (1e3 * 3600 * 24));
50220
- minutes += diffDays * 24 * 60;
50600
+ const diffDays2 = Math.round((d1.getTime() - d2.getTime()) / (1e3 * 3600 * 24));
50601
+ minutes += diffDays2 * 24 * 60;
50221
50602
  } else if (dateKey < reportDate) {
50222
50603
  const d1 = new Date(dateKey);
50223
50604
  const d2 = new Date(reportDate);
50224
- const diffDays = Math.round((d2.getTime() - d1.getTime()) / (1e3 * 3600 * 24));
50225
- minutes -= diffDays * 24 * 60;
50605
+ const diffDays2 = Math.round((d2.getTime() - d1.getTime()) / (1e3 * 3600 * 24));
50606
+ minutes -= diffDays2 * 24 * 60;
50226
50607
  }
50227
50608
  return minutes;
50228
50609
  };
@@ -50632,7 +51013,7 @@ var LinePdfGenerator = ({
50632
51013
  const skuRemarksByIndex = {};
50633
51014
  if (lineInfo.metrics.sku_segments && lineInfo.metrics.sku_segments.length > 0) {
50634
51015
  lineInfo.metrics.sku_segments.forEach((segment, segmentIndex) => {
50635
- const segmentMinutes = getSegmentMinutes(segment.start_time, reportTimezone, lineInfo.date);
51016
+ const segmentMinutes = getSegmentMinutes2(segment.start_time, reportTimezone, lineInfo.date);
50636
51017
  if (!isNaN(segmentMinutes)) {
50637
51018
  const intervalIndex = hourlyTimeRanges.findIndex(
50638
51019
  (inv) => segmentMinutes >= inv.start && segmentMinutes < inv.end
@@ -52367,7 +52748,7 @@ var formatOperationalDateKey2 = (dateKey, options) => {
52367
52748
  timeZone: "UTC"
52368
52749
  });
52369
52750
  };
52370
- var getSegmentMinutes2 = (isoString, timeZone, reportDate) => {
52751
+ var getSegmentMinutes3 = (isoString, timeZone, reportDate) => {
52371
52752
  const date = new Date(isoString);
52372
52753
  if (isNaN(date.getTime())) return NaN;
52373
52754
  const formatter = new Intl.DateTimeFormat("en-US", {
@@ -52388,13 +52769,13 @@ var getSegmentMinutes2 = (isoString, timeZone, reportDate) => {
52388
52769
  if (dateKey > reportDate) {
52389
52770
  const d1 = new Date(dateKey);
52390
52771
  const d2 = new Date(reportDate);
52391
- const diffDays = Math.round((d1.getTime() - d2.getTime()) / (1e3 * 3600 * 24));
52392
- minutes += diffDays * 24 * 60;
52772
+ const diffDays2 = Math.round((d1.getTime() - d2.getTime()) / (1e3 * 3600 * 24));
52773
+ minutes += diffDays2 * 24 * 60;
52393
52774
  } else if (dateKey < reportDate) {
52394
52775
  const d1 = new Date(dateKey);
52395
52776
  const d2 = new Date(reportDate);
52396
- const diffDays = Math.round((d2.getTime() - d1.getTime()) / (1e3 * 3600 * 24));
52397
- minutes -= diffDays * 24 * 60;
52777
+ const diffDays2 = Math.round((d2.getTime() - d1.getTime()) / (1e3 * 3600 * 24));
52778
+ minutes -= diffDays2 * 24 * 60;
52398
52779
  }
52399
52780
  return minutes;
52400
52781
  };
@@ -52626,7 +53007,7 @@ var WorkspacePdfGenerator = ({
52626
53007
  const skuRemarksByIndex = {};
52627
53008
  if (workspace.sku_segments && workspace.sku_segments.length > 0) {
52628
53009
  workspace.sku_segments.forEach((segment, segmentIndex) => {
52629
- const segmentMinutes = getSegmentMinutes2(segment.start_time, reportTimezone, workspace.date);
53010
+ const segmentMinutes = getSegmentMinutes3(segment.start_time, reportTimezone, workspace.date);
52630
53011
  if (!isNaN(segmentMinutes)) {
52631
53012
  const intervalIndex = hourlyIntervals.findIndex(
52632
53013
  (inv) => segmentMinutes >= inv.start && segmentMinutes < inv.end
@@ -53468,12 +53849,10 @@ var formatPercentRange = (min, max) => {
53468
53849
  return `${format10(min)}-${format10(max)}%`;
53469
53850
  };
53470
53851
  var Legend5 = ({
53471
- useBottleneckLabel = false,
53472
53852
  legend,
53473
53853
  metricLabel = "Efficiency"
53474
53854
  }) => {
53475
53855
  const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;
53476
- const exclamationLabel = useBottleneckLabel ? "Bottleneck" : "<50% efficiency";
53477
53856
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2 sm:gap-4 text-xs font-medium text-slate-600", children: [
53478
53857
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "font-medium text-gray-700 hidden sm:block", children: [
53479
53858
  metricLabel,
@@ -53492,11 +53871,6 @@ var Legend5 = ({
53492
53871
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-2 h-2 sm:w-2.5 sm:h-2.5 rounded-full bg-[#E34329]" }),
53493
53872
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: formatPercentRange(effectiveLegend.red_min, effectiveLegend.red_max) })
53494
53873
  ] })
53495
- ] }),
53496
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden sm:block w-px h-4 bg-slate-200 mx-1" }),
53497
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
53498
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center w-3 h-3 sm:w-4 sm:h-4 bg-[#E34329] rounded-full text-white font-bold text-[8px] sm:text-[10px]", children: "!" }),
53499
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: exclamationLabel })
53500
53874
  ] })
53501
53875
  ] });
53502
53876
  };
@@ -53610,7 +53984,6 @@ var WorkspaceGrid = React144__namespace.default.memo(({
53610
53984
  factoryView = "factory",
53611
53985
  line2Uuid = "line-2",
53612
53986
  className = "",
53613
- hasFlowBuffers = false,
53614
53987
  legend = DEFAULT_EFFICIENCY_LEGEND,
53615
53988
  videoSources = {},
53616
53989
  videoStreamsByWorkspaceId = {},
@@ -53654,7 +54027,7 @@ var WorkspaceGrid = React144__namespace.default.memo(({
53654
54027
  );
53655
54028
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex flex-col w-full h-full overflow-hidden bg-slate-50/50 ${className}`, children: [
53656
54029
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-none px-4 py-3 z-20 flex flex-row items-center justify-between gap-4", children: [
53657
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden sm:block", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "inline-flex bg-white/95 rounded-lg shadow-sm px-4 py-2 border border-slate-200/60 backdrop-blur-sm", children: /* @__PURE__ */ jsxRuntime.jsx(Legend5, { legend, useBottleneckLabel: hasFlowBuffers, metricLabel: legendMetricLabel }) }) }),
54030
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden sm:block", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "inline-flex bg-white/95 rounded-lg shadow-sm px-4 py-2 border border-slate-200/60 backdrop-blur-sm", children: /* @__PURE__ */ jsxRuntime.jsx(Legend5, { legend, metricLabel: legendMetricLabel }) }) }),
53658
54031
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-3 shrink-0", children: [
53659
54032
  toolbarRightContent,
53660
54033
  mapViewEnabled && /* @__PURE__ */ jsxRuntime.jsx(
@@ -53674,7 +54047,7 @@ var WorkspaceGrid = React144__namespace.default.memo(({
53674
54047
  )
53675
54048
  ] })
53676
54049
  ] }),
53677
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sm:hidden px-3 py-2 bg-white border-b border-slate-200/60 z-10", children: /* @__PURE__ */ jsxRuntime.jsx(Legend5, { legend, useBottleneckLabel: hasFlowBuffers, metricLabel: legendMetricLabel }) }),
54050
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sm:hidden px-3 py-2 bg-white border-b border-slate-200/60 z-10", children: /* @__PURE__ */ jsxRuntime.jsx(Legend5, { legend, metricLabel: legendMetricLabel }) }),
53678
54051
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 relative overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(AnimatePresence, { mode: "wait", children: viewMode === "video" ? /* @__PURE__ */ jsxRuntime.jsx(
53679
54052
  motion.div,
53680
54053
  {
@@ -64335,6 +64708,7 @@ var buildLineInfoSnapshot = (lineDetails, metrics2) => {
64335
64708
  underperforming_workspace_uuids: metrics2.underperforming_workspace_uuids || [],
64336
64709
  output_array: metrics2.output_array || [],
64337
64710
  output_hourly: metrics2.output_hourly,
64711
+ hourly_target_output: metrics2.hourly_target_output ?? null,
64338
64712
  line_threshold: metrics2.line_threshold ?? 0,
64339
64713
  threshold_pph: metrics2.threshold_pph ?? 0,
64340
64714
  shift_start: metrics2.shift_start || "06:00",
@@ -64421,6 +64795,7 @@ var transformLineMetrics = (lineId, detailResponse, queryDate, queryShiftId) =>
64421
64795
  underperforming_workspace_names: [],
64422
64796
  underperforming_workspace_uuids: [],
64423
64797
  output_array: [],
64798
+ hourly_target_output: null,
64424
64799
  line_threshold: 0,
64425
64800
  threshold_pph: 0,
64426
64801
  shift_start: "06:00",
@@ -65458,6 +65833,9 @@ var BottomSection = React144.memo(({
65458
65833
  workspaceDisplayNames,
65459
65834
  hourlyOutputData,
65460
65835
  hourlyThreshold,
65836
+ hourlyTargetOutput,
65837
+ idleTimeHourly,
65838
+ timezone,
65461
65839
  urlDate,
65462
65840
  urlShift,
65463
65841
  navigate,
@@ -65628,8 +66006,12 @@ var BottomSection = React144.memo(({
65628
66006
  {
65629
66007
  data: hourlyOutputData,
65630
66008
  pphThreshold: hourlyThreshold,
66009
+ hourlyTargetOutput,
65631
66010
  shiftStart: lineInfo.metrics.shift_start || "06:00",
65632
66011
  shiftEnd: lineInfo.metrics.shift_end,
66012
+ idleTimeHourly,
66013
+ shiftDate: lineInfo.date,
66014
+ timezone,
65633
66015
  skuSegments: skuAware ? skuSegments : void 0,
65634
66016
  activeSkuId
65635
66017
  }
@@ -65650,6 +66032,9 @@ var BottomSection = React144.memo(({
65650
66032
  if (prevProps.lineInfo.monitoring_mode !== nextProps.lineInfo.monitoring_mode) return false;
65651
66033
  if (prevProps.skuAware !== nextProps.skuAware) return false;
65652
66034
  if (prevProps.activeSkuId !== nextProps.activeSkuId) return false;
66035
+ if (JSON.stringify(prevProps.hourlyTargetOutput || []) !== JSON.stringify(nextProps.hourlyTargetOutput || [])) {
66036
+ return false;
66037
+ }
65653
66038
  const prevSkuSegmentsSignature = JSON.stringify(
65654
66039
  (prevProps.skuSegments || []).map((segment) => ({
65655
66040
  sku_id: segment.sku_id,
@@ -65722,7 +66107,9 @@ var BottomSection = React144.memo(({
65722
66107
  );
65723
66108
  if (prevWorkspaceSignature !== nextWorkspaceSignature) return false;
65724
66109
  if (prevProps.lineInfo.metrics.shift_start !== nextProps.lineInfo.metrics.shift_start) return false;
66110
+ if (prevProps.lineInfo.metrics.shift_end !== nextProps.lineInfo.metrics.shift_end) return false;
65725
66111
  if (prevProps.hourlyThreshold !== nextProps.hourlyThreshold) return false;
66112
+ if (JSON.stringify(prevProps.idleTimeHourly || {}) !== JSON.stringify(nextProps.idleTimeHourly || {})) return false;
65726
66113
  if (prevProps.urlDate !== nextProps.urlDate || prevProps.urlShift !== nextProps.urlShift) return false;
65727
66114
  if (prevProps.workspaceDisplayNames !== nextProps.workspaceDisplayNames) return false;
65728
66115
  return true;
@@ -65990,6 +66377,7 @@ var KPIDetailView = ({
65990
66377
  }
65991
66378
  }, [urlDate, urlShift, urlTab]);
65992
66379
  const { shiftConfig, isLoading: isShiftConfigLoading } = useDynamicShiftConfig(lineId);
66380
+ const lineTimezone = shiftConfig?.timezone || configuredTimezone;
65993
66381
  const getShiftName = React144.useCallback((shiftId) => {
65994
66382
  return getShiftNameById(shiftId, configuredTimezone, shiftConfig);
65995
66383
  }, [configuredTimezone, shiftConfig]);
@@ -66030,12 +66418,12 @@ var KPIDetailView = ({
66030
66418
  operationalTodayDate.setHours(0, 0, 0, 0);
66031
66419
  compareDateInZone.setHours(0, 0, 0, 0);
66032
66420
  const diffTime = compareDateInZone.getTime() - operationalTodayDate.getTime();
66033
- const diffDays = Math.round(diffTime / (1e3 * 60 * 60 * 24));
66034
- if (diffDays === 0) return "Today";
66035
- if (diffDays === -1) return "Yesterday";
66036
- if (diffDays === 1) return "Tomorrow";
66037
- if (diffDays < -1) return `${Math.abs(diffDays)} days ago`;
66038
- if (diffDays > 1) return `${diffDays} days ahead`;
66421
+ const diffDays2 = Math.round(diffTime / (1e3 * 60 * 60 * 24));
66422
+ if (diffDays2 === 0) return "Today";
66423
+ if (diffDays2 === -1) return "Yesterday";
66424
+ if (diffDays2 === 1) return "Tomorrow";
66425
+ if (diffDays2 < -1) return `${Math.abs(diffDays2)} days ago`;
66426
+ if (diffDays2 > 1) return `${diffDays2} days ahead`;
66039
66427
  return "Today";
66040
66428
  }, [configuredTimezone, shiftConfig]);
66041
66429
  const {
@@ -66245,6 +66633,7 @@ var KPIDetailView = ({
66245
66633
  underperforming_workspace_uuids: metrics2.underperforming_workspace_uuids || [],
66246
66634
  output_array: metrics2.output_array || [],
66247
66635
  output_hourly: metrics2.output_hourly,
66636
+ hourly_target_output: metrics2.hourly_target_output ?? null,
66248
66637
  line_threshold: metrics2.line_threshold ?? 0,
66249
66638
  threshold_pph: metrics2.threshold_pph ?? 0,
66250
66639
  shift_start: metrics2.shift_start || "06:00",
@@ -66254,7 +66643,7 @@ var KPIDetailView = ({
66254
66643
  idle_time_hourly: metrics2.idle_time_hourly || null,
66255
66644
  // Multi-SKU additive fields (Phase 6) — propagated from
66256
66645
  // `useLineDetailPageData`. Backend authoritative; we never recompute.
66257
- // The selector below uses these to swap header KPIs per selected SKU.
66646
+ // The output-card selection below uses these to swap header KPIs per selected SKU.
66258
66647
  sku_aware: Boolean(metrics2.sku_aware),
66259
66648
  real_sku_count: metrics2.real_sku_count ?? 0,
66260
66649
  sku_breakdown: Array.isArray(metrics2.sku_breakdown) ? metrics2.sku_breakdown : [],
@@ -66351,7 +66740,7 @@ var KPIDetailView = ({
66351
66740
  }, [lineSkuSegments, outputChartSkuBreakdown, hasUrlDate, hasUrlShift]);
66352
66741
  const normalizedSelectedSkuId = selectedSkuId !== "all" ? selectedSkuId : null;
66353
66742
  const isLineSkuAware = Boolean(resolvedLineInfo?.metrics.sku_aware);
66354
- const showSkuSelector = isLineSkuAware && realSkuOptions.length > 0;
66743
+ const showSkuSelector = isLineSkuAware && realSkuOptions.length > 0 && !isUptimeMode;
66355
66744
  React144.useEffect(() => {
66356
66745
  if (selectedSkuId === "all") return;
66357
66746
  const stillPresent = realSkuOptions.some((item) => item.sku_id === selectedSkuId);
@@ -66378,15 +66767,11 @@ var KPIDetailView = ({
66378
66767
  ...resolvedLineInfo.metrics,
66379
66768
  current_output: selectedSkuRow.current_output ?? 0,
66380
66769
  ideal_output: selectedSkuRow.ideal_output ?? 0,
66381
- avg_efficiency: selectedSkuRow.avg_efficiency ?? resolvedLineInfo.metrics.avg_efficiency,
66382
66770
  total_workspaces: selectedSkuRow.total_workspaces ?? resolvedLineInfo.metrics.total_workspaces,
66383
66771
  underperforming_workspaces: selectedSkuRow.underperforming_workspaces ?? resolvedLineInfo.metrics.underperforming_workspaces,
66384
66772
  underperforming_workspace_names: selectedSkuRow.underperforming_workspace_names ?? resolvedLineInfo.metrics.underperforming_workspace_names,
66385
66773
  underperforming_workspace_uuids: selectedSkuRow.underperforming_workspace_uuids ?? resolvedLineInfo.metrics.underperforming_workspace_uuids,
66386
- output_array: selectedSkuRow.output_array ?? resolvedLineInfo.metrics.output_array,
66387
- output_hourly: selectedSkuRow.output_hourly ?? resolvedLineInfo.metrics.output_hourly,
66388
- line_threshold: selectedSkuRow.line_threshold ?? resolvedLineInfo.metrics.line_threshold,
66389
- poorest_performing_workspaces: selectedSkuRow.poorest_performing_workspaces ?? resolvedLineInfo.metrics.poorest_performing_workspaces
66774
+ line_threshold: selectedSkuRow.line_threshold ?? resolvedLineInfo.metrics.line_threshold
66390
66775
  }
66391
66776
  };
66392
66777
  }, [resolvedLineInfo, selectedSkuRow]);
@@ -67165,11 +67550,9 @@ var KPIDetailView = ({
67165
67550
  showIdleTime: idleTimeVlmEnabled
67166
67551
  }
67167
67552
  ) : (
67168
- // Phase 6: pass `displayLineInfo` so the SKU selector
67169
- // swaps `current_output`, `ideal_output`,
67170
- // `avg_efficiency`, `line_threshold` on the header KPI
67171
- // cards. When `selectedSkuId === 'all'`, this is exactly
67172
- // `resolvedLineInfo` (aggregate path preserved).
67553
+ // Keep the line output + underperforming cards SKU-aware,
67554
+ // while Average Efficiency stays on the aggregate line
67555
+ // metrics even when a specific SKU is selected.
67173
67556
  /* @__PURE__ */ jsxRuntime.jsx(
67174
67557
  MetricCards,
67175
67558
  {
@@ -67214,6 +67597,9 @@ var KPIDetailView = ({
67214
67597
  workspaceDisplayNames,
67215
67598
  hourlyOutputData,
67216
67599
  hourlyThreshold,
67600
+ hourlyTargetOutput: chartMetrics?.hourly_target_output ?? null,
67601
+ idleTimeHourly: chartMetrics?.idle_time_hourly,
67602
+ timezone: lineTimezone,
67217
67603
  urlDate,
67218
67604
  urlShift,
67219
67605
  navigate,
@@ -67279,8 +67665,8 @@ var KPIDetailView = ({
67279
67665
  showIdleTime: idleTimeVlmEnabled
67280
67666
  }
67281
67667
  ) : (
67282
- // Phase 6: pass `displayLineInfo` so the SKU selector
67283
- // swaps the four SKU-specific fields on the header.
67668
+ // Keep the line output + underperforming cards SKU-aware,
67669
+ // while Average Efficiency stays aggregate.
67284
67670
  /* @__PURE__ */ jsxRuntime.jsx(
67285
67671
  MetricCards,
67286
67672
  {
@@ -67325,6 +67711,8 @@ var KPIDetailView = ({
67325
67711
  workspaceDisplayNames,
67326
67712
  hourlyOutputData,
67327
67713
  hourlyThreshold,
67714
+ idleTimeHourly: chartMetrics?.idle_time_hourly,
67715
+ timezone: lineTimezone,
67328
67716
  urlDate,
67329
67717
  urlShift,
67330
67718
  navigate,
@@ -73561,12 +73949,12 @@ var getDaysDifference = (date, timezone = "UTC", shiftStartTime = "06:00") => {
73561
73949
  operationalTodayDate.setHours(0, 0, 0, 0);
73562
73950
  compareDateInTz.setHours(0, 0, 0, 0);
73563
73951
  const diffTime = compareDateInTz.getTime() - operationalTodayDate.getTime();
73564
- const diffDays = Math.round(diffTime / (1e3 * 60 * 60 * 24));
73565
- if (diffDays === 0) return "Today";
73566
- if (diffDays === -1) return "Yesterday";
73567
- if (diffDays === 1) return "Tomorrow";
73568
- if (diffDays < -1) return `${Math.abs(diffDays)} days ago`;
73569
- if (diffDays > 1) return `${diffDays} days ahead`;
73952
+ const diffDays2 = Math.round(diffTime / (1e3 * 60 * 60 * 24));
73953
+ if (diffDays2 === 0) return "Today";
73954
+ if (diffDays2 === -1) return "Yesterday";
73955
+ if (diffDays2 === 1) return "Tomorrow";
73956
+ if (diffDays2 < -1) return `${Math.abs(diffDays2)} days ago`;
73957
+ if (diffDays2 > 1) return `${diffDays2} days ahead`;
73570
73958
  return "Today";
73571
73959
  };
73572
73960
  var getInitialTab = (sourceType, defaultTab, fromMonthly, urlDate) => {
@@ -74002,7 +74390,7 @@ var WorkspaceDetailView = ({
74002
74390
  () => resolveLiveSkuId(workspace?.sku_segments),
74003
74391
  [workspace?.sku_segments]
74004
74392
  );
74005
- const activeSkuId = selectedSkuId || liveSkuId;
74393
+ const activeSkuId = selectedSkuId;
74006
74394
  const resolvedLineId = effectiveLineId || workspace?.line_id || cachedDetailedMetrics?.line_id || cachedOverviewMetrics?.line_id || overviewFallback?.line_id;
74007
74395
  const { timezone: cycleTimeTimezone } = useTimezone({
74008
74396
  lineId: resolvedLineId || void 0,
@@ -74970,6 +75358,7 @@ var WorkspaceDetailView = ({
74970
75358
  {
74971
75359
  data: workspace.hourly_action_counts || [],
74972
75360
  pphThreshold: workspace.pph_threshold || 0,
75361
+ hourlyTargetOutput: workspace.hourly_target_output,
74973
75362
  shiftStart: workspace.shift_start || "06:00",
74974
75363
  shiftEnd: workspace.shift_end,
74975
75364
  showIdleTime: showChartIdleTime,
@@ -74977,7 +75366,7 @@ var WorkspaceDetailView = ({
74977
75366
  idleTimeClips,
74978
75367
  idleTimeClipClassifications,
74979
75368
  shiftDate: idleClipDate,
74980
- timezone,
75369
+ timezone: effectiveCycleTimeTimezone,
74981
75370
  skuSegments: isSkuAware ? skuSegments : void 0,
74982
75371
  activeSkuId
74983
75372
  }
@@ -75116,6 +75505,7 @@ var WorkspaceDetailView = ({
75116
75505
  {
75117
75506
  data: workspace.hourly_action_counts || [],
75118
75507
  pphThreshold: workspace.pph_threshold || 0,
75508
+ hourlyTargetOutput: workspace.hourly_target_output,
75119
75509
  shiftStart: workspace.shift_start || "06:00",
75120
75510
  shiftEnd: workspace.shift_end,
75121
75511
  showIdleTime: showChartIdleTime,
@@ -75123,7 +75513,7 @@ var WorkspaceDetailView = ({
75123
75513
  idleTimeClips,
75124
75514
  idleTimeClipClassifications,
75125
75515
  shiftDate: idleClipDate,
75126
- timezone,
75516
+ timezone: effectiveCycleTimeTimezone,
75127
75517
  skuSegments: isSkuAware ? skuSegments : void 0,
75128
75518
  activeSkuId
75129
75519
  }
@@ -78400,8 +78790,8 @@ var ImprovementCenterView = () => {
78400
78790
  const firstSeen = new Date(new Date(openedAt).toLocaleString("en-US", { timeZone: timezone }));
78401
78791
  if (Number.isNaN(firstSeen.getTime())) return void 0;
78402
78792
  const now4 = new Date((/* @__PURE__ */ new Date()).toLocaleString("en-US", { timeZone: timezone }));
78403
- const diffDays = Math.max(0, Math.floor((now4.getTime() - firstSeen.getTime()) / (1e3 * 60 * 60 * 24)));
78404
- return Math.max(1, Math.ceil(diffDays / 7));
78793
+ const diffDays2 = Math.max(0, Math.floor((now4.getTime() - firstSeen.getTime()) / (1e3 * 60 * 60 * 24)));
78794
+ return Math.max(1, Math.ceil(diffDays2 / 7));
78405
78795
  };
78406
78796
  const toZonedDate = (value) => {
78407
78797
  if (!value) return void 0;
@@ -79082,12 +79472,12 @@ var ThreadSidebar = ({
79082
79472
  const date = new Date(dateString);
79083
79473
  const now4 = /* @__PURE__ */ new Date();
79084
79474
  const diffMs = now4.getTime() - date.getTime();
79085
- const diffDays = Math.floor(diffMs / (1e3 * 60 * 60 * 24));
79086
- if (diffDays === 0) {
79475
+ const diffDays2 = Math.floor(diffMs / (1e3 * 60 * 60 * 24));
79476
+ if (diffDays2 === 0) {
79087
79477
  return "Today";
79088
- } else if (diffDays === 1) {
79478
+ } else if (diffDays2 === 1) {
79089
79479
  return "Yesterday";
79090
- } else if (diffDays < 7) {
79480
+ } else if (diffDays2 < 7) {
79091
79481
  return date.toLocaleDateString("en-US", { weekday: "short" });
79092
79482
  } else {
79093
79483
  return date.toLocaleDateString("en-US", { month: "short", day: "numeric" });