@optifye/dashboard-core 6.11.35 → 6.11.37

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
@@ -1667,6 +1667,7 @@ var DEFAULT_SHIFT_DATA = {
1667
1667
  efficiency: 0,
1668
1668
  output: 0,
1669
1669
  cycleTime: 0,
1670
+ idealCycleTime: 0,
1670
1671
  pph: 0,
1671
1672
  pphThreshold: 0,
1672
1673
  idealOutput: 0,
@@ -4513,6 +4514,7 @@ var dashboardService = {
4513
4514
  avg_efficiency: item.avg_efficiency || 0,
4514
4515
  total_output: item.total_output || 0,
4515
4516
  avg_cycle_time: item.avg_cycle_time || 0,
4517
+ ideal_cycle_time: item.ideal_cycle_time ?? 0,
4516
4518
  ideal_output: item.ideal_output || 0,
4517
4519
  total_day_output: item.total_day_output ?? item.ideal_output ?? 0,
4518
4520
  avg_pph: item.avg_pph || 0,
@@ -17309,7 +17311,7 @@ var useActiveBreaks = (lineIds) => {
17309
17311
  const [isLoading, setIsLoading] = React143.useState(true);
17310
17312
  const [error, setError] = React143.useState(null);
17311
17313
  const supabase = useSupabase();
17312
- const parseTimeToMinutes4 = (timeStr) => {
17314
+ const parseTimeToMinutes5 = (timeStr) => {
17313
17315
  const [hours, minutes] = timeStr.split(":").map(Number);
17314
17316
  return hours * 60 + minutes;
17315
17317
  };
@@ -17318,8 +17320,8 @@ var useActiveBreaks = (lineIds) => {
17318
17320
  return now4.getHours() * 60 + now4.getMinutes();
17319
17321
  };
17320
17322
  const isTimeInBreak = (breakStart, breakEnd, currentMinutes) => {
17321
- const startMinutes = parseTimeToMinutes4(breakStart);
17322
- const endMinutes = parseTimeToMinutes4(breakEnd);
17323
+ const startMinutes = parseTimeToMinutes5(breakStart);
17324
+ const endMinutes = parseTimeToMinutes5(breakEnd);
17323
17325
  if (endMinutes < startMinutes) {
17324
17326
  return currentMinutes >= startMinutes || currentMinutes < endMinutes;
17325
17327
  } else {
@@ -17327,8 +17329,8 @@ var useActiveBreaks = (lineIds) => {
17327
17329
  }
17328
17330
  };
17329
17331
  const calculateBreakProgress = (breakStart, breakEnd, currentMinutes) => {
17330
- const startMinutes = parseTimeToMinutes4(breakStart);
17331
- const endMinutes = parseTimeToMinutes4(breakEnd);
17332
+ const startMinutes = parseTimeToMinutes5(breakStart);
17333
+ const endMinutes = parseTimeToMinutes5(breakEnd);
17332
17334
  let elapsedMinutes = 0;
17333
17335
  let remainingMinutes = 0;
17334
17336
  if (endMinutes < startMinutes) {
@@ -17346,8 +17348,8 @@ var useActiveBreaks = (lineIds) => {
17346
17348
  return { elapsedMinutes, remainingMinutes };
17347
17349
  };
17348
17350
  const isTimeInShift = (startTime, endTime, currentMinutes) => {
17349
- const startMinutes = parseTimeToMinutes4(startTime);
17350
- const endMinutes = parseTimeToMinutes4(endTime);
17351
+ const startMinutes = parseTimeToMinutes5(startTime);
17352
+ const endMinutes = parseTimeToMinutes5(endTime);
17351
17353
  if (endMinutes < startMinutes) {
17352
17354
  return currentMinutes >= startMinutes || currentMinutes < endMinutes;
17353
17355
  } else {
@@ -17407,8 +17409,8 @@ var useActiveBreaks = (lineIds) => {
17407
17409
  const endTime = breakItem.end || breakItem.endTime || "00:00";
17408
17410
  let duration = breakItem.duration || 0;
17409
17411
  if (!duration || duration === 0) {
17410
- const startMinutes = parseTimeToMinutes4(startTime);
17411
- const endMinutes = parseTimeToMinutes4(endTime);
17412
+ const startMinutes = parseTimeToMinutes5(startTime);
17413
+ const endMinutes = parseTimeToMinutes5(endTime);
17412
17414
  duration = endMinutes < startMinutes ? endMinutes + 24 * 60 - startMinutes : endMinutes - startMinutes;
17413
17415
  }
17414
17416
  return {
@@ -17424,8 +17426,8 @@ var useActiveBreaks = (lineIds) => {
17424
17426
  const endTime = breakItem.end || breakItem.endTime || "00:00";
17425
17427
  let duration = breakItem.duration || 0;
17426
17428
  if (!duration || duration === 0) {
17427
- const startMinutes = parseTimeToMinutes4(startTime);
17428
- const endMinutes = parseTimeToMinutes4(endTime);
17429
+ const startMinutes = parseTimeToMinutes5(startTime);
17430
+ const endMinutes = parseTimeToMinutes5(endTime);
17429
17431
  duration = endMinutes < startMinutes ? endMinutes + 24 * 60 - startMinutes : endMinutes - startMinutes;
17430
17432
  }
17431
17433
  return {
@@ -33440,13 +33442,13 @@ var CycleTimeOverTimeChart = ({
33440
33442
  observer.observe(containerRef.current);
33441
33443
  return () => observer.disconnect();
33442
33444
  }, []);
33443
- const parseTimeToMinutes4 = (value) => {
33445
+ const parseTimeToMinutes5 = (value) => {
33444
33446
  const [hours, minutes] = value.split(":").map(Number);
33445
33447
  if (!Number.isFinite(hours) || !Number.isFinite(minutes)) return 0;
33446
33448
  return hours * 60 + minutes;
33447
33449
  };
33448
33450
  const formatHourLabel = (slotIndex) => {
33449
- const baseMinutes = parseTimeToMinutes4(shiftStart);
33451
+ const baseMinutes = parseTimeToMinutes5(shiftStart);
33450
33452
  const absoluteMinutes = baseMinutes + slotIndex * 60;
33451
33453
  const hour24 = Math.floor(absoluteMinutes % (24 * 60) / 60);
33452
33454
  const ampm = hour24 >= 12 ? "PM" : "AM";
@@ -47903,6 +47905,7 @@ var LineMonthlyPdfGenerator = ({
47903
47905
  rangeEnd,
47904
47906
  selectedShiftId,
47905
47907
  availableShifts,
47908
+ lineAssembly = false,
47906
47909
  compact = false,
47907
47910
  className
47908
47911
  }) => {
@@ -48186,8 +48189,8 @@ var LineMonthlyPdfGenerator = ({
48186
48189
  timeZone: "Asia/Kolkata"
48187
48190
  });
48188
48191
  doc.text(dateStr, 25, yPos);
48189
- doc.text(`${shift.total_workspaces - shift.underperforming_workspaces}`, 60, yPos);
48190
- doc.text(`${shift.total_workspaces}`, 95, yPos);
48192
+ doc.text(`${Math.round(shift.output || 0)}`, 60, yPos);
48193
+ doc.text(`${Math.round(shift.idealOutput || 0)}`, 95, yPos);
48191
48194
  doc.text(`${shift.avg_efficiency.toFixed(1)}%`, 135, yPos);
48192
48195
  const statusColor = getEfficiencyColor(shift.avg_efficiency, effectiveLegend);
48193
48196
  if (statusColor === "green") {
@@ -48217,6 +48220,8 @@ var LineMonthlyPdfGenerator = ({
48217
48220
  doc.setTextColor(0, 0, 0);
48218
48221
  }
48219
48222
  const poorestWorkspaces = underperformingWorkspaces[selectedShiftId] || [];
48223
+ const isCycleTimeWorkspace = (workspace) => workspace.metric_mode === "cycle_time" || lineAssembly && (workspace.avg_cycle_time !== void 0 || workspace.ideal_cycle_time !== void 0 || workspace.cycle_ratio !== void 0);
48224
+ const showCycleTimePoorestPerformers = !isUptimeMode && poorestWorkspaces.some(isCycleTimeWorkspace);
48220
48225
  if (poorestWorkspaces && poorestWorkspaces.length > 0) {
48221
48226
  doc.addPage();
48222
48227
  doc.setFontSize(14);
@@ -48242,7 +48247,11 @@ var LineMonthlyPdfGenerator = ({
48242
48247
  doc.setFillColor(245, 245, 245);
48243
48248
  doc.roundedRect(20, 45, 170, 8, 1, 1, "F");
48244
48249
  doc.text("Workspace", 25, 50);
48245
- doc.text(isUptimeMode ? "Avg Utilization" : "Avg Efficiency", 120, 50);
48250
+ doc.text(
48251
+ isUptimeMode ? "Avg Utilization" : showCycleTimePoorestPerformers ? "Cycle Time" : "Avg Efficiency",
48252
+ 120,
48253
+ 50
48254
+ );
48246
48255
  doc.text("Last 5 Days", 160, 50);
48247
48256
  doc.setLineWidth(0.2);
48248
48257
  doc.setDrawColor(220, 220, 220);
@@ -48261,7 +48270,16 @@ var LineMonthlyPdfGenerator = ({
48261
48270
  );
48262
48271
  const workspaceName = rawWorkspaceName.length > 30 ? `${rawWorkspaceName.substring(0, 27)}...` : rawWorkspaceName;
48263
48272
  doc.text(workspaceName, 25, yPos2);
48264
- doc.text(`${(workspace.avg_efficiency || 0).toFixed(1)}%`, 120, yPos2);
48273
+ if (isUptimeMode) {
48274
+ doc.text(`${(workspace.avg_efficiency || 0).toFixed(1)}%`, 120, yPos2);
48275
+ } else if (isCycleTimeWorkspace(workspace)) {
48276
+ const actualCycleTime = Number.isFinite(workspace.avg_cycle_time) ? Number(workspace.avg_cycle_time) : null;
48277
+ const targetCycleTime = Number.isFinite(workspace.ideal_cycle_time) ? Number(workspace.ideal_cycle_time) : null;
48278
+ const cycleTimeText = actualCycleTime !== null ? `${actualCycleTime.toFixed(1)}s${targetCycleTime !== null ? ` / ${targetCycleTime.toFixed(1)}s` : ""}` : "-";
48279
+ doc.text(cycleTimeText, 120, yPos2);
48280
+ } else {
48281
+ doc.text(`${(workspace.avg_efficiency || 0).toFixed(1)}%`, 120, yPos2);
48282
+ }
48265
48283
  const squareSize = 3;
48266
48284
  const squareSpacing = 1;
48267
48285
  let squareX = 160;
@@ -48356,12 +48374,279 @@ Underperforming Workspaces: ${lineInfo.metrics.underperforming_workspaces} / ${l
48356
48374
  }
48357
48375
  );
48358
48376
  };
48377
+
48378
+ // src/lib/utils/hourlyTargets.ts
48379
+ var stripSeconds2 = (timeStr) => timeStr ? timeStr.slice(0, 5) : timeStr;
48380
+ var MINUTES_PER_DAY = 24 * 60;
48381
+ var parseTimeToMinutes2 = (timeString) => {
48382
+ const normalized = stripSeconds2(timeString || "");
48383
+ if (!normalized || !/^[0-2]\d:[0-5]\d$/.test(normalized)) return Number.NaN;
48384
+ const [hours, minutes] = normalized.split(":").map(Number);
48385
+ return hours * 60 + minutes;
48386
+ };
48387
+ var normalizeBreaksOnShiftTimeline = (shiftStart, breaks) => {
48388
+ const shiftStartMinutes = parseTimeToMinutes2(shiftStart);
48389
+ if (!Number.isFinite(shiftStartMinutes)) return [];
48390
+ const normalizedBreaks = [];
48391
+ for (const entry of breaks) {
48392
+ const startRaw = parseTimeToMinutes2(entry.startTime);
48393
+ const endRaw = parseTimeToMinutes2(entry.endTime);
48394
+ if (!Number.isFinite(startRaw) || !Number.isFinite(endRaw)) continue;
48395
+ let start = startRaw;
48396
+ let end = endRaw;
48397
+ if (end <= start) {
48398
+ end += 24 * 60;
48399
+ }
48400
+ if (start < shiftStartMinutes) {
48401
+ start += 24 * 60;
48402
+ end += 24 * 60;
48403
+ }
48404
+ const label = entry.remarks?.trim() || "Break";
48405
+ normalizedBreaks.push({ start, end, label });
48406
+ }
48407
+ return normalizedBreaks;
48408
+ };
48409
+ var roundTarget = (value, mode) => {
48410
+ if (!Number.isFinite(value)) return 0;
48411
+ switch (mode) {
48412
+ case "floor":
48413
+ return Math.floor(value);
48414
+ case "ceil":
48415
+ return Math.ceil(value);
48416
+ case "round":
48417
+ default:
48418
+ return Math.round(value);
48419
+ }
48420
+ };
48421
+ var formatDateKey = (date) => {
48422
+ const year = date.getUTCFullYear();
48423
+ const month = `${date.getUTCMonth() + 1}`.padStart(2, "0");
48424
+ const day = `${date.getUTCDate()}`.padStart(2, "0");
48425
+ return `${year}-${month}-${day}`;
48426
+ };
48427
+ var shiftDateKey = (dateKey, deltaDays) => {
48428
+ const [yearPart, monthPart, dayPart] = dateKey.split("-").map(Number);
48429
+ const year = Number.isFinite(yearPart) ? yearPart : 1970;
48430
+ const month = Number.isFinite(monthPart) ? monthPart : 1;
48431
+ const day = Number.isFinite(dayPart) ? dayPart : 1;
48432
+ const date = new Date(Date.UTC(year, month - 1, day));
48433
+ date.setUTCDate(date.getUTCDate() + deltaDays);
48434
+ return formatDateKey(date);
48435
+ };
48436
+ var getZonedNowSnapshot = (timeZone, now4) => {
48437
+ const formatter = new Intl.DateTimeFormat("en-US", {
48438
+ timeZone,
48439
+ year: "numeric",
48440
+ month: "2-digit",
48441
+ day: "2-digit",
48442
+ hour: "2-digit",
48443
+ minute: "2-digit",
48444
+ hourCycle: "h23"
48445
+ });
48446
+ const parts = formatter.formatToParts(now4).reduce((acc, part) => {
48447
+ if (part.type !== "literal") {
48448
+ acc[part.type] = part.value;
48449
+ }
48450
+ return acc;
48451
+ }, {});
48452
+ const year = Number(parts.year);
48453
+ const month = Number(parts.month);
48454
+ const day = Number(parts.day);
48455
+ const hour = Number(parts.hour);
48456
+ const minute = Number(parts.minute);
48457
+ return {
48458
+ dateKey: `${String(year).padStart(4, "0")}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`,
48459
+ minutesOfDay: (Number.isFinite(hour) ? hour : 0) * 60 + (Number.isFinite(minute) ? minute : 0)
48460
+ };
48461
+ };
48462
+ var getDateKeyInTimeZone = (timeZone, now4 = /* @__PURE__ */ new Date()) => getZonedNowSnapshot(timeZone, now4).dateKey;
48463
+ var buildHourlyIntervals = ({
48464
+ shiftStart,
48465
+ shiftEnd,
48466
+ bucketMinutes = 60,
48467
+ fallbackHours = 11
48468
+ }) => {
48469
+ const startMinutes = parseTimeToMinutes2(shiftStart);
48470
+ if (!Number.isFinite(startMinutes)) return [];
48471
+ const bucket = Number.isFinite(bucketMinutes) && bucketMinutes > 0 ? Math.floor(bucketMinutes) : 60;
48472
+ let totalMinutes;
48473
+ const endRaw = shiftEnd ? parseTimeToMinutes2(shiftEnd) : Number.NaN;
48474
+ if (!Number.isFinite(endRaw)) {
48475
+ totalMinutes = Math.max(0, Math.round(fallbackHours * 60));
48476
+ } else {
48477
+ let endMinutes = endRaw;
48478
+ if (endMinutes <= startMinutes) {
48479
+ endMinutes += 24 * 60;
48480
+ }
48481
+ totalMinutes = endMinutes - startMinutes;
48482
+ }
48483
+ if (!Number.isFinite(totalMinutes) || totalMinutes <= 0) return [];
48484
+ const count = Math.ceil(totalMinutes / bucket);
48485
+ const shiftEndMinutes = startMinutes + totalMinutes;
48486
+ const intervals = [];
48487
+ for (let i = 0; i < count; i += 1) {
48488
+ const start = startMinutes + i * bucket;
48489
+ const end = Math.min(start + bucket, shiftEndMinutes);
48490
+ const minutes = Math.max(0, end - start);
48491
+ if (minutes <= 0) continue;
48492
+ intervals.push({ start, end, minutes });
48493
+ }
48494
+ return intervals;
48495
+ };
48496
+ var computeBreakMinutesByInterval = ({
48497
+ intervals,
48498
+ shiftStart,
48499
+ breaks
48500
+ }) => {
48501
+ if (!intervals.length || !breaks.length) return intervals.map(() => 0);
48502
+ const normalizedBreaks = normalizeBreaksOnShiftTimeline(shiftStart, breaks);
48503
+ return intervals.map((interval) => {
48504
+ if (!normalizedBreaks.length) return 0;
48505
+ let total = 0;
48506
+ for (const brk of normalizedBreaks) {
48507
+ const overlap = Math.max(0, Math.min(interval.end, brk.end) - Math.max(interval.start, brk.start));
48508
+ total += overlap;
48509
+ if (total >= interval.minutes) return interval.minutes;
48510
+ }
48511
+ return Math.min(interval.minutes, total);
48512
+ });
48513
+ };
48514
+ var computeBreakRemarksByInterval = ({
48515
+ intervals,
48516
+ shiftStart,
48517
+ breaks
48518
+ }) => {
48519
+ if (!intervals.length || !breaks.length) return intervals.map(() => "");
48520
+ const normalizedBreaks = normalizeBreaksOnShiftTimeline(shiftStart, breaks);
48521
+ return intervals.map((interval) => {
48522
+ const labels = normalizedBreaks.filter((brk) => Math.max(0, Math.min(interval.end, brk.end) - Math.max(interval.start, brk.start)) > 0).map((brk) => brk.label).filter((label, index, values) => label && values.indexOf(label) === index);
48523
+ return labels.join(", ");
48524
+ });
48525
+ };
48526
+ var computeEffectiveTargets = ({
48527
+ intervals,
48528
+ breakMinutes,
48529
+ pphThreshold,
48530
+ rounding = "round"
48531
+ }) => {
48532
+ return intervals.map((interval, idx) => {
48533
+ const intervalMinutes = Number(interval?.minutes) || 0;
48534
+ const breakMins = Number(breakMinutes?.[idx]) || 0;
48535
+ const plannedWorkMinutes = Math.max(0, intervalMinutes - breakMins);
48536
+ if (!Number.isFinite(pphThreshold) || pphThreshold <= 0) return 0;
48537
+ if (plannedWorkMinutes <= 0) return 0;
48538
+ return roundTarget(pphThreshold * plannedWorkMinutes / 60, rounding);
48539
+ });
48540
+ };
48541
+ var buildHourlyTargetPlan = ({
48542
+ shiftStart,
48543
+ shiftEnd,
48544
+ breaks = [],
48545
+ pphThreshold,
48546
+ bucketMinutes = 60,
48547
+ fallbackHours = 11,
48548
+ rounding = "round"
48549
+ }) => {
48550
+ const intervals = buildHourlyIntervals({
48551
+ shiftStart,
48552
+ shiftEnd,
48553
+ bucketMinutes,
48554
+ fallbackHours
48555
+ });
48556
+ const breakMinutes = computeBreakMinutesByInterval({
48557
+ intervals,
48558
+ shiftStart,
48559
+ breaks
48560
+ });
48561
+ const breakRemarks = computeBreakRemarksByInterval({
48562
+ intervals,
48563
+ shiftStart,
48564
+ breaks
48565
+ });
48566
+ const productiveMinutes = intervals.map((interval, idx) => Math.max(0, (Number(interval?.minutes) || 0) - (Number(breakMinutes[idx]) || 0)));
48567
+ const targets = computeEffectiveTargets({
48568
+ intervals,
48569
+ breakMinutes,
48570
+ pphThreshold,
48571
+ rounding
48572
+ });
48573
+ return {
48574
+ intervals,
48575
+ breakMinutes,
48576
+ breakRemarks,
48577
+ productiveMinutes,
48578
+ targets
48579
+ };
48580
+ };
48581
+ var isHourlyIntervalComplete = ({
48582
+ reportDate,
48583
+ shiftStart,
48584
+ shiftEnd,
48585
+ interval,
48586
+ timeZone = "Asia/Kolkata",
48587
+ now: now4 = /* @__PURE__ */ new Date()
48588
+ }) => {
48589
+ if (!reportDate) return true;
48590
+ const snapshot = getZonedNowSnapshot(timeZone, now4);
48591
+ const shiftStartMinutes = parseTimeToMinutes2(shiftStart);
48592
+ const shiftEndMinutes = shiftEnd ? parseTimeToMinutes2(shiftEnd) : Number.NaN;
48593
+ const wrapsMidnight = Number.isFinite(shiftStartMinutes) && Number.isFinite(shiftEndMinutes) && shiftEndMinutes <= shiftStartMinutes;
48594
+ if (reportDate === snapshot.dateKey) {
48595
+ return interval.end <= snapshot.minutesOfDay;
48596
+ }
48597
+ if (wrapsMidnight && reportDate === shiftDateKey(snapshot.dateKey, -1)) {
48598
+ return interval.end <= snapshot.minutesOfDay + MINUTES_PER_DAY;
48599
+ }
48600
+ return reportDate < snapshot.dateKey;
48601
+ };
48602
+ var isShiftInProgressForReportDate = ({
48603
+ reportDate,
48604
+ shiftStart,
48605
+ shiftEnd,
48606
+ timeZone = "Asia/Kolkata",
48607
+ now: now4 = /* @__PURE__ */ new Date()
48608
+ }) => {
48609
+ if (!reportDate || !shiftStart || !shiftEnd) return false;
48610
+ const shiftStartMinutes = parseTimeToMinutes2(shiftStart);
48611
+ const shiftEndMinutesRaw = parseTimeToMinutes2(shiftEnd);
48612
+ if (!Number.isFinite(shiftStartMinutes) || !Number.isFinite(shiftEndMinutesRaw)) {
48613
+ return false;
48614
+ }
48615
+ let shiftEndMinutes = shiftEndMinutesRaw;
48616
+ const wrapsMidnight = shiftEndMinutes <= shiftStartMinutes;
48617
+ if (wrapsMidnight) {
48618
+ shiftEndMinutes += MINUTES_PER_DAY;
48619
+ }
48620
+ const snapshot = getZonedNowSnapshot(timeZone, now4);
48621
+ let currentMinutes = null;
48622
+ if (reportDate === snapshot.dateKey) {
48623
+ currentMinutes = snapshot.minutesOfDay;
48624
+ } else if (wrapsMidnight && reportDate === shiftDateKey(snapshot.dateKey, -1)) {
48625
+ currentMinutes = snapshot.minutesOfDay + MINUTES_PER_DAY;
48626
+ }
48627
+ if (currentMinutes === null) {
48628
+ return false;
48629
+ }
48630
+ return shiftStartMinutes <= currentMinutes && currentMinutes < shiftEndMinutes;
48631
+ };
48632
+ var formatOperationalDateKey = (dateKey, options) => {
48633
+ const [yearPart, monthPart, dayPart] = dateKey.split("-").map(Number);
48634
+ const year = Number.isFinite(yearPart) ? yearPart : 1970;
48635
+ const month = Number.isFinite(monthPart) ? monthPart : 1;
48636
+ const day = Number.isFinite(dayPart) ? dayPart : 1;
48637
+ return new Date(Date.UTC(year, month - 1, day, 12)).toLocaleDateString("en-IN", {
48638
+ ...options,
48639
+ timeZone: "UTC"
48640
+ });
48641
+ };
48359
48642
  var LinePdfGenerator = ({
48360
48643
  lineInfo,
48361
48644
  workspaceData,
48362
48645
  issueResolutionSummary,
48363
48646
  shiftName,
48364
- className
48647
+ className,
48648
+ shiftBreaks = [],
48649
+ reportTimezone = "Asia/Kolkata"
48365
48650
  }) => {
48366
48651
  const [isGenerating, setIsGenerating] = React143.useState(false);
48367
48652
  const formatResolutionDuration2 = (seconds) => {
@@ -48388,7 +48673,7 @@ var LinePdfGenerator = ({
48388
48673
  doc.setFontSize(9);
48389
48674
  doc.setFont("helvetica", "normal");
48390
48675
  doc.setTextColor(100, 100, 100);
48391
- const generatedText = `Generated on ${(/* @__PURE__ */ new Date()).toLocaleString("en-IN", { timeZone: "Asia/Kolkata" })}`;
48676
+ const generatedText = `Generated on ${(/* @__PURE__ */ new Date()).toLocaleString("en-IN", { timeZone: reportTimezone })}`;
48392
48677
  const generatedTextWidth = doc.getStringUnitWidth(generatedText) * 9 / doc.internal.scaleFactor;
48393
48678
  doc.text(generatedText, doc.internal.pageSize.width - 20 - generatedTextWidth, 15);
48394
48679
  doc.setDrawColor(200, 200, 200);
@@ -48406,11 +48691,10 @@ var LinePdfGenerator = ({
48406
48691
  const isUptimeMode = lineInfo.monitoring_mode === "uptime";
48407
48692
  const rawShiftType = shiftName || (lineInfo.shift_id === 0 ? "Day" : lineInfo.shift_id === 1 ? "Night" : `Shift ${lineInfo.shift_id}`);
48408
48693
  const shiftType = rawShiftType.toLowerCase().includes("shift") ? rawShiftType : `${rawShiftType} Shift`;
48409
- const date = new Date(lineInfo.date).toLocaleDateString("en-IN", {
48694
+ const date = formatOperationalDateKey(lineInfo.date, {
48410
48695
  weekday: "long",
48411
48696
  day: "numeric",
48412
- month: "long",
48413
- timeZone: "Asia/Kolkata"
48697
+ month: "long"
48414
48698
  });
48415
48699
  const shiftStartTime = lineInfo.metrics.shift_start ? (/* @__PURE__ */ new Date(`2000-01-01 ${lineInfo.metrics.shift_start}`)).toLocaleTimeString("en-IN", {
48416
48700
  hour: "2-digit",
@@ -48418,24 +48702,25 @@ var LinePdfGenerator = ({
48418
48702
  hour12: true,
48419
48703
  timeZone: "Asia/Kolkata"
48420
48704
  }) : "N/A";
48421
- const isToday2 = new Date(lineInfo.date).toDateString() === (/* @__PURE__ */ new Date()).toDateString();
48422
- let reportEndTime;
48423
- if (isToday2) {
48424
- reportEndTime = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-IN", {
48425
- hour: "2-digit",
48426
- minute: "2-digit",
48427
- timeZone: "Asia/Kolkata"
48428
- });
48429
- } else {
48430
- reportEndTime = lineInfo.metrics.shift_end ? (/* @__PURE__ */ new Date(`2000-01-01 ${lineInfo.metrics.shift_end}`)).toLocaleTimeString("en-IN", {
48431
- hour: "2-digit",
48432
- minute: "2-digit",
48433
- hour12: true,
48434
- timeZone: "Asia/Kolkata"
48435
- }) : "N/A";
48436
- }
48705
+ const currentTime = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-IN", {
48706
+ hour: "2-digit",
48707
+ minute: "2-digit",
48708
+ timeZone: reportTimezone
48709
+ });
48710
+ const reportEndTime = isShiftInProgressForReportDate({
48711
+ reportDate: lineInfo.date,
48712
+ shiftStart: lineInfo.metrics.shift_start || "",
48713
+ shiftEnd: lineInfo.metrics.shift_end,
48714
+ timeZone: reportTimezone
48715
+ }) ? currentTime : lineInfo.metrics.shift_end ? (/* @__PURE__ */ new Date(`2000-01-01 ${lineInfo.metrics.shift_end}`)).toLocaleTimeString("en-IN", {
48716
+ hour: "2-digit",
48717
+ minute: "2-digit",
48718
+ hour12: true,
48719
+ timeZone: "Asia/Kolkata"
48720
+ }) : "N/A";
48437
48721
  if (isUptimeMode) {
48438
48722
  const configuredTimezone = "Asia/Kolkata";
48723
+ const effectiveUptimeTimezone = reportTimezone || configuredTimezone;
48439
48724
  const lineShiftStart = lineInfo.metrics.shift_start || "06:00";
48440
48725
  const lineShiftEnd = lineInfo.metrics.shift_end || "14:00";
48441
48726
  const shiftMinutes = getShiftDurationMinutes(lineShiftStart, lineShiftEnd) || 0;
@@ -48449,7 +48734,7 @@ var LinePdfGenerator = ({
48449
48734
  shiftStart,
48450
48735
  shiftEnd,
48451
48736
  shiftDate,
48452
- timezone: configuredTimezone
48737
+ timezone: effectiveUptimeTimezone
48453
48738
  });
48454
48739
  let activeMinutes = uptimeSeries.activeMinutes;
48455
48740
  let idleMinutes = uptimeSeries.idleMinutes;
@@ -48530,7 +48815,7 @@ var LinePdfGenerator = ({
48530
48815
  shiftStart: lineShiftStart,
48531
48816
  shiftEnd: lineShiftEnd,
48532
48817
  shiftDate: lineInfo.date,
48533
- timezone: configuredTimezone
48818
+ timezone: effectiveUptimeTimezone
48534
48819
  });
48535
48820
  hourlyData = buildHourlyFromSeries(lineUptimeSeries);
48536
48821
  }
@@ -48638,13 +48923,13 @@ var LinePdfGenerator = ({
48638
48923
  doc.setFontSize(contentFontSize);
48639
48924
  doc.setFont("helvetica", "normal");
48640
48925
  let yPos2 = headerBottomY2 + 5.5;
48641
- const now5 = /* @__PURE__ */ new Date();
48642
- const currentTimeIST2 = new Date(now5.toLocaleString("en-US", { timeZone: configuredTimezone }));
48643
- const [sYear2, sMonth2, sDay2] = lineInfo.date.split("-").map(Number);
48644
- const [sStartH2, sStartM2] = (lineShiftStart || "06:00").split(":").map(Number);
48645
- const shiftStartBase2 = new Date(sYear2, sMonth2 - 1, sDay2, sStartH2, sStartM2 || 0);
48926
+ const now4 = /* @__PURE__ */ new Date();
48927
+ const currentTimeIST = new Date(now4.toLocaleString("en-US", { timeZone: configuredTimezone }));
48928
+ const [sYear, sMonth, sDay] = lineInfo.date.split("-").map(Number);
48929
+ const [sStartH, sStartM] = (lineShiftStart || "06:00").split(":").map(Number);
48930
+ const shiftStartBase = new Date(sYear, sMonth - 1, sDay, sStartH, sStartM || 0);
48646
48931
  hourlyData.forEach((entry, index) => {
48647
- const bucketStartTime = new Date(shiftStartBase2);
48932
+ const bucketStartTime = new Date(shiftStartBase);
48648
48933
  bucketStartTime.setHours(bucketStartTime.getHours() + index);
48649
48934
  const bucketEndTime = new Date(bucketStartTime);
48650
48935
  bucketEndTime.setHours(bucketEndTime.getHours() + 1);
@@ -48655,7 +48940,7 @@ var LinePdfGenerator = ({
48655
48940
  hour: "numeric",
48656
48941
  hour12: true
48657
48942
  })}`;
48658
- const dataCollected = bucketEndTime.getTime() <= currentTimeIST2.getTime();
48943
+ const dataCollected = bucketEndTime.getTime() <= currentTimeIST.getTime();
48659
48944
  if (index < totalRows2 - 1) {
48660
48945
  const rowBottomY = headerBottomY2 + (index + 1) * rowHeight;
48661
48946
  doc.setDrawColor(200, 200, 200);
@@ -48666,11 +48951,10 @@ var LinePdfGenerator = ({
48666
48951
  doc.text(utilizationStr, 147, yPos2);
48667
48952
  yPos2 += rowHeight;
48668
48953
  });
48669
- const fileDate2 = new Date(lineInfo.date).toLocaleDateString("en-IN", {
48954
+ const fileDate2 = formatOperationalDateKey(lineInfo.date, {
48670
48955
  day: "2-digit",
48671
48956
  month: "short",
48672
- year: "numeric",
48673
- timeZone: "Asia/Kolkata"
48957
+ year: "numeric"
48674
48958
  }).replace(/ /g, "_");
48675
48959
  const fileShift2 = shiftType.replace(/ /g, "_");
48676
48960
  const fileName2 = `${lineInfo.line_name}_${fileDate2}_${fileShift2}.pdf`;
@@ -48723,66 +49007,32 @@ var LinePdfGenerator = ({
48723
49007
  doc.setLineWidth(0.8);
48724
49008
  doc.line(20, 123, 190, 123);
48725
49009
  const hourlyOverviewStartY = 128;
48726
- const parseTimeToMinutes4 = (timeStr) => {
48727
- const [hours, minutes] = timeStr.split(":");
48728
- const hour = parseInt(hours, 10);
48729
- const minute = parseInt(minutes || "0", 10);
48730
- if (Number.isNaN(hour) || Number.isNaN(minute)) {
48731
- return NaN;
48732
- }
48733
- return (hour * 60 + minute) % (24 * 60);
48734
- };
48735
49010
  const formatMinutesLabel = (totalMinutes) => {
48736
49011
  const normalized = (totalMinutes % (24 * 60) + 24 * 60) % (24 * 60);
48737
49012
  const hour = Math.floor(normalized / 60);
48738
49013
  const minute = normalized % 60;
48739
- const time2 = /* @__PURE__ */ new Date();
48740
- time2.setHours(hour);
48741
- time2.setMinutes(minute);
48742
- time2.setSeconds(0);
48743
- time2.setMilliseconds(0);
49014
+ const time2 = new Date(Date.UTC(2e3, 0, 1, hour, minute));
48744
49015
  return time2.toLocaleTimeString("en-IN", {
48745
49016
  hour: "2-digit",
48746
49017
  minute: "2-digit",
48747
49018
  hour12: true,
48748
- timeZone: "Asia/Kolkata"
48749
- });
48750
- };
48751
- const buildRange = (startMinutes, minutes) => {
48752
- const endMinutes = startMinutes + minutes;
48753
- return {
48754
- label: `${formatMinutesLabel(startMinutes)} - ${formatMinutesLabel(endMinutes)}`,
48755
- minutes
48756
- };
48757
- };
48758
- const getHourlyTimeRanges = (startTimeStr, endTimeStr) => {
48759
- const startMinutes = parseTimeToMinutes4(startTimeStr);
48760
- if (Number.isNaN(startMinutes)) {
48761
- return [];
48762
- }
48763
- if (!endTimeStr) {
48764
- const defaultHours = 11;
48765
- return Array.from({ length: defaultHours }, (_, i) => buildRange(startMinutes + i * 60, 60));
48766
- }
48767
- const endMinutes = parseTimeToMinutes4(endTimeStr);
48768
- if (Number.isNaN(endMinutes)) {
48769
- const fallbackHours = 11;
48770
- return Array.from({ length: fallbackHours }, (_, i) => buildRange(startMinutes + i * 60, 60));
48771
- }
48772
- let durationMinutes = endMinutes - startMinutes;
48773
- if (durationMinutes <= 0) {
48774
- durationMinutes += 24 * 60;
48775
- }
48776
- const rangeCount = Math.max(1, Math.ceil(durationMinutes / 60));
48777
- return Array.from({ length: rangeCount }, (_, i) => {
48778
- const remainingMinutes = durationMinutes - i * 60;
48779
- const rangeMinutes = remainingMinutes >= 60 ? 60 : remainingMinutes;
48780
- return buildRange(startMinutes + i * 60, rangeMinutes);
49019
+ timeZone: "UTC"
48781
49020
  });
48782
49021
  };
48783
- const hourlyTimeRanges = lineInfo.metrics.shift_start ? getHourlyTimeRanges(lineInfo.metrics.shift_start, lineInfo.metrics.shift_end) : [];
49022
+ const hourlyTimeRanges = buildHourlyIntervals({
49023
+ shiftStart: lineInfo.metrics.shift_start || "06:00",
49024
+ shiftEnd: lineInfo.metrics.shift_end,
49025
+ fallbackHours: 11
49026
+ });
49027
+ const targetPlan = buildHourlyTargetPlan({
49028
+ shiftStart: lineInfo.metrics.shift_start || "06:00",
49029
+ shiftEnd: lineInfo.metrics.shift_end,
49030
+ breaks: shiftBreaks,
49031
+ pphThreshold: Number(lineInfo.metrics.threshold_pph ?? 0),
49032
+ fallbackHours: Math.max(hourlyTimeRanges.length, 1),
49033
+ rounding: "floor"
49034
+ });
48784
49035
  const shiftDuration = hourlyTimeRanges.length || 11;
48785
- const targetOutputPerHour = Math.round(lineInfo.metrics.threshold_pph ?? 0);
48786
49036
  let hourlyActualOutput = [];
48787
49037
  if (lineInfo.metrics.output_hourly && Object.keys(lineInfo.metrics.output_hourly).length > 0) {
48788
49038
  const [startHourStr, startMinuteStr] = (lineInfo.metrics.shift_start || "6:00").split(":");
@@ -48968,29 +49218,29 @@ var LinePdfGenerator = ({
48968
49218
  doc.text("Remarks", 160, tableHeaderY);
48969
49219
  doc.setFont("helvetica", "normal");
48970
49220
  let yPos = tableStartY;
48971
- const now4 = /* @__PURE__ */ new Date();
48972
- const currentTimeIST = new Date(now4.toLocaleString("en-US", { timeZone: "Asia/Kolkata" }));
48973
- const [sYear, sMonth, sDay] = lineInfo.date.split("-").map(Number);
48974
- const [sStartH, sStartM] = (lineInfo.metrics.shift_start || "06:00").split(":").map(Number);
48975
- const shiftStartBase = new Date(sYear, sMonth - 1, sDay, sStartH, sStartM || 0);
48976
49221
  hourlyTimeRanges.forEach((timeRange, index) => {
48977
49222
  const actualOutput = hourlyActualOutput[index] || 0;
48978
- const bucketStartTime = new Date(shiftStartBase);
48979
- bucketStartTime.setHours(bucketStartTime.getHours() + index);
48980
- const bucketEndTime = new Date(bucketStartTime.getTime() + timeRange.minutes * 60 * 1e3);
48981
- const dataCollected = bucketEndTime.getTime() <= currentTimeIST.getTime();
49223
+ const dataCollected = isHourlyIntervalComplete({
49224
+ reportDate: lineInfo.date,
49225
+ shiftStart: lineInfo.metrics.shift_start || "06:00",
49226
+ shiftEnd: lineInfo.metrics.shift_end,
49227
+ interval: timeRange,
49228
+ timeZone: reportTimezone
49229
+ });
48982
49230
  const outputStr = dataCollected ? actualOutput.toString() : "TBD";
48983
49231
  if (index < totalRows - 1) {
48984
49232
  const rowBottomY = headerBottomY + (index + 1) * rowSpacing;
48985
49233
  doc.setDrawColor(200, 200, 200);
48986
49234
  doc.line(20, rowBottomY, 190, rowBottomY);
48987
49235
  }
48988
- const rangeMinutes = timeRange.minutes || 60;
48989
- const targetForRange = targetOutputPerHour * (rangeMinutes / 60);
48990
- const targetStr = rangeMinutes === 60 ? targetOutputPerHour.toString() : targetForRange.toFixed(1).replace(/\.0$/, "");
48991
- doc.text(timeRange.label, 25, yPos);
49236
+ const targetForRange = targetPlan.targets[index] ?? 0;
49237
+ const targetStr = targetForRange.toString();
49238
+ const remarkText = targetPlan.breakRemarks[index] || "";
49239
+ doc.text(`${formatMinutesLabel(timeRange.start)} - ${formatMinutesLabel(timeRange.end)}`, 25, yPos);
48992
49240
  doc.text(outputStr, 75, yPos);
48993
49241
  doc.text(targetStr, 105, yPos);
49242
+ const remarkDisplay = remarkText ? doc.splitTextToSize(remarkText, 26)[0] : "";
49243
+ doc.text(remarkDisplay, 160, yPos);
48994
49244
  if (!dataCollected) {
48995
49245
  doc.setTextColor(100, 100, 100);
48996
49246
  doc.text("-", 135, yPos);
@@ -49006,11 +49256,10 @@ var LinePdfGenerator = ({
49006
49256
  doc.setTextColor(0, 0, 0);
49007
49257
  yPos += rowSpacing;
49008
49258
  });
49009
- const fileDate = new Date(lineInfo.date).toLocaleDateString("en-IN", {
49259
+ const fileDate = formatOperationalDateKey(lineInfo.date, {
49010
49260
  day: "2-digit",
49011
49261
  month: "short",
49012
- year: "numeric",
49013
- timeZone: "Asia/Kolkata"
49262
+ year: "numeric"
49014
49263
  }).replace(/ /g, "_");
49015
49264
  const fileShift = shiftType.replace(/ /g, "_");
49016
49265
  const fileName = `${lineInfo.line_name}_${fileDate}_${fileShift}.pdf`;
@@ -49938,6 +50187,13 @@ var WorkspaceMonthlyHistory = ({
49938
50187
  }, [analysisMonthlyData, selectedShiftId, isUptimeMode, shiftWorkSeconds]);
49939
50188
  const efficiencyDelta = trendSummary?.avg_efficiency?.delta_pp ?? 0;
49940
50189
  const efficiencyImproved = efficiencyDelta >= 0;
50190
+ const assemblyRangeCycleTime = React143.useMemo(() => {
50191
+ const trendCycleTime = Number(trendSummary?.avg_cycle_time?.current);
50192
+ if (Number.isFinite(trendCycleTime) && trendCycleTime > 0) {
50193
+ return Math.round(trendCycleTime);
50194
+ }
50195
+ return metrics2?.avgCycleTime ?? 0;
50196
+ }, [trendSummary?.avg_cycle_time?.current, metrics2?.avgCycleTime]);
49941
50197
  const cycleDeltaRaw = trendSummary?.avg_cycle_time?.delta_seconds ?? 0;
49942
50198
  const cyclePrev = trendSummary?.avg_cycle_time?.previous ?? 0;
49943
50199
  const cycleDelta = cyclePrev ? cycleDeltaRaw / cyclePrev * 100 : 0;
@@ -50123,7 +50379,7 @@ var WorkspaceMonthlyHistory = ({
50123
50379
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-semibold text-gray-600 mb-1", children: "Avg Cycle Time" }),
50124
50380
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 flex-nowrap", children: [
50125
50381
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-2xl font-bold text-gray-900", children: [
50126
- metrics2?.avgCycleTime ?? 0,
50382
+ assemblyRangeCycleTime,
50127
50383
  "s"
50128
50384
  ] }),
50129
50385
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-1 ${cycleWorsened ? "bg-red-50 text-red-600" : "bg-emerald-50 text-emerald-600"} px-2 py-0.5 rounded-full text-[10px] font-medium whitespace-nowrap flex-shrink-0`, children: [
@@ -50459,7 +50715,25 @@ var WorkspaceWhatsAppShareButton = ({
50459
50715
  }
50460
50716
  );
50461
50717
  };
50462
- var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiencyLegend, hourlyCycleTimes }) => {
50718
+ var formatOperationalDateKey2 = (dateKey, options) => {
50719
+ const [yearPart, monthPart, dayPart] = dateKey.split("-").map(Number);
50720
+ const year = Number.isFinite(yearPart) ? yearPart : 1970;
50721
+ const month = Number.isFinite(monthPart) ? monthPart : 1;
50722
+ const day = Number.isFinite(dayPart) ? dayPart : 1;
50723
+ return new Date(Date.UTC(year, month - 1, day, 12)).toLocaleDateString("en-IN", {
50724
+ ...options,
50725
+ timeZone: "UTC"
50726
+ });
50727
+ };
50728
+ var WorkspacePdfGenerator = ({
50729
+ workspace,
50730
+ className,
50731
+ idleTimeReasons,
50732
+ efficiencyLegend,
50733
+ hourlyCycleTimes,
50734
+ shiftBreaks = [],
50735
+ reportTimezone = "Asia/Kolkata"
50736
+ }) => {
50463
50737
  const [isGenerating, setIsGenerating] = React143.useState(false);
50464
50738
  const entityConfig = useEntityConfig();
50465
50739
  const effectiveLegend = efficiencyLegend || DEFAULT_EFFICIENCY_LEGEND;
@@ -50489,7 +50763,7 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
50489
50763
  doc.setFontSize(9);
50490
50764
  doc.setFont("helvetica", "normal");
50491
50765
  doc.setTextColor(100, 100, 100);
50492
- const generatedText = `Generated on ${(/* @__PURE__ */ new Date()).toLocaleString("en-IN", { timeZone: "Asia/Kolkata" })}`;
50766
+ const generatedText = `Generated on ${(/* @__PURE__ */ new Date()).toLocaleString("en-IN", { timeZone: reportTimezone })}`;
50493
50767
  const generatedTextWidth = doc.getStringUnitWidth(generatedText) * 9 / doc.internal.scaleFactor;
50494
50768
  doc.text(generatedText, doc.internal.pageSize.width - 20 - generatedTextWidth, 15);
50495
50769
  doc.setDrawColor(200, 200, 200);
@@ -50508,11 +50782,10 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
50508
50782
  doc.setFontSize(13);
50509
50783
  doc.setFont("helvetica", "normal");
50510
50784
  doc.setTextColor(60, 60, 60);
50511
- const date = new Date(workspace.date).toLocaleDateString("en-IN", {
50785
+ const date = formatOperationalDateKey2(workspace.date, {
50512
50786
  weekday: "long",
50513
50787
  day: "numeric",
50514
- month: "long",
50515
- timeZone: "Asia/Kolkata"
50788
+ month: "long"
50516
50789
  });
50517
50790
  const rawShiftType = workspace.shift_type || (workspace.shift_id === 0 ? "Day" : workspace.shift_id === 1 ? "Night" : `Shift ${workspace.shift_id}`);
50518
50791
  const shiftType = rawShiftType.toLowerCase().includes("shift") ? rawShiftType : `${rawShiftType} Shift`;
@@ -50521,7 +50794,7 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
50521
50794
  const currentTime = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-IN", {
50522
50795
  hour: "2-digit",
50523
50796
  minute: "2-digit",
50524
- timeZone: "Asia/Kolkata"
50797
+ timeZone: reportTimezone
50525
50798
  });
50526
50799
  const shiftStartTime = (/* @__PURE__ */ new Date(`2000-01-01 ${workspace.shift_start}`)).toLocaleTimeString("en-IN", {
50527
50800
  hour: "2-digit",
@@ -50533,29 +50806,12 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
50533
50806
  minute: "2-digit",
50534
50807
  hour12: true
50535
50808
  });
50536
- const parseTimeToMinutes4 = (timeValue) => {
50537
- const [hourPart, minutePart] = timeValue.split(":").map(Number);
50538
- const hour = Number.isFinite(hourPart) ? hourPart : 0;
50539
- const minute = Number.isFinite(minutePart) ? minutePart : 0;
50540
- return hour * 60 + minute;
50541
- };
50542
- const toShiftUtcMs = (dateKey, timeValue) => {
50543
- const [yearPart, monthPart, dayPart] = dateKey.split("-").map(Number);
50544
- const year = Number.isFinite(yearPart) ? yearPart : 1970;
50545
- const month = Number.isFinite(monthPart) ? monthPart : 1;
50546
- const day = Number.isFinite(dayPart) ? dayPart : 1;
50547
- const [hourPart, minutePart] = timeValue.split(":").map(Number);
50548
- const hour = Number.isFinite(hourPart) ? hourPart : 0;
50549
- const minute = Number.isFinite(minutePart) ? minutePart : 0;
50550
- const IST_OFFSET_MINUTES = 330;
50551
- return Date.UTC(year, month - 1, day, hour, minute) - IST_OFFSET_MINUTES * 60 * 1e3;
50552
- };
50553
- const shiftStartMinutes = parseTimeToMinutes4(workspace.shift_start);
50554
- const shiftEndMinutes = parseTimeToMinutes4(workspace.shift_end);
50555
- const wrapsMidnight = shiftEndMinutes <= shiftStartMinutes;
50556
- const shiftStartUtcMs = toShiftUtcMs(workspace.date, workspace.shift_start);
50557
- const shiftEndUtcMs = toShiftUtcMs(workspace.date, workspace.shift_end) + (wrapsMidnight ? 24 * 60 * 60 * 1e3 : 0);
50558
- const isShiftInProgress = Date.now() >= shiftStartUtcMs && Date.now() < shiftEndUtcMs;
50809
+ const isShiftInProgress = isShiftInProgressForReportDate({
50810
+ reportDate: workspace.date,
50811
+ shiftStart: workspace.shift_start,
50812
+ shiftEnd: workspace.shift_end,
50813
+ timeZone: reportTimezone
50814
+ });
50559
50815
  const reportPeriodEndTime = isShiftInProgress ? currentTime : shiftEndTime;
50560
50816
  doc.setFontSize(12);
50561
50817
  doc.setTextColor(80, 80, 80);
@@ -50661,7 +50917,7 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
50661
50917
  shiftStart: workspace.shift_start,
50662
50918
  shiftEnd: workspace.shift_end,
50663
50919
  shiftDate: workspace.date,
50664
- timezone: "Asia/Kolkata"
50920
+ timezone: reportTimezone
50665
50921
  }) : null;
50666
50922
  const hourlyUptime = uptimeSeries?.points?.length ? Array.from({ length: Math.ceil(uptimeSeries.shiftMinutes / 60) }, (_, index) => {
50667
50923
  const start = index * 60;
@@ -50676,6 +50932,31 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
50676
50932
  const hourlyData = isUptimeMode ? hourlyUptime : isAssemblyCycleMode ? hourlyCycleTimes && hourlyCycleTimes.length > 0 ? hourlyCycleTimes : workspace.hourly_action_counts || [] : workspace.hourly_action_counts || [];
50677
50933
  const hourlyTarget = workspace.pph_threshold;
50678
50934
  const cycleTarget = workspace.ideal_cycle_time || 0;
50935
+ const hourlyIntervals = buildHourlyIntervals({
50936
+ shiftStart: workspace.shift_start,
50937
+ shiftEnd: workspace.shift_end,
50938
+ fallbackHours: Math.max(hourlyData.length, 1)
50939
+ });
50940
+ const outputTargetPlan = !isUptimeMode && !isAssemblyCycleMode ? buildHourlyTargetPlan({
50941
+ shiftStart: workspace.shift_start,
50942
+ shiftEnd: workspace.shift_end,
50943
+ breaks: shiftBreaks,
50944
+ pphThreshold: workspace.pph_threshold,
50945
+ fallbackHours: Math.max(hourlyData.length, 1),
50946
+ rounding: "floor"
50947
+ }) : null;
50948
+ const formatIntervalLabel = (totalMinutes) => {
50949
+ const normalized = (totalMinutes % (24 * 60) + 24 * 60) % (24 * 60);
50950
+ const hour = Math.floor(normalized / 60);
50951
+ const minute = normalized % 60;
50952
+ const time2 = new Date(Date.UTC(2e3, 0, 1, hour, minute));
50953
+ return time2.toLocaleTimeString("en-IN", {
50954
+ hour: "numeric",
50955
+ ...minute > 0 ? { minute: "2-digit" } : {},
50956
+ hour12: true,
50957
+ timeZone: "UTC"
50958
+ });
50959
+ };
50679
50960
  const pageHeight = doc.internal.pageSize.height;
50680
50961
  const maxContentY = pageHeight - 15;
50681
50962
  const baseTableStartY = hourlyPerfStartY + 31;
@@ -50743,36 +51024,30 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
50743
51024
  doc.setFontSize(contentFontSize);
50744
51025
  doc.setFont("helvetica", "normal");
50745
51026
  let yPos = headerBottomY + 5.5;
50746
- const workspaceDate = new Date(workspace.date);
50747
- const today = /* @__PURE__ */ new Date();
50748
- today.setHours(0, 0, 0, 0);
50749
- workspaceDate.setHours(0, 0, 0, 0);
50750
- const isToday2 = workspaceDate.getTime() === today.getTime();
51027
+ const isToday2 = getDateKeyInTimeZone(reportTimezone) === workspace.date;
50751
51028
  let currentHour = 24;
50752
51029
  if (isToday2) {
50753
51030
  const now4 = /* @__PURE__ */ new Date();
50754
- const currentTimeIST = new Date(now4.toLocaleString("en-US", { timeZone: "Asia/Kolkata" }));
50755
- currentHour = currentTimeIST.getHours();
51031
+ const currentTimeInReportTimezone = new Date(now4.toLocaleString("en-US", { timeZone: reportTimezone }));
51032
+ currentHour = currentTimeInReportTimezone.getHours();
50756
51033
  }
50757
51034
  hourlyData.forEach((entry, index) => {
50758
- const startTime = /* @__PURE__ */ new Date(`2000-01-01 ${workspace.shift_start}`);
50759
- startTime.setHours(startTime.getHours() + index);
50760
- const endTime = new Date(startTime);
50761
- endTime.setHours(endTime.getHours() + 1);
50762
- const timeRange = `${startTime.toLocaleTimeString("en-IN", {
50763
- hour: "numeric",
50764
- hour12: true
50765
- })} - ${endTime.toLocaleTimeString("en-IN", {
50766
- hour: "numeric",
50767
- hour12: true
50768
- })}`;
50769
- const hourNumber = startTime.getHours();
50770
- const dataCollected = !isToday2 || hourNumber < currentHour;
51035
+ const interval = hourlyIntervals[index];
51036
+ const timeRange = interval ? `${formatIntervalLabel(interval.start)} - ${formatIntervalLabel(interval.end)}` : `${index + 1}`;
51037
+ const dataCollected = interval ? isHourlyIntervalComplete({
51038
+ reportDate: workspace.date,
51039
+ shiftStart: workspace.shift_start,
51040
+ shiftEnd: workspace.shift_end,
51041
+ interval,
51042
+ timeZone: reportTimezone
51043
+ }) : !isToday2 || currentHour >= 24;
50771
51044
  const outputValue = isUptimeMode ? entry.activeMinutes ?? 0 : entry;
50772
51045
  const idleValue = isUptimeMode ? entry.idleMinutes ?? 0 : 0;
50773
51046
  const uptimePercent = isUptimeMode ? entry.uptimePercent ?? 0 : 0;
50774
51047
  const outputStr = dataCollected ? outputValue.toString() : "TBD";
50775
- const targetStr = isUptimeMode ? dataCollected ? idleValue.toString() : "TBD" : hourlyTarget.toString();
51048
+ const effectiveTarget = outputTargetPlan?.targets[index] ?? hourlyTarget;
51049
+ const remarkText = outputTargetPlan?.breakRemarks[index] || "";
51050
+ const targetStr = isUptimeMode ? dataCollected ? idleValue.toString() : "TBD" : effectiveTarget.toString();
50776
51051
  if (index < totalRows - 1) {
50777
51052
  const rowBottomY = headerBottomY + (index + 1) * rowHeight;
50778
51053
  doc.setDrawColor(200, 200, 200);
@@ -50804,10 +51079,12 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
50804
51079
  } else {
50805
51080
  doc.text(outputStr, 75, yPos);
50806
51081
  doc.text(targetStr, 105, yPos);
51082
+ const remarkDisplay = remarkText ? doc.splitTextToSize(remarkText, 26)[0] : "";
51083
+ doc.text(remarkDisplay, 160, yPos);
50807
51084
  if (!dataCollected) {
50808
51085
  doc.setTextColor(100, 100, 100);
50809
51086
  doc.text("-", 135, yPos);
50810
- } else if (outputValue >= hourlyTarget) {
51087
+ } else if (outputValue >= effectiveTarget) {
50811
51088
  doc.setTextColor(0, 171, 69);
50812
51089
  doc.setFont("ZapfDingbats", "normal");
50813
51090
  doc.text("4", 135, yPos);
@@ -50821,11 +51098,10 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
50821
51098
  yPos += rowHeight;
50822
51099
  });
50823
51100
  const workspaceDisplayName = getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
50824
- const fileDate = new Date(workspace.date).toLocaleDateString("en-IN", {
51101
+ const fileDate = formatOperationalDateKey2(workspace.date, {
50825
51102
  day: "2-digit",
50826
51103
  month: "short",
50827
- year: "numeric",
50828
- timeZone: "Asia/Kolkata"
51104
+ year: "numeric"
50829
51105
  }).replace(/ /g, "_");
50830
51106
  const fileShift = shiftType.replace(/ /g, "_");
50831
51107
  const fileName = `${workspaceDisplayName}_${fileDate}_${fileShift}.pdf`;
@@ -50871,12 +51147,25 @@ var WorkspaceMonthlyPdfGenerator = ({
50871
51147
  availableShifts,
50872
51148
  shiftConfig,
50873
51149
  efficiencyLegend,
51150
+ trendSummary,
50874
51151
  className,
50875
51152
  compact = false,
50876
51153
  isAssemblyWorkspace = false
50877
51154
  }) => {
50878
51155
  const [isGenerating, setIsGenerating] = React143.useState(false);
50879
51156
  const effectiveLegend = efficiencyLegend || DEFAULT_EFFICIENCY_LEGEND;
51157
+ const drawStatusMark = (doc, x, y, met) => {
51158
+ doc.setLineWidth(0.8);
51159
+ if (met) {
51160
+ doc.setDrawColor(0, 171, 69);
51161
+ doc.line(x - 1.8, y - 0.2, x - 0.5, y + 1.2);
51162
+ doc.line(x - 0.5, y + 1.2, x + 2.1, y - 1.6);
51163
+ return;
51164
+ }
51165
+ doc.setDrawColor(227, 67, 41);
51166
+ doc.line(x - 1.8, y - 1.6, x + 1.8, y + 1.6);
51167
+ doc.line(x - 1.8, y + 1.6, x + 1.8, y - 1.6);
51168
+ };
50880
51169
  const generatePDF = async () => {
50881
51170
  setIsGenerating(true);
50882
51171
  try {
@@ -51071,12 +51360,13 @@ var WorkspaceMonthlyPdfGenerator = ({
51071
51360
  doc.setFont("helvetica", "bold");
51072
51361
  doc.text(`${outputMetrics.underperformingDays} of ${outputMetrics.totalDays}`, 120, kpiStartY + kpiSpacing * 3);
51073
51362
  } else {
51363
+ const medianCycleTime = Number.isFinite(trendSummary?.avg_cycle_time?.current) ? Number(trendSummary?.avg_cycle_time?.current) : outputMetrics.avgCycleTime;
51074
51364
  createKPIBox(kpiStartY);
51075
51365
  doc.setFontSize(11);
51076
51366
  doc.setFont("helvetica", "normal");
51077
51367
  doc.text("Average Cycle Time:", 25, kpiStartY);
51078
51368
  doc.setFont("helvetica", "bold");
51079
- doc.text(`${Math.round(outputMetrics.avgCycleTime)}s`, 120, kpiStartY);
51369
+ doc.text(`${medianCycleTime.toFixed(1)}s`, 120, kpiStartY);
51080
51370
  createKPIBox(kpiStartY + kpiSpacing);
51081
51371
  doc.setFont("helvetica", "normal");
51082
51372
  doc.text("Average Idle Time:", 25, kpiStartY + kpiSpacing);
@@ -51111,8 +51401,8 @@ var WorkspaceMonthlyPdfGenerator = ({
51111
51401
  doc.roundedRect(20, tableHeaderY, 170, 7, 1, 1, "F");
51112
51402
  const textY = tableHeaderY + 5;
51113
51403
  doc.text("Date", 25, textY);
51114
- doc.text(isUptimeMode ? "Productive" : isAssemblyWorkspace ? "Cycle Time" : "Actual", 60, textY);
51115
- doc.text(isUptimeMode ? "Idle" : isAssemblyWorkspace ? "Target CT" : "Standard", 95, textY);
51404
+ doc.text(isUptimeMode ? "Productive" : isAssemblyWorkspace ? "Actual Cycle Time" : "Actual", 60, textY);
51405
+ doc.text(isUptimeMode ? "Idle" : isAssemblyWorkspace ? "Standard Cycle Time" : "Standard", 95, textY);
51116
51406
  doc.text(isUptimeMode ? "Utilization" : "Efficiency", 135, textY);
51117
51407
  doc.text("Status", 170, textY);
51118
51408
  doc.setLineWidth(0.2);
@@ -51144,31 +51434,18 @@ var WorkspaceMonthlyPdfGenerator = ({
51144
51434
  doc.text(formatIdleTime(productiveSeconds), 60, yPos);
51145
51435
  doc.text(formatIdleTime(clampedIdleSeconds), 95, yPos);
51146
51436
  doc.text(`${utilization}%`, 135, yPos);
51147
- if (utilization >= effectiveLegend.green_min) {
51148
- doc.setTextColor(0, 171, 69);
51149
- doc.text("\u2713", 170, yPos);
51150
- } else {
51151
- doc.setTextColor(227, 67, 41);
51152
- doc.text("\xD7", 170, yPos);
51153
- }
51154
- doc.setTextColor(0, 0, 0);
51437
+ drawStatusMark(doc, 171, yPos - 0.3, utilization >= effectiveLegend.green_min);
51155
51438
  } else {
51156
51439
  if (isAssemblyWorkspace) {
51440
+ const targetCycleTime = Number.isFinite(shift.idealCycleTime) && Number(shift.idealCycleTime) > 0 ? Number(shift.idealCycleTime) : shift.pphThreshold > 0 ? 3600 / shift.pphThreshold : null;
51157
51441
  doc.text(`${shift.cycleTime.toFixed(1)}`, 60, yPos);
51158
- doc.text(`${shift.pphThreshold > 0 ? (3600 / shift.pphThreshold).toFixed(1) : "-"}`, 95, yPos);
51442
+ doc.text(targetCycleTime !== null ? `${targetCycleTime.toFixed(1)}` : "-", 95, yPos);
51159
51443
  } else {
51160
51444
  doc.text(`${shift.output}`, 60, yPos);
51161
51445
  doc.text(`${shift.targetOutput}`, 95, yPos);
51162
51446
  }
51163
51447
  doc.text(`${shift.efficiency.toFixed(1)}%`, 135, yPos);
51164
- if (shift.efficiency >= effectiveLegend.green_min) {
51165
- doc.setTextColor(0, 171, 69);
51166
- doc.text("\u2713", 170, yPos);
51167
- } else {
51168
- doc.setTextColor(227, 67, 41);
51169
- doc.text("\xD7", 170, yPos);
51170
- }
51171
- doc.setTextColor(0, 0, 0);
51448
+ drawStatusMark(doc, 171, yPos - 0.3, shift.efficiency >= effectiveLegend.green_min);
51172
51449
  }
51173
51450
  yPos += 8;
51174
51451
  });
@@ -58535,6 +58812,50 @@ var UserUsageStats = ({
58535
58812
  ] })
58536
58813
  ] });
58537
58814
  };
58815
+
58816
+ // src/lib/utils/teamUsage.ts
58817
+ var ISO_DATE_KEY_REGEX = /^\d{4}-\d{2}-\d{2}$/;
58818
+ function formatLocalDateKey(date) {
58819
+ const year = date.getFullYear();
58820
+ const month = String(date.getMonth() + 1).padStart(2, "0");
58821
+ const day = String(date.getDate()).padStart(2, "0");
58822
+ return `${year}-${month}-${day}`;
58823
+ }
58824
+ function parseUsageDate(dateLike) {
58825
+ if (ISO_DATE_KEY_REGEX.test(dateLike)) {
58826
+ const [year, month, day] = dateLike.split("-").map(Number);
58827
+ return new Date(year, month - 1, day);
58828
+ }
58829
+ return new Date(dateLike);
58830
+ }
58831
+ function normalizeUsageDateKey(dateLike) {
58832
+ if (!dateLike) return void 0;
58833
+ if (ISO_DATE_KEY_REGEX.test(dateLike)) return dateLike;
58834
+ const parsed = parseUsageDate(dateLike);
58835
+ if (Number.isNaN(parsed.getTime())) {
58836
+ return dateLike.split("T")[0] || dateLike;
58837
+ }
58838
+ return formatLocalDateKey(parsed);
58839
+ }
58840
+ function getCurrentWeekToDateUsageRange(today = /* @__PURE__ */ new Date()) {
58841
+ const normalizedToday = new Date(today);
58842
+ normalizedToday.setHours(0, 0, 0, 0);
58843
+ const dayOfWeek = normalizedToday.getDay();
58844
+ const daysSinceMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1;
58845
+ const monday = new Date(normalizedToday);
58846
+ monday.setDate(normalizedToday.getDate() - daysSinceMonday);
58847
+ return {
58848
+ startDate: formatLocalDateKey(monday),
58849
+ endDate: formatLocalDateKey(normalizedToday),
58850
+ daysElapsed: daysSinceMonday + 1
58851
+ };
58852
+ }
58853
+ function calculateAverageDailyDuration(durationMs, dayCount) {
58854
+ if (!Number.isFinite(durationMs) || dayCount <= 0) {
58855
+ return 0;
58856
+ }
58857
+ return Math.round(durationMs / dayCount);
58858
+ }
58538
58859
  var UserUsageDetailModal = ({
58539
58860
  userId,
58540
58861
  userName,
@@ -58544,7 +58865,7 @@ var UserUsageDetailModal = ({
58544
58865
  endDate
58545
58866
  }) => {
58546
58867
  const [currentWeekStart, setCurrentWeekStart] = React143.useState(() => {
58547
- if (startDate) return new Date(startDate);
58868
+ if (startDate) return parseUsageDate(startDate);
58548
58869
  const { start } = getCurrentWeekRange();
58549
58870
  return start;
58550
58871
  });
@@ -58554,8 +58875,8 @@ var UserUsageDetailModal = ({
58554
58875
  return end;
58555
58876
  }, [currentWeekStart]);
58556
58877
  const { data, isLoading, error } = useUserUsage(userId, {
58557
- startDate: currentWeekStart.toISOString().slice(0, 10),
58558
- endDate: currentWeekEnd.toISOString().slice(0, 10)
58878
+ startDate: formatLocalDateKey(currentWeekStart),
58879
+ endDate: formatLocalDateKey(currentWeekEnd)
58559
58880
  });
58560
58881
  const handlePrevWeek = () => {
58561
58882
  setCurrentWeekStart((prev) => {
@@ -58648,8 +58969,8 @@ function UsageContent({
58648
58969
  } else {
58649
58970
  daysToCount = 7;
58650
58971
  }
58651
- const weekTotalMs = weekData.reduce((sum, d) => sum + d.total_duration_ms, 0);
58652
- const weekAvgMs = daysToCount > 0 ? weekTotalMs / daysToCount : 0;
58972
+ const weekActiveMs = weekData.reduce((sum, d) => sum + d.active_duration_ms, 0);
58973
+ const weekAvgMs = calculateAverageDailyDuration(weekActiveMs, daysToCount);
58653
58974
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
58654
58975
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center mb-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 bg-gray-50 rounded-lg p-1 border border-gray-100", children: [
58655
58976
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -58661,7 +58982,7 @@ function UsageContent({
58661
58982
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { className: "w-4 h-4" })
58662
58983
  }
58663
58984
  ),
58664
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-gray-700 min-w-[140px] text-center", children: formatDateRange(currentWeekStart.toISOString().slice(0, 10), currentWeekEnd.toISOString().slice(0, 10)) }),
58985
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-gray-700 min-w-[140px] text-center", children: formatDateRange(formatLocalDateKey(currentWeekStart), formatLocalDateKey(currentWeekEnd)) }),
58665
58986
  /* @__PURE__ */ jsxRuntime.jsx(
58666
58987
  "button",
58667
58988
  {
@@ -58693,7 +59014,7 @@ function DailyBarChart({
58693
59014
  const weekData = React143.useMemo(() => buildWeekData(dailyData, getDateStringsInRange(weekStart, weekEnd)), [dailyData, weekStart, weekEnd]);
58694
59015
  const chartData = React143.useMemo(() => {
58695
59016
  return weekData.map((day, index) => {
58696
- const date = new Date(day.date);
59017
+ const date = parseUsageDate(day.date);
58697
59018
  return {
58698
59019
  ...day,
58699
59020
  index,
@@ -58703,7 +59024,7 @@ function DailyBarChart({
58703
59024
  // green-500
58704
59025
  passiveColor: "#fbbf24",
58705
59026
  // amber-400
58706
- isToday: day.date === (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)
59027
+ isToday: day.date === formatLocalDateKey(/* @__PURE__ */ new Date())
58707
59028
  };
58708
59029
  });
58709
59030
  }, [weekData]);
@@ -58723,7 +59044,7 @@ function DailyBarChart({
58723
59044
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
58724
59045
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-2 h-2 rounded-full bg-gray-300" }),
58725
59046
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm font-medium text-gray-600", children: [
58726
- "Average Daily usage: ",
59047
+ "Average Daily active usage: ",
58727
59048
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-900 font-semibold", children: avgDailyMs > 0 ? formatDuration2(avgDailyMs) : "0m" })
58728
59049
  ] })
58729
59050
  ] }),
@@ -58888,17 +59209,14 @@ function getCurrentWeekRange() {
58888
59209
  function getDateStringsInRange(start, end) {
58889
59210
  const result = [];
58890
59211
  for (let d = new Date(start); d <= end; d.setDate(d.getDate() + 1)) {
58891
- const year = d.getFullYear();
58892
- const month = String(d.getMonth() + 1).padStart(2, "0");
58893
- const day = String(d.getDate()).padStart(2, "0");
58894
- result.push(`${year}-${month}-${day}`);
59212
+ result.push(formatLocalDateKey(d));
58895
59213
  }
58896
59214
  return result;
58897
59215
  }
58898
59216
  function buildWeekData(dailyData, weekDates) {
58899
59217
  const dataMap = new Map(
58900
59218
  (dailyData || []).map((d) => {
58901
- const key = normalizeDateKey(d.date);
59219
+ const key = normalizeUsageDateKey(d.date);
58902
59220
  return [key, { ...d, date: key }];
58903
59221
  })
58904
59222
  );
@@ -58913,22 +59231,10 @@ function buildWeekData(dailyData, weekDates) {
58913
59231
  };
58914
59232
  });
58915
59233
  }
58916
- function normalizeDateKey(dateLike) {
58917
- if (!dateLike) return void 0;
58918
- try {
58919
- const date = new Date(dateLike);
58920
- const year = date.getFullYear();
58921
- const month = String(date.getMonth() + 1).padStart(2, "0");
58922
- const day = String(date.getDate()).padStart(2, "0");
58923
- return `${year}-${month}-${day}`;
58924
- } catch {
58925
- return dateLike.split("T")[0] || dateLike;
58926
- }
58927
- }
58928
59234
  function formatDateRange(startDate, endDate) {
58929
59235
  try {
58930
- const start = new Date(startDate);
58931
- const end = new Date(endDate);
59236
+ const start = parseUsageDate(startDate);
59237
+ const end = parseUsageDate(endDate);
58932
59238
  const opts = { month: "short", day: "numeric" };
58933
59239
  const yearOpts = { year: "numeric" };
58934
59240
  return `${start.toLocaleDateString("en-US", opts)} - ${end.toLocaleDateString("en-US", opts)}, ${end.toLocaleDateString("en-US", yearOpts)}`;
@@ -58939,7 +59245,7 @@ function formatDateRange(startDate, endDate) {
58939
59245
  function formatDate(dateStr) {
58940
59246
  if (!dateStr) return "";
58941
59247
  try {
58942
- const date = new Date(dateStr);
59248
+ const date = parseUsageDate(dateStr);
58943
59249
  return date.toLocaleDateString("en-US", { month: "short", day: "numeric" });
58944
59250
  } catch {
58945
59251
  return dateStr;
@@ -59217,7 +59523,7 @@ var UserManagementTable = ({
59217
59523
  }
59218
59524
  ),
59219
59525
  /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider", children: "Assignments" }),
59220
- showUsageStats && /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider", children: "Average Daily usage" }),
59526
+ showUsageStats && /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider", children: "Average Daily Active Usage" }),
59221
59527
  /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider", children: "Actions" })
59222
59528
  ] }) }),
59223
59529
  /* @__PURE__ */ jsxRuntime.jsx("tbody", { className: "bg-white divide-y divide-gray-200", children: filteredAndSortedUsers.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("tr", { children: /* @__PURE__ */ jsxRuntime.jsx("td", { colSpan: showUsageStats ? 5 : 4, className: "px-6 py-12", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center text-gray-400 gap-2", children: [
@@ -64334,7 +64640,17 @@ var KPIDetailView = ({
64334
64640
  )
64335
64641
  ] }),
64336
64642
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 ml-auto", children: [
64337
- resolvedLineInfo && activeTab === "overview" && /* @__PURE__ */ jsxRuntime.jsx(LinePdfGenerator, { lineInfo: resolvedLineInfo, workspaceData: resolvedWorkspaces || [], issueResolutionSummary, shiftName: getShiftName(resolvedLineInfo.shift_id) }),
64643
+ resolvedLineInfo && activeTab === "overview" && /* @__PURE__ */ jsxRuntime.jsx(
64644
+ LinePdfGenerator,
64645
+ {
64646
+ lineInfo: resolvedLineInfo,
64647
+ workspaceData: resolvedWorkspaces || [],
64648
+ issueResolutionSummary,
64649
+ shiftName: getShiftName(resolvedLineInfo.shift_id),
64650
+ shiftBreaks: shiftConfig?.shifts?.find((shift) => shift.shiftId === resolvedLineInfo.shift_id)?.breaks || [],
64651
+ reportTimezone: shiftConfig?.timezone || configuredTimezone
64652
+ }
64653
+ ),
64338
64654
  activeTab === "monthly_history" && !urlDate && !urlShift && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
64339
64655
  /* @__PURE__ */ jsxRuntime.jsx(
64340
64656
  MonthlyRangeFilter_default,
@@ -64363,6 +64679,8 @@ var KPIDetailView = ({
64363
64679
  rangeStart,
64364
64680
  rangeEnd,
64365
64681
  selectedShiftId,
64682
+ availableShifts: shiftConfig?.shifts?.map((shift) => ({ id: shift.shiftId, name: shift.shiftName })),
64683
+ lineAssembly: resolvedLineInfo?.assembly === true,
64366
64684
  compact: true
64367
64685
  }
64368
64686
  )
@@ -64541,7 +64859,7 @@ var KPIDetailViewWithDisplayNames = withSelectedLineDisplayNames(KPIDetailView);
64541
64859
  var KPIDetailView_default = KPIDetailViewWithDisplayNames;
64542
64860
  var isNonEmptyString = (value) => typeof value === "string" && value.trim().length > 0;
64543
64861
  var resolveCompanyId = (...candidates) => candidates.find(isNonEmptyString);
64544
- var parseTimeToMinutes2 = (value) => {
64862
+ var parseTimeToMinutes3 = (value) => {
64545
64863
  if (!value) return null;
64546
64864
  const [hourStr, minuteStr] = value.split(":");
64547
64865
  const hour = Number.parseInt(hourStr ?? "", 10);
@@ -64553,8 +64871,8 @@ var getShiftEndDate = (shift, timezone) => {
64553
64871
  if (!shift?.date) return null;
64554
64872
  const startTime = shift.startTime || "06:00";
64555
64873
  const endTime = shift.endTime || "18:00";
64556
- const startMinutes = parseTimeToMinutes2(startTime);
64557
- const endMinutes = parseTimeToMinutes2(endTime);
64874
+ const startMinutes = parseTimeToMinutes3(startTime);
64875
+ const endMinutes = parseTimeToMinutes3(endTime);
64558
64876
  if (startMinutes === null || endMinutes === null) return null;
64559
64877
  const shiftStartDate = dateFnsTz.fromZonedTime(`${shift.date}T${startTime}:00`, timezone);
64560
64878
  let durationMinutes = endMinutes - startMinutes;
@@ -64592,7 +64910,7 @@ var createKpisOverviewUrl = ({
64592
64910
  return queryString ? `/kpis?${queryString}` : "/kpis";
64593
64911
  };
64594
64912
  var getZonedDateAtMidday = (dateKey, timezone) => dateFnsTz.fromZonedTime(`${dateKey}T12:00:00`, timezone);
64595
- var formatDateKey = (dateKey, timezone, options) => {
64913
+ var formatDateKey2 = (dateKey, timezone, options) => {
64596
64914
  try {
64597
64915
  return new Intl.DateTimeFormat("en-US", {
64598
64916
  ...options,
@@ -65660,7 +65978,7 @@ var KPIsOverviewView = ({
65660
65978
  setActiveTab(newTab);
65661
65979
  }, [activeTab, leaderboardLines.length, lines.length]);
65662
65980
  const formatLocalDate2 = React143.useCallback((dateKey) => {
65663
- return formatDateKey(dateKey, configuredTimezone, {
65981
+ return formatDateKey2(dateKey, configuredTimezone, {
65664
65982
  year: "numeric",
65665
65983
  month: "long",
65666
65984
  day: "numeric"
@@ -65674,8 +65992,8 @@ var KPIsOverviewView = ({
65674
65992
  zonedNow.getMonth(),
65675
65993
  new Date(zonedNow.getFullYear(), zonedNow.getMonth() + 1, 0).getDate()
65676
65994
  );
65677
- const startLabel = formatDateKey(startOfMonthKey, configuredTimezone, { month: "short", day: "numeric" });
65678
- const endLabel = formatDateKey(endOfMonthKey, configuredTimezone, { month: "short", day: "numeric" });
65995
+ const startLabel = formatDateKey2(startOfMonthKey, configuredTimezone, { month: "short", day: "numeric" });
65996
+ const endLabel = formatDateKey2(endOfMonthKey, configuredTimezone, { month: "short", day: "numeric" });
65679
65997
  return `${startLabel} - ${endLabel}, ${zonedNow.getFullYear()}`;
65680
65998
  };
65681
65999
  const isMonthlyMode = activeTab === "leaderboard" && timeRange === "monthly";
@@ -71310,6 +71628,7 @@ var WorkspaceDetailView = ({
71310
71628
  efficiency: monitoringMode === "uptime" ? Number.isFinite(metric.avg_efficiency) ? Number(metric.avg_efficiency) : computedUptimeEfficiency : Number.isFinite(metric.avg_efficiency) ? Number(metric.avg_efficiency) : 0,
71311
71629
  output: metric.total_output || 0,
71312
71630
  cycleTime: metric.avg_cycle_time || 0,
71631
+ idealCycleTime: Number(metric.ideal_cycle_time || 0),
71313
71632
  pph: metric.avg_pph || 0,
71314
71633
  pphThreshold: metric.pph_threshold || 0,
71315
71634
  idealOutput: Number(metric.ideal_output || 0),
@@ -71865,7 +72184,9 @@ var WorkspaceDetailView = ({
71865
72184
  workspace,
71866
72185
  idleTimeReasons: idleTimeChartData,
71867
72186
  efficiencyLegend,
71868
- hourlyCycleTimes: cycleTimeChartData
72187
+ hourlyCycleTimes: cycleTimeChartData,
72188
+ shiftBreaks: shiftConfig?.shifts?.find((shift2) => shift2.shiftId === workspace.shift_id)?.breaks || [],
72189
+ reportTimezone: shiftConfig?.timezone || timezone
71869
72190
  }
71870
72191
  ) }),
71871
72192
  activeTab === "monthly_history" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
@@ -71900,6 +72221,7 @@ var WorkspaceDetailView = ({
71900
72221
  workspaceId,
71901
72222
  workspaceName: formattedWorkspaceName,
71902
72223
  monthlyData,
72224
+ analysisData: analysisMonthlyData,
71903
72225
  selectedMonth,
71904
72226
  selectedYear,
71905
72227
  monitoringMode: workspace?.monitoring_mode,
@@ -71908,7 +72230,10 @@ var WorkspaceDetailView = ({
71908
72230
  selectedShiftId: selectedShift,
71909
72231
  availableShifts: shiftConfig?.shifts?.map((s) => ({ id: s.shiftId, name: s.shiftName })),
71910
72232
  shiftConfig,
71911
- compact: true
72233
+ efficiencyLegend,
72234
+ trendSummary: workspaceMonthlyTrend,
72235
+ compact: true,
72236
+ isAssemblyWorkspace
71912
72237
  }
71913
72238
  )
71914
72239
  ] })
@@ -73495,39 +73820,8 @@ var TeamManagementView = ({
73495
73820
  const [users, setUsers] = React143.useState([]);
73496
73821
  const [availableLines, setAvailableLines] = React143.useState([]);
73497
73822
  const [availableFactories, setAvailableFactories] = React143.useState([]);
73498
- const [stats, setStats] = React143.useState({
73499
- totalUsers: 0,
73500
- owners: 0,
73501
- it: 0,
73502
- plantHeads: 0,
73503
- industrialEngineers: 0,
73504
- supervisors: 0,
73505
- optifye: 0
73506
- });
73823
+ const [usageSummaryByUser, setUsageSummaryByUser] = React143.useState({});
73507
73824
  const [isAddUserDialogOpen, setIsAddUserDialogOpen] = React143.useState(false);
73508
- const normalizeIds = React143.useCallback((value) => {
73509
- if (Array.isArray(value)) {
73510
- return value.filter((id3) => typeof id3 === "string" && id3.length > 0);
73511
- }
73512
- if (typeof value === "string" && value.length > 0) {
73513
- return [value];
73514
- }
73515
- return [];
73516
- }, []);
73517
- const factoryScopedRoleFactoryIds = React143.useMemo(() => {
73518
- if (!isFactoryScopedRole(user?.role_level)) return [];
73519
- const scopedFactoryIds = normalizeIds(user?.access_scope?.factory_ids);
73520
- if (scopedFactoryIds.length > 0) {
73521
- return Array.from(new Set(scopedFactoryIds));
73522
- }
73523
- const propertyFactoryIds = normalizeIds(
73524
- user?.properties?.factory_ids ?? user?.properties?.factory_id
73525
- );
73526
- if (propertyFactoryIds.length > 0) {
73527
- return Array.from(new Set(propertyFactoryIds));
73528
- }
73529
- return entityConfig?.factoryId ? [entityConfig.factoryId] : [];
73530
- }, [user, entityConfig?.factoryId, normalizeIds]);
73531
73825
  const notifyScopeRefresh = React143.useCallback(() => {
73532
73826
  if (typeof window !== "undefined") {
73533
73827
  window.dispatchEvent(new Event("rbac:refresh-scope"));
@@ -73536,37 +73830,17 @@ var TeamManagementView = ({
73536
73830
  const canAddUsers = canRoleManageUsers(user?.role_level);
73537
73831
  const canViewUsageStats = canRoleViewUsageStats(user?.role_level);
73538
73832
  const pageCompanyId = entityConfig?.companyId || user?.properties?.company_id;
73539
- const companyIdForUsage = pageCompanyId;
73540
- const usageDateRange = React143.useMemo(() => {
73541
- const today = /* @__PURE__ */ new Date();
73542
- const dayOfWeek = today.getDay();
73543
- const daysSinceMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1;
73544
- const monday = new Date(today);
73545
- monday.setDate(today.getDate() - daysSinceMonday);
73546
- return {
73547
- startDate: monday.toISOString().slice(0, 10),
73548
- endDate: today.toISOString().slice(0, 10),
73549
- daysElapsed: daysSinceMonday + 1
73550
- // Include today
73551
- };
73552
- }, []);
73553
- const {
73554
- data: usageData,
73555
- isLoading: isUsageLoading
73556
- } = useCompanyUsersUsage(canViewUsageStats ? companyIdForUsage : void 0, {
73557
- startDate: usageDateRange.startDate,
73558
- endDate: usageDateRange.endDate
73559
- });
73833
+ const usageDateRange = React143.useMemo(() => getCurrentWeekToDateUsageRange(), []);
73560
73834
  const avgDailyUsageMap = React143.useMemo(() => {
73561
- if (!usageData?.users) {
73835
+ if (!canViewUsageStats) {
73562
73836
  return {};
73563
73837
  }
73564
73838
  const daysElapsed = usageDateRange.daysElapsed;
73565
- return usageData.users.reduce((acc, user2) => {
73566
- acc[user2.user_id] = Math.round(user2.total_duration_ms / daysElapsed);
73839
+ return Object.entries(usageSummaryByUser).reduce((acc, [userId, usage]) => {
73840
+ acc[userId] = calculateAverageDailyDuration(usage.active_duration_ms, daysElapsed);
73567
73841
  return acc;
73568
73842
  }, {});
73569
- }, [usageData, usageDateRange.daysElapsed]);
73843
+ }, [canViewUsageStats, usageSummaryByUser, usageDateRange.daysElapsed]);
73570
73844
  const pageTitle = "Team Management";
73571
73845
  const pageDescription = canViewUsageStats ? "Manage team members and view their daily usage" : "Manage supervisors in your factory";
73572
73846
  const hasAccess = user ? user.role_level === void 0 || canRoleAccessTeamManagement(user.role_level) : false;
@@ -73590,7 +73864,6 @@ var TeamManagementView = ({
73590
73864
  setIsLoading(true);
73591
73865
  setError(void 0);
73592
73866
  try {
73593
- const userManagementService = createUserManagementService(supabase);
73594
73867
  const { data: { session } } = await supabase.auth.getSession();
73595
73868
  if (!session?.access_token) {
73596
73869
  throw new Error("No authentication token available");
@@ -73600,116 +73873,31 @@ var TeamManagementView = ({
73600
73873
  if (!backendUrl) {
73601
73874
  throw new Error("Backend URL is not configured. Please set NEXT_PUBLIC_BACKEND_URL in your environment.");
73602
73875
  }
73603
- if ((user?.role_level === "optifye" || user?.role_level === "owner" || user?.role_level === "it") && companyId) {
73604
- const { data: factories } = await supabase.from("factories").select("id, factory_name, company_id").eq("company_id", companyId).order("factory_name");
73605
- const linesResponse = await fetch(`${backendUrl}/api/lines?company_id=${companyId}`, {
73606
- headers: {
73607
- "Authorization": `Bearer ${token}`,
73608
- "Content-Type": "application/json"
73609
- }
73610
- });
73611
- if (!linesResponse.ok) {
73612
- throw new Error(`Failed to fetch lines: ${linesResponse.statusText}`);
73613
- }
73614
- const linesData = await linesResponse.json();
73615
- const lines = linesData.lines || [];
73616
- setAvailableFactories(factories || []);
73617
- setAvailableLines(lines);
73618
- console.log("[TeamManagementView] Company-scoped team view - Company:", companyId, "Loaded factories:", factories?.length, "lines:", lines?.length);
73619
- } else if (isFactoryScopedRole(user?.role_level)) {
73620
- if (factoryScopedRoleFactoryIds.length > 0) {
73621
- if (companyId) {
73622
- const linesResponse = await fetch(`${backendUrl}/api/lines?company_id=${companyId}`, {
73623
- headers: {
73624
- "Authorization": `Bearer ${token}`,
73625
- "Content-Type": "application/json"
73626
- }
73627
- });
73628
- if (!linesResponse.ok) {
73629
- throw new Error(`Failed to fetch lines: ${linesResponse.statusText}`);
73630
- }
73631
- const linesData = await linesResponse.json();
73632
- const allLines = linesData.lines || [];
73633
- const lines = allLines.filter((line) => factoryScopedRoleFactoryIds.includes(line.factory_id));
73634
- setAvailableLines(lines);
73635
- console.log("[TeamManagementView] Factory-scoped team view - Factories:", factoryScopedRoleFactoryIds, "Loaded lines:", lines?.length);
73636
- }
73637
- } else if (entityConfig?.factoryId) {
73638
- const linesResponse = await fetch(`${backendUrl}/api/lines?factory_id=${entityConfig.factoryId}`, {
73639
- headers: {
73640
- "Authorization": `Bearer ${token}`,
73641
- "Content-Type": "application/json"
73642
- }
73643
- });
73644
- if (!linesResponse.ok) {
73645
- throw new Error(`Failed to fetch lines: ${linesResponse.statusText}`);
73646
- }
73647
- const linesData = await linesResponse.json();
73648
- const lines = linesData.lines || [];
73649
- setAvailableLines(lines);
73650
- console.log("[TeamManagementView] Plant Head - Using entityConfig factory:", entityConfig.factoryId, "Loaded lines:", lines?.length);
73651
- } else {
73652
- setAvailableLines([]);
73653
- console.warn("[TeamManagementView] Plant Head has no factory assignments");
73654
- }
73655
- setAvailableFactories([]);
73656
- } else if (companyId) {
73657
- const linesResponse = await fetch(`${backendUrl}/api/lines?company_id=${companyId}`, {
73876
+ const params = new URLSearchParams({
73877
+ start_date: usageDateRange.startDate,
73878
+ end_date: usageDateRange.endDate
73879
+ });
73880
+ const bootstrapResponse = await fetch(
73881
+ `${backendUrl}/api/users/company/${companyId}/bootstrap?${params.toString()}`,
73882
+ {
73658
73883
  headers: {
73659
73884
  "Authorization": `Bearer ${token}`,
73660
73885
  "Content-Type": "application/json"
73661
73886
  }
73662
- });
73663
- if (!linesResponse.ok) {
73664
- throw new Error(`Failed to fetch lines: ${linesResponse.statusText}`);
73665
73887
  }
73666
- const linesData = await linesResponse.json();
73667
- const lines = linesData.lines || [];
73668
- setAvailableLines(lines);
73669
- setAvailableFactories([]);
73670
- console.log("[TeamManagementView] Fallback - Company:", companyId, "Loaded lines:", lines?.length);
73671
- }
73672
- const usersPromise = isFactoryScopedRole(user?.role_level) ? (async () => {
73673
- if (factoryScopedRoleFactoryIds.length === 0) {
73674
- return [];
73675
- }
73676
- const results = await Promise.allSettled(
73677
- factoryScopedRoleFactoryIds.map((factoryId) => userManagementService.getFactoryUsers(factoryId))
73678
- );
73679
- const successful = results.filter(
73680
- (result) => result.status === "fulfilled"
73681
- ).flatMap((result) => result.value || []);
73682
- if (successful.length > 0) {
73683
- const byUserId = /* @__PURE__ */ new Map();
73684
- successful.forEach((u) => {
73685
- if (u?.user_id) {
73686
- byUserId.set(u.user_id, u);
73687
- }
73688
- });
73689
- return Array.from(byUserId.values());
73690
- }
73691
- const firstRejected = results.find(
73692
- (result) => result.status === "rejected"
73693
- );
73694
- if (firstRejected) {
73695
- throw firstRejected.reason;
73696
- }
73697
- return [];
73698
- })() : userManagementService.getCompanyUsers(companyId);
73699
- const [usersData, userStats] = await Promise.all([
73700
- usersPromise,
73701
- userManagementService.getUserStats(companyId)
73702
- ]);
73703
- setUsers(usersData);
73704
- setStats({
73705
- totalUsers: userStats.total,
73706
- owners: userStats.owners,
73707
- it: userStats.it,
73708
- plantHeads: userStats.plant_heads,
73709
- industrialEngineers: userStats.industrial_engineers,
73710
- supervisors: userStats.supervisors,
73711
- optifye: 0
73712
- });
73888
+ );
73889
+ if (!bootstrapResponse.ok) {
73890
+ const errorData = await bootstrapResponse.json().catch(() => ({}));
73891
+ throw new Error(errorData.detail || `Failed to fetch team bootstrap: ${bootstrapResponse.statusText}`);
73892
+ }
73893
+ const bootstrapData = await bootstrapResponse.json();
73894
+ setAvailableFactories((bootstrapData.factories || []).map((factory) => ({
73895
+ id: factory.id,
73896
+ factory_name: factory.factory_name
73897
+ })));
73898
+ setAvailableLines(bootstrapData.lines || []);
73899
+ setUsers(bootstrapData.users || []);
73900
+ setUsageSummaryByUser(bootstrapData.usage_summary_by_user || {});
73713
73901
  } catch (err) {
73714
73902
  console.error("Error loading team management data:", err);
73715
73903
  setError(err instanceof Error ? err.message : "Failed to load data");
@@ -73717,7 +73905,7 @@ var TeamManagementView = ({
73717
73905
  } finally {
73718
73906
  setIsLoading(false);
73719
73907
  }
73720
- }, [supabase, user, pageCompanyId, entityConfig?.factoryId, factoryScopedRoleFactoryIds]);
73908
+ }, [supabase, user, pageCompanyId, usageDateRange.endDate, usageDateRange.startDate]);
73721
73909
  React143.useEffect(() => {
73722
73910
  const companyId = pageCompanyId;
73723
73911
  const canLoad = hasAccess && user && !!companyId;
@@ -73898,7 +74086,7 @@ var TeamManagementView = ({
73898
74086
  availableLines,
73899
74087
  availableFactories,
73900
74088
  avgDailyUsage: avgDailyUsageMap,
73901
- isUsageLoading,
74089
+ isUsageLoading: false,
73902
74090
  showUsageStats: canViewUsageStats
73903
74091
  }
73904
74092
  ) }),
@@ -80123,7 +80311,7 @@ var useOperationsOverviewRefresh = ({
80123
80311
  };
80124
80312
  }, [companyId, enabled, getIsPageActive, isLiveScope, lineIds.length, refreshFromResume, startPolling, stopPolling, supabase]);
80125
80313
  };
80126
- var parseTimeToMinutes3 = (value) => {
80314
+ var parseTimeToMinutes4 = (value) => {
80127
80315
  if (!value) return null;
80128
80316
  const parts = value.split(":");
80129
80317
  if (parts.length < 2) return null;
@@ -80158,8 +80346,8 @@ var classifyShiftBucket = ({
80158
80346
  return "day";
80159
80347
  }
80160
80348
  }
80161
- const startMinutes = parseTimeToMinutes3(startTime);
80162
- const endMinutes = parseTimeToMinutes3(endTime);
80349
+ const startMinutes = parseTimeToMinutes4(startTime);
80350
+ const endMinutes = parseTimeToMinutes4(endTime);
80163
80351
  if (startMinutes !== null) {
80164
80352
  if (startMinutes >= 4 * 60 && startMinutes < 18 * 60) return "day";
80165
80353
  return "night";
@@ -80218,8 +80406,8 @@ var getShiftWindowsForConfig = (shiftConfig, timezone) => {
80218
80406
  ];
80219
80407
  };
80220
80408
  var normalizeShiftWindowMinutes = (startTime, endTime) => {
80221
- const startMinutes = parseTimeToMinutes3(startTime);
80222
- const endMinutesRaw = parseTimeToMinutes3(endTime);
80409
+ const startMinutes = parseTimeToMinutes4(startTime);
80410
+ const endMinutesRaw = parseTimeToMinutes4(endTime);
80223
80411
  if (startMinutes === null || endMinutesRaw === null) {
80224
80412
  return null;
80225
80413
  }
@@ -80385,7 +80573,7 @@ var PlantHeadView = () => {
80385
80573
  startTime: shift.startTime,
80386
80574
  endTime: shift.endTime
80387
80575
  });
80388
- const startMinutes = parseTimeToMinutes3(shift.startTime);
80576
+ const startMinutes = parseTimeToMinutes4(shift.startTime);
80389
80577
  if (bucket === "day" && startMinutes !== null) {
80390
80578
  candidateStarts.push(startMinutes);
80391
80579
  }
@@ -80395,7 +80583,7 @@ var PlantHeadView = () => {
80395
80583
  scopedLineIds.forEach((lineId) => {
80396
80584
  const shiftConfig = shiftConfigMap.get(lineId) || staticShiftConfig;
80397
80585
  getShiftWindowsForConfig(shiftConfig).forEach((shift) => {
80398
- const startMinutes = parseTimeToMinutes3(shift.startTime);
80586
+ const startMinutes = parseTimeToMinutes4(shift.startTime);
80399
80587
  if (startMinutes !== null) {
80400
80588
  candidateStarts.push(startMinutes);
80401
80589
  }
@@ -80486,7 +80674,7 @@ var PlantHeadView = () => {
80486
80674
  startTime: shift.startTime,
80487
80675
  endTime: shift.endTime
80488
80676
  });
80489
- return bucket === "day" ? parseTimeToMinutes3(shift.startTime) : null;
80677
+ return bucket === "day" ? parseTimeToMinutes4(shift.startTime) : null;
80490
80678
  }).filter((value) => value !== null);
80491
80679
  }) : [];
80492
80680
  if (dayStartCandidates.length > 0) {