@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.mjs CHANGED
@@ -1638,6 +1638,7 @@ var DEFAULT_SHIFT_DATA = {
1638
1638
  efficiency: 0,
1639
1639
  output: 0,
1640
1640
  cycleTime: 0,
1641
+ idealCycleTime: 0,
1641
1642
  pph: 0,
1642
1643
  pphThreshold: 0,
1643
1644
  idealOutput: 0,
@@ -4484,6 +4485,7 @@ var dashboardService = {
4484
4485
  avg_efficiency: item.avg_efficiency || 0,
4485
4486
  total_output: item.total_output || 0,
4486
4487
  avg_cycle_time: item.avg_cycle_time || 0,
4488
+ ideal_cycle_time: item.ideal_cycle_time ?? 0,
4487
4489
  ideal_output: item.ideal_output || 0,
4488
4490
  total_day_output: item.total_day_output ?? item.ideal_output ?? 0,
4489
4491
  avg_pph: item.avg_pph || 0,
@@ -17280,7 +17282,7 @@ var useActiveBreaks = (lineIds) => {
17280
17282
  const [isLoading, setIsLoading] = useState(true);
17281
17283
  const [error, setError] = useState(null);
17282
17284
  const supabase = useSupabase();
17283
- const parseTimeToMinutes4 = (timeStr) => {
17285
+ const parseTimeToMinutes5 = (timeStr) => {
17284
17286
  const [hours, minutes] = timeStr.split(":").map(Number);
17285
17287
  return hours * 60 + minutes;
17286
17288
  };
@@ -17289,8 +17291,8 @@ var useActiveBreaks = (lineIds) => {
17289
17291
  return now4.getHours() * 60 + now4.getMinutes();
17290
17292
  };
17291
17293
  const isTimeInBreak = (breakStart, breakEnd, currentMinutes) => {
17292
- const startMinutes = parseTimeToMinutes4(breakStart);
17293
- const endMinutes = parseTimeToMinutes4(breakEnd);
17294
+ const startMinutes = parseTimeToMinutes5(breakStart);
17295
+ const endMinutes = parseTimeToMinutes5(breakEnd);
17294
17296
  if (endMinutes < startMinutes) {
17295
17297
  return currentMinutes >= startMinutes || currentMinutes < endMinutes;
17296
17298
  } else {
@@ -17298,8 +17300,8 @@ var useActiveBreaks = (lineIds) => {
17298
17300
  }
17299
17301
  };
17300
17302
  const calculateBreakProgress = (breakStart, breakEnd, currentMinutes) => {
17301
- const startMinutes = parseTimeToMinutes4(breakStart);
17302
- const endMinutes = parseTimeToMinutes4(breakEnd);
17303
+ const startMinutes = parseTimeToMinutes5(breakStart);
17304
+ const endMinutes = parseTimeToMinutes5(breakEnd);
17303
17305
  let elapsedMinutes = 0;
17304
17306
  let remainingMinutes = 0;
17305
17307
  if (endMinutes < startMinutes) {
@@ -17317,8 +17319,8 @@ var useActiveBreaks = (lineIds) => {
17317
17319
  return { elapsedMinutes, remainingMinutes };
17318
17320
  };
17319
17321
  const isTimeInShift = (startTime, endTime, currentMinutes) => {
17320
- const startMinutes = parseTimeToMinutes4(startTime);
17321
- const endMinutes = parseTimeToMinutes4(endTime);
17322
+ const startMinutes = parseTimeToMinutes5(startTime);
17323
+ const endMinutes = parseTimeToMinutes5(endTime);
17322
17324
  if (endMinutes < startMinutes) {
17323
17325
  return currentMinutes >= startMinutes || currentMinutes < endMinutes;
17324
17326
  } else {
@@ -17378,8 +17380,8 @@ var useActiveBreaks = (lineIds) => {
17378
17380
  const endTime = breakItem.end || breakItem.endTime || "00:00";
17379
17381
  let duration = breakItem.duration || 0;
17380
17382
  if (!duration || duration === 0) {
17381
- const startMinutes = parseTimeToMinutes4(startTime);
17382
- const endMinutes = parseTimeToMinutes4(endTime);
17383
+ const startMinutes = parseTimeToMinutes5(startTime);
17384
+ const endMinutes = parseTimeToMinutes5(endTime);
17383
17385
  duration = endMinutes < startMinutes ? endMinutes + 24 * 60 - startMinutes : endMinutes - startMinutes;
17384
17386
  }
17385
17387
  return {
@@ -17395,8 +17397,8 @@ var useActiveBreaks = (lineIds) => {
17395
17397
  const endTime = breakItem.end || breakItem.endTime || "00:00";
17396
17398
  let duration = breakItem.duration || 0;
17397
17399
  if (!duration || duration === 0) {
17398
- const startMinutes = parseTimeToMinutes4(startTime);
17399
- const endMinutes = parseTimeToMinutes4(endTime);
17400
+ const startMinutes = parseTimeToMinutes5(startTime);
17401
+ const endMinutes = parseTimeToMinutes5(endTime);
17400
17402
  duration = endMinutes < startMinutes ? endMinutes + 24 * 60 - startMinutes : endMinutes - startMinutes;
17401
17403
  }
17402
17404
  return {
@@ -33411,13 +33413,13 @@ var CycleTimeOverTimeChart = ({
33411
33413
  observer.observe(containerRef.current);
33412
33414
  return () => observer.disconnect();
33413
33415
  }, []);
33414
- const parseTimeToMinutes4 = (value) => {
33416
+ const parseTimeToMinutes5 = (value) => {
33415
33417
  const [hours, minutes] = value.split(":").map(Number);
33416
33418
  if (!Number.isFinite(hours) || !Number.isFinite(minutes)) return 0;
33417
33419
  return hours * 60 + minutes;
33418
33420
  };
33419
33421
  const formatHourLabel = (slotIndex) => {
33420
- const baseMinutes = parseTimeToMinutes4(shiftStart);
33422
+ const baseMinutes = parseTimeToMinutes5(shiftStart);
33421
33423
  const absoluteMinutes = baseMinutes + slotIndex * 60;
33422
33424
  const hour24 = Math.floor(absoluteMinutes % (24 * 60) / 60);
33423
33425
  const ampm = hour24 >= 12 ? "PM" : "AM";
@@ -47874,6 +47876,7 @@ var LineMonthlyPdfGenerator = ({
47874
47876
  rangeEnd,
47875
47877
  selectedShiftId,
47876
47878
  availableShifts,
47879
+ lineAssembly = false,
47877
47880
  compact = false,
47878
47881
  className
47879
47882
  }) => {
@@ -48157,8 +48160,8 @@ var LineMonthlyPdfGenerator = ({
48157
48160
  timeZone: "Asia/Kolkata"
48158
48161
  });
48159
48162
  doc.text(dateStr, 25, yPos);
48160
- doc.text(`${shift.total_workspaces - shift.underperforming_workspaces}`, 60, yPos);
48161
- doc.text(`${shift.total_workspaces}`, 95, yPos);
48163
+ doc.text(`${Math.round(shift.output || 0)}`, 60, yPos);
48164
+ doc.text(`${Math.round(shift.idealOutput || 0)}`, 95, yPos);
48162
48165
  doc.text(`${shift.avg_efficiency.toFixed(1)}%`, 135, yPos);
48163
48166
  const statusColor = getEfficiencyColor(shift.avg_efficiency, effectiveLegend);
48164
48167
  if (statusColor === "green") {
@@ -48188,6 +48191,8 @@ var LineMonthlyPdfGenerator = ({
48188
48191
  doc.setTextColor(0, 0, 0);
48189
48192
  }
48190
48193
  const poorestWorkspaces = underperformingWorkspaces[selectedShiftId] || [];
48194
+ 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);
48195
+ const showCycleTimePoorestPerformers = !isUptimeMode && poorestWorkspaces.some(isCycleTimeWorkspace);
48191
48196
  if (poorestWorkspaces && poorestWorkspaces.length > 0) {
48192
48197
  doc.addPage();
48193
48198
  doc.setFontSize(14);
@@ -48213,7 +48218,11 @@ var LineMonthlyPdfGenerator = ({
48213
48218
  doc.setFillColor(245, 245, 245);
48214
48219
  doc.roundedRect(20, 45, 170, 8, 1, 1, "F");
48215
48220
  doc.text("Workspace", 25, 50);
48216
- doc.text(isUptimeMode ? "Avg Utilization" : "Avg Efficiency", 120, 50);
48221
+ doc.text(
48222
+ isUptimeMode ? "Avg Utilization" : showCycleTimePoorestPerformers ? "Cycle Time" : "Avg Efficiency",
48223
+ 120,
48224
+ 50
48225
+ );
48217
48226
  doc.text("Last 5 Days", 160, 50);
48218
48227
  doc.setLineWidth(0.2);
48219
48228
  doc.setDrawColor(220, 220, 220);
@@ -48232,7 +48241,16 @@ var LineMonthlyPdfGenerator = ({
48232
48241
  );
48233
48242
  const workspaceName = rawWorkspaceName.length > 30 ? `${rawWorkspaceName.substring(0, 27)}...` : rawWorkspaceName;
48234
48243
  doc.text(workspaceName, 25, yPos2);
48235
- doc.text(`${(workspace.avg_efficiency || 0).toFixed(1)}%`, 120, yPos2);
48244
+ if (isUptimeMode) {
48245
+ doc.text(`${(workspace.avg_efficiency || 0).toFixed(1)}%`, 120, yPos2);
48246
+ } else if (isCycleTimeWorkspace(workspace)) {
48247
+ const actualCycleTime = Number.isFinite(workspace.avg_cycle_time) ? Number(workspace.avg_cycle_time) : null;
48248
+ const targetCycleTime = Number.isFinite(workspace.ideal_cycle_time) ? Number(workspace.ideal_cycle_time) : null;
48249
+ const cycleTimeText = actualCycleTime !== null ? `${actualCycleTime.toFixed(1)}s${targetCycleTime !== null ? ` / ${targetCycleTime.toFixed(1)}s` : ""}` : "-";
48250
+ doc.text(cycleTimeText, 120, yPos2);
48251
+ } else {
48252
+ doc.text(`${(workspace.avg_efficiency || 0).toFixed(1)}%`, 120, yPos2);
48253
+ }
48236
48254
  const squareSize = 3;
48237
48255
  const squareSpacing = 1;
48238
48256
  let squareX = 160;
@@ -48327,12 +48345,279 @@ Underperforming Workspaces: ${lineInfo.metrics.underperforming_workspaces} / ${l
48327
48345
  }
48328
48346
  );
48329
48347
  };
48348
+
48349
+ // src/lib/utils/hourlyTargets.ts
48350
+ var stripSeconds2 = (timeStr) => timeStr ? timeStr.slice(0, 5) : timeStr;
48351
+ var MINUTES_PER_DAY = 24 * 60;
48352
+ var parseTimeToMinutes2 = (timeString) => {
48353
+ const normalized = stripSeconds2(timeString || "");
48354
+ if (!normalized || !/^[0-2]\d:[0-5]\d$/.test(normalized)) return Number.NaN;
48355
+ const [hours, minutes] = normalized.split(":").map(Number);
48356
+ return hours * 60 + minutes;
48357
+ };
48358
+ var normalizeBreaksOnShiftTimeline = (shiftStart, breaks) => {
48359
+ const shiftStartMinutes = parseTimeToMinutes2(shiftStart);
48360
+ if (!Number.isFinite(shiftStartMinutes)) return [];
48361
+ const normalizedBreaks = [];
48362
+ for (const entry of breaks) {
48363
+ const startRaw = parseTimeToMinutes2(entry.startTime);
48364
+ const endRaw = parseTimeToMinutes2(entry.endTime);
48365
+ if (!Number.isFinite(startRaw) || !Number.isFinite(endRaw)) continue;
48366
+ let start = startRaw;
48367
+ let end = endRaw;
48368
+ if (end <= start) {
48369
+ end += 24 * 60;
48370
+ }
48371
+ if (start < shiftStartMinutes) {
48372
+ start += 24 * 60;
48373
+ end += 24 * 60;
48374
+ }
48375
+ const label = entry.remarks?.trim() || "Break";
48376
+ normalizedBreaks.push({ start, end, label });
48377
+ }
48378
+ return normalizedBreaks;
48379
+ };
48380
+ var roundTarget = (value, mode) => {
48381
+ if (!Number.isFinite(value)) return 0;
48382
+ switch (mode) {
48383
+ case "floor":
48384
+ return Math.floor(value);
48385
+ case "ceil":
48386
+ return Math.ceil(value);
48387
+ case "round":
48388
+ default:
48389
+ return Math.round(value);
48390
+ }
48391
+ };
48392
+ var formatDateKey = (date) => {
48393
+ const year = date.getUTCFullYear();
48394
+ const month = `${date.getUTCMonth() + 1}`.padStart(2, "0");
48395
+ const day = `${date.getUTCDate()}`.padStart(2, "0");
48396
+ return `${year}-${month}-${day}`;
48397
+ };
48398
+ var shiftDateKey = (dateKey, deltaDays) => {
48399
+ const [yearPart, monthPart, dayPart] = dateKey.split("-").map(Number);
48400
+ const year = Number.isFinite(yearPart) ? yearPart : 1970;
48401
+ const month = Number.isFinite(monthPart) ? monthPart : 1;
48402
+ const day = Number.isFinite(dayPart) ? dayPart : 1;
48403
+ const date = new Date(Date.UTC(year, month - 1, day));
48404
+ date.setUTCDate(date.getUTCDate() + deltaDays);
48405
+ return formatDateKey(date);
48406
+ };
48407
+ var getZonedNowSnapshot = (timeZone, now4) => {
48408
+ const formatter = new Intl.DateTimeFormat("en-US", {
48409
+ timeZone,
48410
+ year: "numeric",
48411
+ month: "2-digit",
48412
+ day: "2-digit",
48413
+ hour: "2-digit",
48414
+ minute: "2-digit",
48415
+ hourCycle: "h23"
48416
+ });
48417
+ const parts = formatter.formatToParts(now4).reduce((acc, part) => {
48418
+ if (part.type !== "literal") {
48419
+ acc[part.type] = part.value;
48420
+ }
48421
+ return acc;
48422
+ }, {});
48423
+ const year = Number(parts.year);
48424
+ const month = Number(parts.month);
48425
+ const day = Number(parts.day);
48426
+ const hour = Number(parts.hour);
48427
+ const minute = Number(parts.minute);
48428
+ return {
48429
+ dateKey: `${String(year).padStart(4, "0")}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`,
48430
+ minutesOfDay: (Number.isFinite(hour) ? hour : 0) * 60 + (Number.isFinite(minute) ? minute : 0)
48431
+ };
48432
+ };
48433
+ var getDateKeyInTimeZone = (timeZone, now4 = /* @__PURE__ */ new Date()) => getZonedNowSnapshot(timeZone, now4).dateKey;
48434
+ var buildHourlyIntervals = ({
48435
+ shiftStart,
48436
+ shiftEnd,
48437
+ bucketMinutes = 60,
48438
+ fallbackHours = 11
48439
+ }) => {
48440
+ const startMinutes = parseTimeToMinutes2(shiftStart);
48441
+ if (!Number.isFinite(startMinutes)) return [];
48442
+ const bucket = Number.isFinite(bucketMinutes) && bucketMinutes > 0 ? Math.floor(bucketMinutes) : 60;
48443
+ let totalMinutes;
48444
+ const endRaw = shiftEnd ? parseTimeToMinutes2(shiftEnd) : Number.NaN;
48445
+ if (!Number.isFinite(endRaw)) {
48446
+ totalMinutes = Math.max(0, Math.round(fallbackHours * 60));
48447
+ } else {
48448
+ let endMinutes = endRaw;
48449
+ if (endMinutes <= startMinutes) {
48450
+ endMinutes += 24 * 60;
48451
+ }
48452
+ totalMinutes = endMinutes - startMinutes;
48453
+ }
48454
+ if (!Number.isFinite(totalMinutes) || totalMinutes <= 0) return [];
48455
+ const count = Math.ceil(totalMinutes / bucket);
48456
+ const shiftEndMinutes = startMinutes + totalMinutes;
48457
+ const intervals = [];
48458
+ for (let i = 0; i < count; i += 1) {
48459
+ const start = startMinutes + i * bucket;
48460
+ const end = Math.min(start + bucket, shiftEndMinutes);
48461
+ const minutes = Math.max(0, end - start);
48462
+ if (minutes <= 0) continue;
48463
+ intervals.push({ start, end, minutes });
48464
+ }
48465
+ return intervals;
48466
+ };
48467
+ var computeBreakMinutesByInterval = ({
48468
+ intervals,
48469
+ shiftStart,
48470
+ breaks
48471
+ }) => {
48472
+ if (!intervals.length || !breaks.length) return intervals.map(() => 0);
48473
+ const normalizedBreaks = normalizeBreaksOnShiftTimeline(shiftStart, breaks);
48474
+ return intervals.map((interval) => {
48475
+ if (!normalizedBreaks.length) return 0;
48476
+ let total = 0;
48477
+ for (const brk of normalizedBreaks) {
48478
+ const overlap = Math.max(0, Math.min(interval.end, brk.end) - Math.max(interval.start, brk.start));
48479
+ total += overlap;
48480
+ if (total >= interval.minutes) return interval.minutes;
48481
+ }
48482
+ return Math.min(interval.minutes, total);
48483
+ });
48484
+ };
48485
+ var computeBreakRemarksByInterval = ({
48486
+ intervals,
48487
+ shiftStart,
48488
+ breaks
48489
+ }) => {
48490
+ if (!intervals.length || !breaks.length) return intervals.map(() => "");
48491
+ const normalizedBreaks = normalizeBreaksOnShiftTimeline(shiftStart, breaks);
48492
+ return intervals.map((interval) => {
48493
+ 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);
48494
+ return labels.join(", ");
48495
+ });
48496
+ };
48497
+ var computeEffectiveTargets = ({
48498
+ intervals,
48499
+ breakMinutes,
48500
+ pphThreshold,
48501
+ rounding = "round"
48502
+ }) => {
48503
+ return intervals.map((interval, idx) => {
48504
+ const intervalMinutes = Number(interval?.minutes) || 0;
48505
+ const breakMins = Number(breakMinutes?.[idx]) || 0;
48506
+ const plannedWorkMinutes = Math.max(0, intervalMinutes - breakMins);
48507
+ if (!Number.isFinite(pphThreshold) || pphThreshold <= 0) return 0;
48508
+ if (plannedWorkMinutes <= 0) return 0;
48509
+ return roundTarget(pphThreshold * plannedWorkMinutes / 60, rounding);
48510
+ });
48511
+ };
48512
+ var buildHourlyTargetPlan = ({
48513
+ shiftStart,
48514
+ shiftEnd,
48515
+ breaks = [],
48516
+ pphThreshold,
48517
+ bucketMinutes = 60,
48518
+ fallbackHours = 11,
48519
+ rounding = "round"
48520
+ }) => {
48521
+ const intervals = buildHourlyIntervals({
48522
+ shiftStart,
48523
+ shiftEnd,
48524
+ bucketMinutes,
48525
+ fallbackHours
48526
+ });
48527
+ const breakMinutes = computeBreakMinutesByInterval({
48528
+ intervals,
48529
+ shiftStart,
48530
+ breaks
48531
+ });
48532
+ const breakRemarks = computeBreakRemarksByInterval({
48533
+ intervals,
48534
+ shiftStart,
48535
+ breaks
48536
+ });
48537
+ const productiveMinutes = intervals.map((interval, idx) => Math.max(0, (Number(interval?.minutes) || 0) - (Number(breakMinutes[idx]) || 0)));
48538
+ const targets = computeEffectiveTargets({
48539
+ intervals,
48540
+ breakMinutes,
48541
+ pphThreshold,
48542
+ rounding
48543
+ });
48544
+ return {
48545
+ intervals,
48546
+ breakMinutes,
48547
+ breakRemarks,
48548
+ productiveMinutes,
48549
+ targets
48550
+ };
48551
+ };
48552
+ var isHourlyIntervalComplete = ({
48553
+ reportDate,
48554
+ shiftStart,
48555
+ shiftEnd,
48556
+ interval,
48557
+ timeZone = "Asia/Kolkata",
48558
+ now: now4 = /* @__PURE__ */ new Date()
48559
+ }) => {
48560
+ if (!reportDate) return true;
48561
+ const snapshot = getZonedNowSnapshot(timeZone, now4);
48562
+ const shiftStartMinutes = parseTimeToMinutes2(shiftStart);
48563
+ const shiftEndMinutes = shiftEnd ? parseTimeToMinutes2(shiftEnd) : Number.NaN;
48564
+ const wrapsMidnight = Number.isFinite(shiftStartMinutes) && Number.isFinite(shiftEndMinutes) && shiftEndMinutes <= shiftStartMinutes;
48565
+ if (reportDate === snapshot.dateKey) {
48566
+ return interval.end <= snapshot.minutesOfDay;
48567
+ }
48568
+ if (wrapsMidnight && reportDate === shiftDateKey(snapshot.dateKey, -1)) {
48569
+ return interval.end <= snapshot.minutesOfDay + MINUTES_PER_DAY;
48570
+ }
48571
+ return reportDate < snapshot.dateKey;
48572
+ };
48573
+ var isShiftInProgressForReportDate = ({
48574
+ reportDate,
48575
+ shiftStart,
48576
+ shiftEnd,
48577
+ timeZone = "Asia/Kolkata",
48578
+ now: now4 = /* @__PURE__ */ new Date()
48579
+ }) => {
48580
+ if (!reportDate || !shiftStart || !shiftEnd) return false;
48581
+ const shiftStartMinutes = parseTimeToMinutes2(shiftStart);
48582
+ const shiftEndMinutesRaw = parseTimeToMinutes2(shiftEnd);
48583
+ if (!Number.isFinite(shiftStartMinutes) || !Number.isFinite(shiftEndMinutesRaw)) {
48584
+ return false;
48585
+ }
48586
+ let shiftEndMinutes = shiftEndMinutesRaw;
48587
+ const wrapsMidnight = shiftEndMinutes <= shiftStartMinutes;
48588
+ if (wrapsMidnight) {
48589
+ shiftEndMinutes += MINUTES_PER_DAY;
48590
+ }
48591
+ const snapshot = getZonedNowSnapshot(timeZone, now4);
48592
+ let currentMinutes = null;
48593
+ if (reportDate === snapshot.dateKey) {
48594
+ currentMinutes = snapshot.minutesOfDay;
48595
+ } else if (wrapsMidnight && reportDate === shiftDateKey(snapshot.dateKey, -1)) {
48596
+ currentMinutes = snapshot.minutesOfDay + MINUTES_PER_DAY;
48597
+ }
48598
+ if (currentMinutes === null) {
48599
+ return false;
48600
+ }
48601
+ return shiftStartMinutes <= currentMinutes && currentMinutes < shiftEndMinutes;
48602
+ };
48603
+ var formatOperationalDateKey = (dateKey, options) => {
48604
+ const [yearPart, monthPart, dayPart] = dateKey.split("-").map(Number);
48605
+ const year = Number.isFinite(yearPart) ? yearPart : 1970;
48606
+ const month = Number.isFinite(monthPart) ? monthPart : 1;
48607
+ const day = Number.isFinite(dayPart) ? dayPart : 1;
48608
+ return new Date(Date.UTC(year, month - 1, day, 12)).toLocaleDateString("en-IN", {
48609
+ ...options,
48610
+ timeZone: "UTC"
48611
+ });
48612
+ };
48330
48613
  var LinePdfGenerator = ({
48331
48614
  lineInfo,
48332
48615
  workspaceData,
48333
48616
  issueResolutionSummary,
48334
48617
  shiftName,
48335
- className
48618
+ className,
48619
+ shiftBreaks = [],
48620
+ reportTimezone = "Asia/Kolkata"
48336
48621
  }) => {
48337
48622
  const [isGenerating, setIsGenerating] = useState(false);
48338
48623
  const formatResolutionDuration2 = (seconds) => {
@@ -48359,7 +48644,7 @@ var LinePdfGenerator = ({
48359
48644
  doc.setFontSize(9);
48360
48645
  doc.setFont("helvetica", "normal");
48361
48646
  doc.setTextColor(100, 100, 100);
48362
- const generatedText = `Generated on ${(/* @__PURE__ */ new Date()).toLocaleString("en-IN", { timeZone: "Asia/Kolkata" })}`;
48647
+ const generatedText = `Generated on ${(/* @__PURE__ */ new Date()).toLocaleString("en-IN", { timeZone: reportTimezone })}`;
48363
48648
  const generatedTextWidth = doc.getStringUnitWidth(generatedText) * 9 / doc.internal.scaleFactor;
48364
48649
  doc.text(generatedText, doc.internal.pageSize.width - 20 - generatedTextWidth, 15);
48365
48650
  doc.setDrawColor(200, 200, 200);
@@ -48377,11 +48662,10 @@ var LinePdfGenerator = ({
48377
48662
  const isUptimeMode = lineInfo.monitoring_mode === "uptime";
48378
48663
  const rawShiftType = shiftName || (lineInfo.shift_id === 0 ? "Day" : lineInfo.shift_id === 1 ? "Night" : `Shift ${lineInfo.shift_id}`);
48379
48664
  const shiftType = rawShiftType.toLowerCase().includes("shift") ? rawShiftType : `${rawShiftType} Shift`;
48380
- const date = new Date(lineInfo.date).toLocaleDateString("en-IN", {
48665
+ const date = formatOperationalDateKey(lineInfo.date, {
48381
48666
  weekday: "long",
48382
48667
  day: "numeric",
48383
- month: "long",
48384
- timeZone: "Asia/Kolkata"
48668
+ month: "long"
48385
48669
  });
48386
48670
  const shiftStartTime = lineInfo.metrics.shift_start ? (/* @__PURE__ */ new Date(`2000-01-01 ${lineInfo.metrics.shift_start}`)).toLocaleTimeString("en-IN", {
48387
48671
  hour: "2-digit",
@@ -48389,24 +48673,25 @@ var LinePdfGenerator = ({
48389
48673
  hour12: true,
48390
48674
  timeZone: "Asia/Kolkata"
48391
48675
  }) : "N/A";
48392
- const isToday2 = new Date(lineInfo.date).toDateString() === (/* @__PURE__ */ new Date()).toDateString();
48393
- let reportEndTime;
48394
- if (isToday2) {
48395
- reportEndTime = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-IN", {
48396
- hour: "2-digit",
48397
- minute: "2-digit",
48398
- timeZone: "Asia/Kolkata"
48399
- });
48400
- } else {
48401
- reportEndTime = lineInfo.metrics.shift_end ? (/* @__PURE__ */ new Date(`2000-01-01 ${lineInfo.metrics.shift_end}`)).toLocaleTimeString("en-IN", {
48402
- hour: "2-digit",
48403
- minute: "2-digit",
48404
- hour12: true,
48405
- timeZone: "Asia/Kolkata"
48406
- }) : "N/A";
48407
- }
48676
+ const currentTime = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-IN", {
48677
+ hour: "2-digit",
48678
+ minute: "2-digit",
48679
+ timeZone: reportTimezone
48680
+ });
48681
+ const reportEndTime = isShiftInProgressForReportDate({
48682
+ reportDate: lineInfo.date,
48683
+ shiftStart: lineInfo.metrics.shift_start || "",
48684
+ shiftEnd: lineInfo.metrics.shift_end,
48685
+ timeZone: reportTimezone
48686
+ }) ? currentTime : lineInfo.metrics.shift_end ? (/* @__PURE__ */ new Date(`2000-01-01 ${lineInfo.metrics.shift_end}`)).toLocaleTimeString("en-IN", {
48687
+ hour: "2-digit",
48688
+ minute: "2-digit",
48689
+ hour12: true,
48690
+ timeZone: "Asia/Kolkata"
48691
+ }) : "N/A";
48408
48692
  if (isUptimeMode) {
48409
48693
  const configuredTimezone = "Asia/Kolkata";
48694
+ const effectiveUptimeTimezone = reportTimezone || configuredTimezone;
48410
48695
  const lineShiftStart = lineInfo.metrics.shift_start || "06:00";
48411
48696
  const lineShiftEnd = lineInfo.metrics.shift_end || "14:00";
48412
48697
  const shiftMinutes = getShiftDurationMinutes(lineShiftStart, lineShiftEnd) || 0;
@@ -48420,7 +48705,7 @@ var LinePdfGenerator = ({
48420
48705
  shiftStart,
48421
48706
  shiftEnd,
48422
48707
  shiftDate,
48423
- timezone: configuredTimezone
48708
+ timezone: effectiveUptimeTimezone
48424
48709
  });
48425
48710
  let activeMinutes = uptimeSeries.activeMinutes;
48426
48711
  let idleMinutes = uptimeSeries.idleMinutes;
@@ -48501,7 +48786,7 @@ var LinePdfGenerator = ({
48501
48786
  shiftStart: lineShiftStart,
48502
48787
  shiftEnd: lineShiftEnd,
48503
48788
  shiftDate: lineInfo.date,
48504
- timezone: configuredTimezone
48789
+ timezone: effectiveUptimeTimezone
48505
48790
  });
48506
48791
  hourlyData = buildHourlyFromSeries(lineUptimeSeries);
48507
48792
  }
@@ -48609,13 +48894,13 @@ var LinePdfGenerator = ({
48609
48894
  doc.setFontSize(contentFontSize);
48610
48895
  doc.setFont("helvetica", "normal");
48611
48896
  let yPos2 = headerBottomY2 + 5.5;
48612
- const now5 = /* @__PURE__ */ new Date();
48613
- const currentTimeIST2 = new Date(now5.toLocaleString("en-US", { timeZone: configuredTimezone }));
48614
- const [sYear2, sMonth2, sDay2] = lineInfo.date.split("-").map(Number);
48615
- const [sStartH2, sStartM2] = (lineShiftStart || "06:00").split(":").map(Number);
48616
- const shiftStartBase2 = new Date(sYear2, sMonth2 - 1, sDay2, sStartH2, sStartM2 || 0);
48897
+ const now4 = /* @__PURE__ */ new Date();
48898
+ const currentTimeIST = new Date(now4.toLocaleString("en-US", { timeZone: configuredTimezone }));
48899
+ const [sYear, sMonth, sDay] = lineInfo.date.split("-").map(Number);
48900
+ const [sStartH, sStartM] = (lineShiftStart || "06:00").split(":").map(Number);
48901
+ const shiftStartBase = new Date(sYear, sMonth - 1, sDay, sStartH, sStartM || 0);
48617
48902
  hourlyData.forEach((entry, index) => {
48618
- const bucketStartTime = new Date(shiftStartBase2);
48903
+ const bucketStartTime = new Date(shiftStartBase);
48619
48904
  bucketStartTime.setHours(bucketStartTime.getHours() + index);
48620
48905
  const bucketEndTime = new Date(bucketStartTime);
48621
48906
  bucketEndTime.setHours(bucketEndTime.getHours() + 1);
@@ -48626,7 +48911,7 @@ var LinePdfGenerator = ({
48626
48911
  hour: "numeric",
48627
48912
  hour12: true
48628
48913
  })}`;
48629
- const dataCollected = bucketEndTime.getTime() <= currentTimeIST2.getTime();
48914
+ const dataCollected = bucketEndTime.getTime() <= currentTimeIST.getTime();
48630
48915
  if (index < totalRows2 - 1) {
48631
48916
  const rowBottomY = headerBottomY2 + (index + 1) * rowHeight;
48632
48917
  doc.setDrawColor(200, 200, 200);
@@ -48637,11 +48922,10 @@ var LinePdfGenerator = ({
48637
48922
  doc.text(utilizationStr, 147, yPos2);
48638
48923
  yPos2 += rowHeight;
48639
48924
  });
48640
- const fileDate2 = new Date(lineInfo.date).toLocaleDateString("en-IN", {
48925
+ const fileDate2 = formatOperationalDateKey(lineInfo.date, {
48641
48926
  day: "2-digit",
48642
48927
  month: "short",
48643
- year: "numeric",
48644
- timeZone: "Asia/Kolkata"
48928
+ year: "numeric"
48645
48929
  }).replace(/ /g, "_");
48646
48930
  const fileShift2 = shiftType.replace(/ /g, "_");
48647
48931
  const fileName2 = `${lineInfo.line_name}_${fileDate2}_${fileShift2}.pdf`;
@@ -48694,66 +48978,32 @@ var LinePdfGenerator = ({
48694
48978
  doc.setLineWidth(0.8);
48695
48979
  doc.line(20, 123, 190, 123);
48696
48980
  const hourlyOverviewStartY = 128;
48697
- const parseTimeToMinutes4 = (timeStr) => {
48698
- const [hours, minutes] = timeStr.split(":");
48699
- const hour = parseInt(hours, 10);
48700
- const minute = parseInt(minutes || "0", 10);
48701
- if (Number.isNaN(hour) || Number.isNaN(minute)) {
48702
- return NaN;
48703
- }
48704
- return (hour * 60 + minute) % (24 * 60);
48705
- };
48706
48981
  const formatMinutesLabel = (totalMinutes) => {
48707
48982
  const normalized = (totalMinutes % (24 * 60) + 24 * 60) % (24 * 60);
48708
48983
  const hour = Math.floor(normalized / 60);
48709
48984
  const minute = normalized % 60;
48710
- const time2 = /* @__PURE__ */ new Date();
48711
- time2.setHours(hour);
48712
- time2.setMinutes(minute);
48713
- time2.setSeconds(0);
48714
- time2.setMilliseconds(0);
48985
+ const time2 = new Date(Date.UTC(2e3, 0, 1, hour, minute));
48715
48986
  return time2.toLocaleTimeString("en-IN", {
48716
48987
  hour: "2-digit",
48717
48988
  minute: "2-digit",
48718
48989
  hour12: true,
48719
- timeZone: "Asia/Kolkata"
48720
- });
48721
- };
48722
- const buildRange = (startMinutes, minutes) => {
48723
- const endMinutes = startMinutes + minutes;
48724
- return {
48725
- label: `${formatMinutesLabel(startMinutes)} - ${formatMinutesLabel(endMinutes)}`,
48726
- minutes
48727
- };
48728
- };
48729
- const getHourlyTimeRanges = (startTimeStr, endTimeStr) => {
48730
- const startMinutes = parseTimeToMinutes4(startTimeStr);
48731
- if (Number.isNaN(startMinutes)) {
48732
- return [];
48733
- }
48734
- if (!endTimeStr) {
48735
- const defaultHours = 11;
48736
- return Array.from({ length: defaultHours }, (_, i) => buildRange(startMinutes + i * 60, 60));
48737
- }
48738
- const endMinutes = parseTimeToMinutes4(endTimeStr);
48739
- if (Number.isNaN(endMinutes)) {
48740
- const fallbackHours = 11;
48741
- return Array.from({ length: fallbackHours }, (_, i) => buildRange(startMinutes + i * 60, 60));
48742
- }
48743
- let durationMinutes = endMinutes - startMinutes;
48744
- if (durationMinutes <= 0) {
48745
- durationMinutes += 24 * 60;
48746
- }
48747
- const rangeCount = Math.max(1, Math.ceil(durationMinutes / 60));
48748
- return Array.from({ length: rangeCount }, (_, i) => {
48749
- const remainingMinutes = durationMinutes - i * 60;
48750
- const rangeMinutes = remainingMinutes >= 60 ? 60 : remainingMinutes;
48751
- return buildRange(startMinutes + i * 60, rangeMinutes);
48990
+ timeZone: "UTC"
48752
48991
  });
48753
48992
  };
48754
- const hourlyTimeRanges = lineInfo.metrics.shift_start ? getHourlyTimeRanges(lineInfo.metrics.shift_start, lineInfo.metrics.shift_end) : [];
48993
+ const hourlyTimeRanges = buildHourlyIntervals({
48994
+ shiftStart: lineInfo.metrics.shift_start || "06:00",
48995
+ shiftEnd: lineInfo.metrics.shift_end,
48996
+ fallbackHours: 11
48997
+ });
48998
+ const targetPlan = buildHourlyTargetPlan({
48999
+ shiftStart: lineInfo.metrics.shift_start || "06:00",
49000
+ shiftEnd: lineInfo.metrics.shift_end,
49001
+ breaks: shiftBreaks,
49002
+ pphThreshold: Number(lineInfo.metrics.threshold_pph ?? 0),
49003
+ fallbackHours: Math.max(hourlyTimeRanges.length, 1),
49004
+ rounding: "floor"
49005
+ });
48755
49006
  const shiftDuration = hourlyTimeRanges.length || 11;
48756
- const targetOutputPerHour = Math.round(lineInfo.metrics.threshold_pph ?? 0);
48757
49007
  let hourlyActualOutput = [];
48758
49008
  if (lineInfo.metrics.output_hourly && Object.keys(lineInfo.metrics.output_hourly).length > 0) {
48759
49009
  const [startHourStr, startMinuteStr] = (lineInfo.metrics.shift_start || "6:00").split(":");
@@ -48939,29 +49189,29 @@ var LinePdfGenerator = ({
48939
49189
  doc.text("Remarks", 160, tableHeaderY);
48940
49190
  doc.setFont("helvetica", "normal");
48941
49191
  let yPos = tableStartY;
48942
- const now4 = /* @__PURE__ */ new Date();
48943
- const currentTimeIST = new Date(now4.toLocaleString("en-US", { timeZone: "Asia/Kolkata" }));
48944
- const [sYear, sMonth, sDay] = lineInfo.date.split("-").map(Number);
48945
- const [sStartH, sStartM] = (lineInfo.metrics.shift_start || "06:00").split(":").map(Number);
48946
- const shiftStartBase = new Date(sYear, sMonth - 1, sDay, sStartH, sStartM || 0);
48947
49192
  hourlyTimeRanges.forEach((timeRange, index) => {
48948
49193
  const actualOutput = hourlyActualOutput[index] || 0;
48949
- const bucketStartTime = new Date(shiftStartBase);
48950
- bucketStartTime.setHours(bucketStartTime.getHours() + index);
48951
- const bucketEndTime = new Date(bucketStartTime.getTime() + timeRange.minutes * 60 * 1e3);
48952
- const dataCollected = bucketEndTime.getTime() <= currentTimeIST.getTime();
49194
+ const dataCollected = isHourlyIntervalComplete({
49195
+ reportDate: lineInfo.date,
49196
+ shiftStart: lineInfo.metrics.shift_start || "06:00",
49197
+ shiftEnd: lineInfo.metrics.shift_end,
49198
+ interval: timeRange,
49199
+ timeZone: reportTimezone
49200
+ });
48953
49201
  const outputStr = dataCollected ? actualOutput.toString() : "TBD";
48954
49202
  if (index < totalRows - 1) {
48955
49203
  const rowBottomY = headerBottomY + (index + 1) * rowSpacing;
48956
49204
  doc.setDrawColor(200, 200, 200);
48957
49205
  doc.line(20, rowBottomY, 190, rowBottomY);
48958
49206
  }
48959
- const rangeMinutes = timeRange.minutes || 60;
48960
- const targetForRange = targetOutputPerHour * (rangeMinutes / 60);
48961
- const targetStr = rangeMinutes === 60 ? targetOutputPerHour.toString() : targetForRange.toFixed(1).replace(/\.0$/, "");
48962
- doc.text(timeRange.label, 25, yPos);
49207
+ const targetForRange = targetPlan.targets[index] ?? 0;
49208
+ const targetStr = targetForRange.toString();
49209
+ const remarkText = targetPlan.breakRemarks[index] || "";
49210
+ doc.text(`${formatMinutesLabel(timeRange.start)} - ${formatMinutesLabel(timeRange.end)}`, 25, yPos);
48963
49211
  doc.text(outputStr, 75, yPos);
48964
49212
  doc.text(targetStr, 105, yPos);
49213
+ const remarkDisplay = remarkText ? doc.splitTextToSize(remarkText, 26)[0] : "";
49214
+ doc.text(remarkDisplay, 160, yPos);
48965
49215
  if (!dataCollected) {
48966
49216
  doc.setTextColor(100, 100, 100);
48967
49217
  doc.text("-", 135, yPos);
@@ -48977,11 +49227,10 @@ var LinePdfGenerator = ({
48977
49227
  doc.setTextColor(0, 0, 0);
48978
49228
  yPos += rowSpacing;
48979
49229
  });
48980
- const fileDate = new Date(lineInfo.date).toLocaleDateString("en-IN", {
49230
+ const fileDate = formatOperationalDateKey(lineInfo.date, {
48981
49231
  day: "2-digit",
48982
49232
  month: "short",
48983
- year: "numeric",
48984
- timeZone: "Asia/Kolkata"
49233
+ year: "numeric"
48985
49234
  }).replace(/ /g, "_");
48986
49235
  const fileShift = shiftType.replace(/ /g, "_");
48987
49236
  const fileName = `${lineInfo.line_name}_${fileDate}_${fileShift}.pdf`;
@@ -49909,6 +50158,13 @@ var WorkspaceMonthlyHistory = ({
49909
50158
  }, [analysisMonthlyData, selectedShiftId, isUptimeMode, shiftWorkSeconds]);
49910
50159
  const efficiencyDelta = trendSummary?.avg_efficiency?.delta_pp ?? 0;
49911
50160
  const efficiencyImproved = efficiencyDelta >= 0;
50161
+ const assemblyRangeCycleTime = useMemo(() => {
50162
+ const trendCycleTime = Number(trendSummary?.avg_cycle_time?.current);
50163
+ if (Number.isFinite(trendCycleTime) && trendCycleTime > 0) {
50164
+ return Math.round(trendCycleTime);
50165
+ }
50166
+ return metrics2?.avgCycleTime ?? 0;
50167
+ }, [trendSummary?.avg_cycle_time?.current, metrics2?.avgCycleTime]);
49912
50168
  const cycleDeltaRaw = trendSummary?.avg_cycle_time?.delta_seconds ?? 0;
49913
50169
  const cyclePrev = trendSummary?.avg_cycle_time?.previous ?? 0;
49914
50170
  const cycleDelta = cyclePrev ? cycleDeltaRaw / cyclePrev * 100 : 0;
@@ -50094,7 +50350,7 @@ var WorkspaceMonthlyHistory = ({
50094
50350
  /* @__PURE__ */ jsx("div", { className: "text-sm font-semibold text-gray-600 mb-1", children: "Avg Cycle Time" }),
50095
50351
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 flex-nowrap", children: [
50096
50352
  /* @__PURE__ */ jsxs("div", { className: "text-2xl font-bold text-gray-900", children: [
50097
- metrics2?.avgCycleTime ?? 0,
50353
+ assemblyRangeCycleTime,
50098
50354
  "s"
50099
50355
  ] }),
50100
50356
  /* @__PURE__ */ 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: [
@@ -50430,7 +50686,25 @@ var WorkspaceWhatsAppShareButton = ({
50430
50686
  }
50431
50687
  );
50432
50688
  };
50433
- var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiencyLegend, hourlyCycleTimes }) => {
50689
+ var formatOperationalDateKey2 = (dateKey, options) => {
50690
+ const [yearPart, monthPart, dayPart] = dateKey.split("-").map(Number);
50691
+ const year = Number.isFinite(yearPart) ? yearPart : 1970;
50692
+ const month = Number.isFinite(monthPart) ? monthPart : 1;
50693
+ const day = Number.isFinite(dayPart) ? dayPart : 1;
50694
+ return new Date(Date.UTC(year, month - 1, day, 12)).toLocaleDateString("en-IN", {
50695
+ ...options,
50696
+ timeZone: "UTC"
50697
+ });
50698
+ };
50699
+ var WorkspacePdfGenerator = ({
50700
+ workspace,
50701
+ className,
50702
+ idleTimeReasons,
50703
+ efficiencyLegend,
50704
+ hourlyCycleTimes,
50705
+ shiftBreaks = [],
50706
+ reportTimezone = "Asia/Kolkata"
50707
+ }) => {
50434
50708
  const [isGenerating, setIsGenerating] = useState(false);
50435
50709
  const entityConfig = useEntityConfig();
50436
50710
  const effectiveLegend = efficiencyLegend || DEFAULT_EFFICIENCY_LEGEND;
@@ -50460,7 +50734,7 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
50460
50734
  doc.setFontSize(9);
50461
50735
  doc.setFont("helvetica", "normal");
50462
50736
  doc.setTextColor(100, 100, 100);
50463
- const generatedText = `Generated on ${(/* @__PURE__ */ new Date()).toLocaleString("en-IN", { timeZone: "Asia/Kolkata" })}`;
50737
+ const generatedText = `Generated on ${(/* @__PURE__ */ new Date()).toLocaleString("en-IN", { timeZone: reportTimezone })}`;
50464
50738
  const generatedTextWidth = doc.getStringUnitWidth(generatedText) * 9 / doc.internal.scaleFactor;
50465
50739
  doc.text(generatedText, doc.internal.pageSize.width - 20 - generatedTextWidth, 15);
50466
50740
  doc.setDrawColor(200, 200, 200);
@@ -50479,11 +50753,10 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
50479
50753
  doc.setFontSize(13);
50480
50754
  doc.setFont("helvetica", "normal");
50481
50755
  doc.setTextColor(60, 60, 60);
50482
- const date = new Date(workspace.date).toLocaleDateString("en-IN", {
50756
+ const date = formatOperationalDateKey2(workspace.date, {
50483
50757
  weekday: "long",
50484
50758
  day: "numeric",
50485
- month: "long",
50486
- timeZone: "Asia/Kolkata"
50759
+ month: "long"
50487
50760
  });
50488
50761
  const rawShiftType = workspace.shift_type || (workspace.shift_id === 0 ? "Day" : workspace.shift_id === 1 ? "Night" : `Shift ${workspace.shift_id}`);
50489
50762
  const shiftType = rawShiftType.toLowerCase().includes("shift") ? rawShiftType : `${rawShiftType} Shift`;
@@ -50492,7 +50765,7 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
50492
50765
  const currentTime = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-IN", {
50493
50766
  hour: "2-digit",
50494
50767
  minute: "2-digit",
50495
- timeZone: "Asia/Kolkata"
50768
+ timeZone: reportTimezone
50496
50769
  });
50497
50770
  const shiftStartTime = (/* @__PURE__ */ new Date(`2000-01-01 ${workspace.shift_start}`)).toLocaleTimeString("en-IN", {
50498
50771
  hour: "2-digit",
@@ -50504,29 +50777,12 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
50504
50777
  minute: "2-digit",
50505
50778
  hour12: true
50506
50779
  });
50507
- const parseTimeToMinutes4 = (timeValue) => {
50508
- const [hourPart, minutePart] = timeValue.split(":").map(Number);
50509
- const hour = Number.isFinite(hourPart) ? hourPart : 0;
50510
- const minute = Number.isFinite(minutePart) ? minutePart : 0;
50511
- return hour * 60 + minute;
50512
- };
50513
- const toShiftUtcMs = (dateKey, timeValue) => {
50514
- const [yearPart, monthPart, dayPart] = dateKey.split("-").map(Number);
50515
- const year = Number.isFinite(yearPart) ? yearPart : 1970;
50516
- const month = Number.isFinite(monthPart) ? monthPart : 1;
50517
- const day = Number.isFinite(dayPart) ? dayPart : 1;
50518
- const [hourPart, minutePart] = timeValue.split(":").map(Number);
50519
- const hour = Number.isFinite(hourPart) ? hourPart : 0;
50520
- const minute = Number.isFinite(minutePart) ? minutePart : 0;
50521
- const IST_OFFSET_MINUTES = 330;
50522
- return Date.UTC(year, month - 1, day, hour, minute) - IST_OFFSET_MINUTES * 60 * 1e3;
50523
- };
50524
- const shiftStartMinutes = parseTimeToMinutes4(workspace.shift_start);
50525
- const shiftEndMinutes = parseTimeToMinutes4(workspace.shift_end);
50526
- const wrapsMidnight = shiftEndMinutes <= shiftStartMinutes;
50527
- const shiftStartUtcMs = toShiftUtcMs(workspace.date, workspace.shift_start);
50528
- const shiftEndUtcMs = toShiftUtcMs(workspace.date, workspace.shift_end) + (wrapsMidnight ? 24 * 60 * 60 * 1e3 : 0);
50529
- const isShiftInProgress = Date.now() >= shiftStartUtcMs && Date.now() < shiftEndUtcMs;
50780
+ const isShiftInProgress = isShiftInProgressForReportDate({
50781
+ reportDate: workspace.date,
50782
+ shiftStart: workspace.shift_start,
50783
+ shiftEnd: workspace.shift_end,
50784
+ timeZone: reportTimezone
50785
+ });
50530
50786
  const reportPeriodEndTime = isShiftInProgress ? currentTime : shiftEndTime;
50531
50787
  doc.setFontSize(12);
50532
50788
  doc.setTextColor(80, 80, 80);
@@ -50632,7 +50888,7 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
50632
50888
  shiftStart: workspace.shift_start,
50633
50889
  shiftEnd: workspace.shift_end,
50634
50890
  shiftDate: workspace.date,
50635
- timezone: "Asia/Kolkata"
50891
+ timezone: reportTimezone
50636
50892
  }) : null;
50637
50893
  const hourlyUptime = uptimeSeries?.points?.length ? Array.from({ length: Math.ceil(uptimeSeries.shiftMinutes / 60) }, (_, index) => {
50638
50894
  const start = index * 60;
@@ -50647,6 +50903,31 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
50647
50903
  const hourlyData = isUptimeMode ? hourlyUptime : isAssemblyCycleMode ? hourlyCycleTimes && hourlyCycleTimes.length > 0 ? hourlyCycleTimes : workspace.hourly_action_counts || [] : workspace.hourly_action_counts || [];
50648
50904
  const hourlyTarget = workspace.pph_threshold;
50649
50905
  const cycleTarget = workspace.ideal_cycle_time || 0;
50906
+ const hourlyIntervals = buildHourlyIntervals({
50907
+ shiftStart: workspace.shift_start,
50908
+ shiftEnd: workspace.shift_end,
50909
+ fallbackHours: Math.max(hourlyData.length, 1)
50910
+ });
50911
+ const outputTargetPlan = !isUptimeMode && !isAssemblyCycleMode ? buildHourlyTargetPlan({
50912
+ shiftStart: workspace.shift_start,
50913
+ shiftEnd: workspace.shift_end,
50914
+ breaks: shiftBreaks,
50915
+ pphThreshold: workspace.pph_threshold,
50916
+ fallbackHours: Math.max(hourlyData.length, 1),
50917
+ rounding: "floor"
50918
+ }) : null;
50919
+ const formatIntervalLabel = (totalMinutes) => {
50920
+ const normalized = (totalMinutes % (24 * 60) + 24 * 60) % (24 * 60);
50921
+ const hour = Math.floor(normalized / 60);
50922
+ const minute = normalized % 60;
50923
+ const time2 = new Date(Date.UTC(2e3, 0, 1, hour, minute));
50924
+ return time2.toLocaleTimeString("en-IN", {
50925
+ hour: "numeric",
50926
+ ...minute > 0 ? { minute: "2-digit" } : {},
50927
+ hour12: true,
50928
+ timeZone: "UTC"
50929
+ });
50930
+ };
50650
50931
  const pageHeight = doc.internal.pageSize.height;
50651
50932
  const maxContentY = pageHeight - 15;
50652
50933
  const baseTableStartY = hourlyPerfStartY + 31;
@@ -50714,36 +50995,30 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
50714
50995
  doc.setFontSize(contentFontSize);
50715
50996
  doc.setFont("helvetica", "normal");
50716
50997
  let yPos = headerBottomY + 5.5;
50717
- const workspaceDate = new Date(workspace.date);
50718
- const today = /* @__PURE__ */ new Date();
50719
- today.setHours(0, 0, 0, 0);
50720
- workspaceDate.setHours(0, 0, 0, 0);
50721
- const isToday2 = workspaceDate.getTime() === today.getTime();
50998
+ const isToday2 = getDateKeyInTimeZone(reportTimezone) === workspace.date;
50722
50999
  let currentHour = 24;
50723
51000
  if (isToday2) {
50724
51001
  const now4 = /* @__PURE__ */ new Date();
50725
- const currentTimeIST = new Date(now4.toLocaleString("en-US", { timeZone: "Asia/Kolkata" }));
50726
- currentHour = currentTimeIST.getHours();
51002
+ const currentTimeInReportTimezone = new Date(now4.toLocaleString("en-US", { timeZone: reportTimezone }));
51003
+ currentHour = currentTimeInReportTimezone.getHours();
50727
51004
  }
50728
51005
  hourlyData.forEach((entry, index) => {
50729
- const startTime = /* @__PURE__ */ new Date(`2000-01-01 ${workspace.shift_start}`);
50730
- startTime.setHours(startTime.getHours() + index);
50731
- const endTime = new Date(startTime);
50732
- endTime.setHours(endTime.getHours() + 1);
50733
- const timeRange = `${startTime.toLocaleTimeString("en-IN", {
50734
- hour: "numeric",
50735
- hour12: true
50736
- })} - ${endTime.toLocaleTimeString("en-IN", {
50737
- hour: "numeric",
50738
- hour12: true
50739
- })}`;
50740
- const hourNumber = startTime.getHours();
50741
- const dataCollected = !isToday2 || hourNumber < currentHour;
51006
+ const interval = hourlyIntervals[index];
51007
+ const timeRange = interval ? `${formatIntervalLabel(interval.start)} - ${formatIntervalLabel(interval.end)}` : `${index + 1}`;
51008
+ const dataCollected = interval ? isHourlyIntervalComplete({
51009
+ reportDate: workspace.date,
51010
+ shiftStart: workspace.shift_start,
51011
+ shiftEnd: workspace.shift_end,
51012
+ interval,
51013
+ timeZone: reportTimezone
51014
+ }) : !isToday2 || currentHour >= 24;
50742
51015
  const outputValue = isUptimeMode ? entry.activeMinutes ?? 0 : entry;
50743
51016
  const idleValue = isUptimeMode ? entry.idleMinutes ?? 0 : 0;
50744
51017
  const uptimePercent = isUptimeMode ? entry.uptimePercent ?? 0 : 0;
50745
51018
  const outputStr = dataCollected ? outputValue.toString() : "TBD";
50746
- const targetStr = isUptimeMode ? dataCollected ? idleValue.toString() : "TBD" : hourlyTarget.toString();
51019
+ const effectiveTarget = outputTargetPlan?.targets[index] ?? hourlyTarget;
51020
+ const remarkText = outputTargetPlan?.breakRemarks[index] || "";
51021
+ const targetStr = isUptimeMode ? dataCollected ? idleValue.toString() : "TBD" : effectiveTarget.toString();
50747
51022
  if (index < totalRows - 1) {
50748
51023
  const rowBottomY = headerBottomY + (index + 1) * rowHeight;
50749
51024
  doc.setDrawColor(200, 200, 200);
@@ -50775,10 +51050,12 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
50775
51050
  } else {
50776
51051
  doc.text(outputStr, 75, yPos);
50777
51052
  doc.text(targetStr, 105, yPos);
51053
+ const remarkDisplay = remarkText ? doc.splitTextToSize(remarkText, 26)[0] : "";
51054
+ doc.text(remarkDisplay, 160, yPos);
50778
51055
  if (!dataCollected) {
50779
51056
  doc.setTextColor(100, 100, 100);
50780
51057
  doc.text("-", 135, yPos);
50781
- } else if (outputValue >= hourlyTarget) {
51058
+ } else if (outputValue >= effectiveTarget) {
50782
51059
  doc.setTextColor(0, 171, 69);
50783
51060
  doc.setFont("ZapfDingbats", "normal");
50784
51061
  doc.text("4", 135, yPos);
@@ -50792,11 +51069,10 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
50792
51069
  yPos += rowHeight;
50793
51070
  });
50794
51071
  const workspaceDisplayName = getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
50795
- const fileDate = new Date(workspace.date).toLocaleDateString("en-IN", {
51072
+ const fileDate = formatOperationalDateKey2(workspace.date, {
50796
51073
  day: "2-digit",
50797
51074
  month: "short",
50798
- year: "numeric",
50799
- timeZone: "Asia/Kolkata"
51075
+ year: "numeric"
50800
51076
  }).replace(/ /g, "_");
50801
51077
  const fileShift = shiftType.replace(/ /g, "_");
50802
51078
  const fileName = `${workspaceDisplayName}_${fileDate}_${fileShift}.pdf`;
@@ -50842,12 +51118,25 @@ var WorkspaceMonthlyPdfGenerator = ({
50842
51118
  availableShifts,
50843
51119
  shiftConfig,
50844
51120
  efficiencyLegend,
51121
+ trendSummary,
50845
51122
  className,
50846
51123
  compact = false,
50847
51124
  isAssemblyWorkspace = false
50848
51125
  }) => {
50849
51126
  const [isGenerating, setIsGenerating] = useState(false);
50850
51127
  const effectiveLegend = efficiencyLegend || DEFAULT_EFFICIENCY_LEGEND;
51128
+ const drawStatusMark = (doc, x, y, met) => {
51129
+ doc.setLineWidth(0.8);
51130
+ if (met) {
51131
+ doc.setDrawColor(0, 171, 69);
51132
+ doc.line(x - 1.8, y - 0.2, x - 0.5, y + 1.2);
51133
+ doc.line(x - 0.5, y + 1.2, x + 2.1, y - 1.6);
51134
+ return;
51135
+ }
51136
+ doc.setDrawColor(227, 67, 41);
51137
+ doc.line(x - 1.8, y - 1.6, x + 1.8, y + 1.6);
51138
+ doc.line(x - 1.8, y + 1.6, x + 1.8, y - 1.6);
51139
+ };
50851
51140
  const generatePDF = async () => {
50852
51141
  setIsGenerating(true);
50853
51142
  try {
@@ -51042,12 +51331,13 @@ var WorkspaceMonthlyPdfGenerator = ({
51042
51331
  doc.setFont("helvetica", "bold");
51043
51332
  doc.text(`${outputMetrics.underperformingDays} of ${outputMetrics.totalDays}`, 120, kpiStartY + kpiSpacing * 3);
51044
51333
  } else {
51334
+ const medianCycleTime = Number.isFinite(trendSummary?.avg_cycle_time?.current) ? Number(trendSummary?.avg_cycle_time?.current) : outputMetrics.avgCycleTime;
51045
51335
  createKPIBox(kpiStartY);
51046
51336
  doc.setFontSize(11);
51047
51337
  doc.setFont("helvetica", "normal");
51048
51338
  doc.text("Average Cycle Time:", 25, kpiStartY);
51049
51339
  doc.setFont("helvetica", "bold");
51050
- doc.text(`${Math.round(outputMetrics.avgCycleTime)}s`, 120, kpiStartY);
51340
+ doc.text(`${medianCycleTime.toFixed(1)}s`, 120, kpiStartY);
51051
51341
  createKPIBox(kpiStartY + kpiSpacing);
51052
51342
  doc.setFont("helvetica", "normal");
51053
51343
  doc.text("Average Idle Time:", 25, kpiStartY + kpiSpacing);
@@ -51082,8 +51372,8 @@ var WorkspaceMonthlyPdfGenerator = ({
51082
51372
  doc.roundedRect(20, tableHeaderY, 170, 7, 1, 1, "F");
51083
51373
  const textY = tableHeaderY + 5;
51084
51374
  doc.text("Date", 25, textY);
51085
- doc.text(isUptimeMode ? "Productive" : isAssemblyWorkspace ? "Cycle Time" : "Actual", 60, textY);
51086
- doc.text(isUptimeMode ? "Idle" : isAssemblyWorkspace ? "Target CT" : "Standard", 95, textY);
51375
+ doc.text(isUptimeMode ? "Productive" : isAssemblyWorkspace ? "Actual Cycle Time" : "Actual", 60, textY);
51376
+ doc.text(isUptimeMode ? "Idle" : isAssemblyWorkspace ? "Standard Cycle Time" : "Standard", 95, textY);
51087
51377
  doc.text(isUptimeMode ? "Utilization" : "Efficiency", 135, textY);
51088
51378
  doc.text("Status", 170, textY);
51089
51379
  doc.setLineWidth(0.2);
@@ -51115,31 +51405,18 @@ var WorkspaceMonthlyPdfGenerator = ({
51115
51405
  doc.text(formatIdleTime(productiveSeconds), 60, yPos);
51116
51406
  doc.text(formatIdleTime(clampedIdleSeconds), 95, yPos);
51117
51407
  doc.text(`${utilization}%`, 135, yPos);
51118
- if (utilization >= effectiveLegend.green_min) {
51119
- doc.setTextColor(0, 171, 69);
51120
- doc.text("\u2713", 170, yPos);
51121
- } else {
51122
- doc.setTextColor(227, 67, 41);
51123
- doc.text("\xD7", 170, yPos);
51124
- }
51125
- doc.setTextColor(0, 0, 0);
51408
+ drawStatusMark(doc, 171, yPos - 0.3, utilization >= effectiveLegend.green_min);
51126
51409
  } else {
51127
51410
  if (isAssemblyWorkspace) {
51411
+ const targetCycleTime = Number.isFinite(shift.idealCycleTime) && Number(shift.idealCycleTime) > 0 ? Number(shift.idealCycleTime) : shift.pphThreshold > 0 ? 3600 / shift.pphThreshold : null;
51128
51412
  doc.text(`${shift.cycleTime.toFixed(1)}`, 60, yPos);
51129
- doc.text(`${shift.pphThreshold > 0 ? (3600 / shift.pphThreshold).toFixed(1) : "-"}`, 95, yPos);
51413
+ doc.text(targetCycleTime !== null ? `${targetCycleTime.toFixed(1)}` : "-", 95, yPos);
51130
51414
  } else {
51131
51415
  doc.text(`${shift.output}`, 60, yPos);
51132
51416
  doc.text(`${shift.targetOutput}`, 95, yPos);
51133
51417
  }
51134
51418
  doc.text(`${shift.efficiency.toFixed(1)}%`, 135, yPos);
51135
- if (shift.efficiency >= effectiveLegend.green_min) {
51136
- doc.setTextColor(0, 171, 69);
51137
- doc.text("\u2713", 170, yPos);
51138
- } else {
51139
- doc.setTextColor(227, 67, 41);
51140
- doc.text("\xD7", 170, yPos);
51141
- }
51142
- doc.setTextColor(0, 0, 0);
51419
+ drawStatusMark(doc, 171, yPos - 0.3, shift.efficiency >= effectiveLegend.green_min);
51143
51420
  }
51144
51421
  yPos += 8;
51145
51422
  });
@@ -58506,6 +58783,50 @@ var UserUsageStats = ({
58506
58783
  ] })
58507
58784
  ] });
58508
58785
  };
58786
+
58787
+ // src/lib/utils/teamUsage.ts
58788
+ var ISO_DATE_KEY_REGEX = /^\d{4}-\d{2}-\d{2}$/;
58789
+ function formatLocalDateKey(date) {
58790
+ const year = date.getFullYear();
58791
+ const month = String(date.getMonth() + 1).padStart(2, "0");
58792
+ const day = String(date.getDate()).padStart(2, "0");
58793
+ return `${year}-${month}-${day}`;
58794
+ }
58795
+ function parseUsageDate(dateLike) {
58796
+ if (ISO_DATE_KEY_REGEX.test(dateLike)) {
58797
+ const [year, month, day] = dateLike.split("-").map(Number);
58798
+ return new Date(year, month - 1, day);
58799
+ }
58800
+ return new Date(dateLike);
58801
+ }
58802
+ function normalizeUsageDateKey(dateLike) {
58803
+ if (!dateLike) return void 0;
58804
+ if (ISO_DATE_KEY_REGEX.test(dateLike)) return dateLike;
58805
+ const parsed = parseUsageDate(dateLike);
58806
+ if (Number.isNaN(parsed.getTime())) {
58807
+ return dateLike.split("T")[0] || dateLike;
58808
+ }
58809
+ return formatLocalDateKey(parsed);
58810
+ }
58811
+ function getCurrentWeekToDateUsageRange(today = /* @__PURE__ */ new Date()) {
58812
+ const normalizedToday = new Date(today);
58813
+ normalizedToday.setHours(0, 0, 0, 0);
58814
+ const dayOfWeek = normalizedToday.getDay();
58815
+ const daysSinceMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1;
58816
+ const monday = new Date(normalizedToday);
58817
+ monday.setDate(normalizedToday.getDate() - daysSinceMonday);
58818
+ return {
58819
+ startDate: formatLocalDateKey(monday),
58820
+ endDate: formatLocalDateKey(normalizedToday),
58821
+ daysElapsed: daysSinceMonday + 1
58822
+ };
58823
+ }
58824
+ function calculateAverageDailyDuration(durationMs, dayCount) {
58825
+ if (!Number.isFinite(durationMs) || dayCount <= 0) {
58826
+ return 0;
58827
+ }
58828
+ return Math.round(durationMs / dayCount);
58829
+ }
58509
58830
  var UserUsageDetailModal = ({
58510
58831
  userId,
58511
58832
  userName,
@@ -58515,7 +58836,7 @@ var UserUsageDetailModal = ({
58515
58836
  endDate
58516
58837
  }) => {
58517
58838
  const [currentWeekStart, setCurrentWeekStart] = useState(() => {
58518
- if (startDate) return new Date(startDate);
58839
+ if (startDate) return parseUsageDate(startDate);
58519
58840
  const { start } = getCurrentWeekRange();
58520
58841
  return start;
58521
58842
  });
@@ -58525,8 +58846,8 @@ var UserUsageDetailModal = ({
58525
58846
  return end;
58526
58847
  }, [currentWeekStart]);
58527
58848
  const { data, isLoading, error } = useUserUsage(userId, {
58528
- startDate: currentWeekStart.toISOString().slice(0, 10),
58529
- endDate: currentWeekEnd.toISOString().slice(0, 10)
58849
+ startDate: formatLocalDateKey(currentWeekStart),
58850
+ endDate: formatLocalDateKey(currentWeekEnd)
58530
58851
  });
58531
58852
  const handlePrevWeek = () => {
58532
58853
  setCurrentWeekStart((prev) => {
@@ -58619,8 +58940,8 @@ function UsageContent({
58619
58940
  } else {
58620
58941
  daysToCount = 7;
58621
58942
  }
58622
- const weekTotalMs = weekData.reduce((sum, d) => sum + d.total_duration_ms, 0);
58623
- const weekAvgMs = daysToCount > 0 ? weekTotalMs / daysToCount : 0;
58943
+ const weekActiveMs = weekData.reduce((sum, d) => sum + d.active_duration_ms, 0);
58944
+ const weekAvgMs = calculateAverageDailyDuration(weekActiveMs, daysToCount);
58624
58945
  return /* @__PURE__ */ jsxs("div", { children: [
58625
58946
  /* @__PURE__ */ jsx("div", { className: "flex justify-center mb-6", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 bg-gray-50 rounded-lg p-1 border border-gray-100", children: [
58626
58947
  /* @__PURE__ */ jsx(
@@ -58632,7 +58953,7 @@ function UsageContent({
58632
58953
  children: /* @__PURE__ */ jsx(ChevronLeft, { className: "w-4 h-4" })
58633
58954
  }
58634
58955
  ),
58635
- /* @__PURE__ */ 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)) }),
58956
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-gray-700 min-w-[140px] text-center", children: formatDateRange(formatLocalDateKey(currentWeekStart), formatLocalDateKey(currentWeekEnd)) }),
58636
58957
  /* @__PURE__ */ jsx(
58637
58958
  "button",
58638
58959
  {
@@ -58664,7 +58985,7 @@ function DailyBarChart({
58664
58985
  const weekData = useMemo(() => buildWeekData(dailyData, getDateStringsInRange(weekStart, weekEnd)), [dailyData, weekStart, weekEnd]);
58665
58986
  const chartData = useMemo(() => {
58666
58987
  return weekData.map((day, index) => {
58667
- const date = new Date(day.date);
58988
+ const date = parseUsageDate(day.date);
58668
58989
  return {
58669
58990
  ...day,
58670
58991
  index,
@@ -58674,7 +58995,7 @@ function DailyBarChart({
58674
58995
  // green-500
58675
58996
  passiveColor: "#fbbf24",
58676
58997
  // amber-400
58677
- isToday: day.date === (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)
58998
+ isToday: day.date === formatLocalDateKey(/* @__PURE__ */ new Date())
58678
58999
  };
58679
59000
  });
58680
59001
  }, [weekData]);
@@ -58694,7 +59015,7 @@ function DailyBarChart({
58694
59015
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
58695
59016
  /* @__PURE__ */ jsx("div", { className: "w-2 h-2 rounded-full bg-gray-300" }),
58696
59017
  /* @__PURE__ */ jsxs("span", { className: "text-sm font-medium text-gray-600", children: [
58697
- "Average Daily usage: ",
59018
+ "Average Daily active usage: ",
58698
59019
  /* @__PURE__ */ jsx("span", { className: "text-gray-900 font-semibold", children: avgDailyMs > 0 ? formatDuration2(avgDailyMs) : "0m" })
58699
59020
  ] })
58700
59021
  ] }),
@@ -58859,17 +59180,14 @@ function getCurrentWeekRange() {
58859
59180
  function getDateStringsInRange(start, end) {
58860
59181
  const result = [];
58861
59182
  for (let d = new Date(start); d <= end; d.setDate(d.getDate() + 1)) {
58862
- const year = d.getFullYear();
58863
- const month = String(d.getMonth() + 1).padStart(2, "0");
58864
- const day = String(d.getDate()).padStart(2, "0");
58865
- result.push(`${year}-${month}-${day}`);
59183
+ result.push(formatLocalDateKey(d));
58866
59184
  }
58867
59185
  return result;
58868
59186
  }
58869
59187
  function buildWeekData(dailyData, weekDates) {
58870
59188
  const dataMap = new Map(
58871
59189
  (dailyData || []).map((d) => {
58872
- const key = normalizeDateKey(d.date);
59190
+ const key = normalizeUsageDateKey(d.date);
58873
59191
  return [key, { ...d, date: key }];
58874
59192
  })
58875
59193
  );
@@ -58884,22 +59202,10 @@ function buildWeekData(dailyData, weekDates) {
58884
59202
  };
58885
59203
  });
58886
59204
  }
58887
- function normalizeDateKey(dateLike) {
58888
- if (!dateLike) return void 0;
58889
- try {
58890
- const date = new Date(dateLike);
58891
- const year = date.getFullYear();
58892
- const month = String(date.getMonth() + 1).padStart(2, "0");
58893
- const day = String(date.getDate()).padStart(2, "0");
58894
- return `${year}-${month}-${day}`;
58895
- } catch {
58896
- return dateLike.split("T")[0] || dateLike;
58897
- }
58898
- }
58899
59205
  function formatDateRange(startDate, endDate) {
58900
59206
  try {
58901
- const start = new Date(startDate);
58902
- const end = new Date(endDate);
59207
+ const start = parseUsageDate(startDate);
59208
+ const end = parseUsageDate(endDate);
58903
59209
  const opts = { month: "short", day: "numeric" };
58904
59210
  const yearOpts = { year: "numeric" };
58905
59211
  return `${start.toLocaleDateString("en-US", opts)} - ${end.toLocaleDateString("en-US", opts)}, ${end.toLocaleDateString("en-US", yearOpts)}`;
@@ -58910,7 +59216,7 @@ function formatDateRange(startDate, endDate) {
58910
59216
  function formatDate(dateStr) {
58911
59217
  if (!dateStr) return "";
58912
59218
  try {
58913
- const date = new Date(dateStr);
59219
+ const date = parseUsageDate(dateStr);
58914
59220
  return date.toLocaleDateString("en-US", { month: "short", day: "numeric" });
58915
59221
  } catch {
58916
59222
  return dateStr;
@@ -59188,7 +59494,7 @@ var UserManagementTable = ({
59188
59494
  }
59189
59495
  ),
59190
59496
  /* @__PURE__ */ jsx("th", { className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider", children: "Assignments" }),
59191
- showUsageStats && /* @__PURE__ */ jsx("th", { className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider", children: "Average Daily usage" }),
59497
+ showUsageStats && /* @__PURE__ */ jsx("th", { className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider", children: "Average Daily Active Usage" }),
59192
59498
  /* @__PURE__ */ jsx("th", { className: "px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider", children: "Actions" })
59193
59499
  ] }) }),
59194
59500
  /* @__PURE__ */ jsx("tbody", { className: "bg-white divide-y divide-gray-200", children: filteredAndSortedUsers.length === 0 ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan: showUsageStats ? 5 : 4, className: "px-6 py-12", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center text-gray-400 gap-2", children: [
@@ -64305,7 +64611,17 @@ var KPIDetailView = ({
64305
64611
  )
64306
64612
  ] }),
64307
64613
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 ml-auto", children: [
64308
- resolvedLineInfo && activeTab === "overview" && /* @__PURE__ */ jsx(LinePdfGenerator, { lineInfo: resolvedLineInfo, workspaceData: resolvedWorkspaces || [], issueResolutionSummary, shiftName: getShiftName(resolvedLineInfo.shift_id) }),
64614
+ resolvedLineInfo && activeTab === "overview" && /* @__PURE__ */ jsx(
64615
+ LinePdfGenerator,
64616
+ {
64617
+ lineInfo: resolvedLineInfo,
64618
+ workspaceData: resolvedWorkspaces || [],
64619
+ issueResolutionSummary,
64620
+ shiftName: getShiftName(resolvedLineInfo.shift_id),
64621
+ shiftBreaks: shiftConfig?.shifts?.find((shift) => shift.shiftId === resolvedLineInfo.shift_id)?.breaks || [],
64622
+ reportTimezone: shiftConfig?.timezone || configuredTimezone
64623
+ }
64624
+ ),
64309
64625
  activeTab === "monthly_history" && !urlDate && !urlShift && /* @__PURE__ */ jsxs(Fragment, { children: [
64310
64626
  /* @__PURE__ */ jsx(
64311
64627
  MonthlyRangeFilter_default,
@@ -64334,6 +64650,8 @@ var KPIDetailView = ({
64334
64650
  rangeStart,
64335
64651
  rangeEnd,
64336
64652
  selectedShiftId,
64653
+ availableShifts: shiftConfig?.shifts?.map((shift) => ({ id: shift.shiftId, name: shift.shiftName })),
64654
+ lineAssembly: resolvedLineInfo?.assembly === true,
64337
64655
  compact: true
64338
64656
  }
64339
64657
  )
@@ -64512,7 +64830,7 @@ var KPIDetailViewWithDisplayNames = withSelectedLineDisplayNames(KPIDetailView);
64512
64830
  var KPIDetailView_default = KPIDetailViewWithDisplayNames;
64513
64831
  var isNonEmptyString = (value) => typeof value === "string" && value.trim().length > 0;
64514
64832
  var resolveCompanyId = (...candidates) => candidates.find(isNonEmptyString);
64515
- var parseTimeToMinutes2 = (value) => {
64833
+ var parseTimeToMinutes3 = (value) => {
64516
64834
  if (!value) return null;
64517
64835
  const [hourStr, minuteStr] = value.split(":");
64518
64836
  const hour = Number.parseInt(hourStr ?? "", 10);
@@ -64524,8 +64842,8 @@ var getShiftEndDate = (shift, timezone) => {
64524
64842
  if (!shift?.date) return null;
64525
64843
  const startTime = shift.startTime || "06:00";
64526
64844
  const endTime = shift.endTime || "18:00";
64527
- const startMinutes = parseTimeToMinutes2(startTime);
64528
- const endMinutes = parseTimeToMinutes2(endTime);
64845
+ const startMinutes = parseTimeToMinutes3(startTime);
64846
+ const endMinutes = parseTimeToMinutes3(endTime);
64529
64847
  if (startMinutes === null || endMinutes === null) return null;
64530
64848
  const shiftStartDate = fromZonedTime(`${shift.date}T${startTime}:00`, timezone);
64531
64849
  let durationMinutes = endMinutes - startMinutes;
@@ -64563,7 +64881,7 @@ var createKpisOverviewUrl = ({
64563
64881
  return queryString ? `/kpis?${queryString}` : "/kpis";
64564
64882
  };
64565
64883
  var getZonedDateAtMidday = (dateKey, timezone) => fromZonedTime(`${dateKey}T12:00:00`, timezone);
64566
- var formatDateKey = (dateKey, timezone, options) => {
64884
+ var formatDateKey2 = (dateKey, timezone, options) => {
64567
64885
  try {
64568
64886
  return new Intl.DateTimeFormat("en-US", {
64569
64887
  ...options,
@@ -65631,7 +65949,7 @@ var KPIsOverviewView = ({
65631
65949
  setActiveTab(newTab);
65632
65950
  }, [activeTab, leaderboardLines.length, lines.length]);
65633
65951
  const formatLocalDate2 = useCallback((dateKey) => {
65634
- return formatDateKey(dateKey, configuredTimezone, {
65952
+ return formatDateKey2(dateKey, configuredTimezone, {
65635
65953
  year: "numeric",
65636
65954
  month: "long",
65637
65955
  day: "numeric"
@@ -65645,8 +65963,8 @@ var KPIsOverviewView = ({
65645
65963
  zonedNow.getMonth(),
65646
65964
  new Date(zonedNow.getFullYear(), zonedNow.getMonth() + 1, 0).getDate()
65647
65965
  );
65648
- const startLabel = formatDateKey(startOfMonthKey, configuredTimezone, { month: "short", day: "numeric" });
65649
- const endLabel = formatDateKey(endOfMonthKey, configuredTimezone, { month: "short", day: "numeric" });
65966
+ const startLabel = formatDateKey2(startOfMonthKey, configuredTimezone, { month: "short", day: "numeric" });
65967
+ const endLabel = formatDateKey2(endOfMonthKey, configuredTimezone, { month: "short", day: "numeric" });
65650
65968
  return `${startLabel} - ${endLabel}, ${zonedNow.getFullYear()}`;
65651
65969
  };
65652
65970
  const isMonthlyMode = activeTab === "leaderboard" && timeRange === "monthly";
@@ -71281,6 +71599,7 @@ var WorkspaceDetailView = ({
71281
71599
  efficiency: monitoringMode === "uptime" ? Number.isFinite(metric.avg_efficiency) ? Number(metric.avg_efficiency) : computedUptimeEfficiency : Number.isFinite(metric.avg_efficiency) ? Number(metric.avg_efficiency) : 0,
71282
71600
  output: metric.total_output || 0,
71283
71601
  cycleTime: metric.avg_cycle_time || 0,
71602
+ idealCycleTime: Number(metric.ideal_cycle_time || 0),
71284
71603
  pph: metric.avg_pph || 0,
71285
71604
  pphThreshold: metric.pph_threshold || 0,
71286
71605
  idealOutput: Number(metric.ideal_output || 0),
@@ -71836,7 +72155,9 @@ var WorkspaceDetailView = ({
71836
72155
  workspace,
71837
72156
  idleTimeReasons: idleTimeChartData,
71838
72157
  efficiencyLegend,
71839
- hourlyCycleTimes: cycleTimeChartData
72158
+ hourlyCycleTimes: cycleTimeChartData,
72159
+ shiftBreaks: shiftConfig?.shifts?.find((shift2) => shift2.shiftId === workspace.shift_id)?.breaks || [],
72160
+ reportTimezone: shiftConfig?.timezone || timezone
71840
72161
  }
71841
72162
  ) }),
71842
72163
  activeTab === "monthly_history" && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
@@ -71871,6 +72192,7 @@ var WorkspaceDetailView = ({
71871
72192
  workspaceId,
71872
72193
  workspaceName: formattedWorkspaceName,
71873
72194
  monthlyData,
72195
+ analysisData: analysisMonthlyData,
71874
72196
  selectedMonth,
71875
72197
  selectedYear,
71876
72198
  monitoringMode: workspace?.monitoring_mode,
@@ -71879,7 +72201,10 @@ var WorkspaceDetailView = ({
71879
72201
  selectedShiftId: selectedShift,
71880
72202
  availableShifts: shiftConfig?.shifts?.map((s) => ({ id: s.shiftId, name: s.shiftName })),
71881
72203
  shiftConfig,
71882
- compact: true
72204
+ efficiencyLegend,
72205
+ trendSummary: workspaceMonthlyTrend,
72206
+ compact: true,
72207
+ isAssemblyWorkspace
71883
72208
  }
71884
72209
  )
71885
72210
  ] })
@@ -73466,39 +73791,8 @@ var TeamManagementView = ({
73466
73791
  const [users, setUsers] = useState([]);
73467
73792
  const [availableLines, setAvailableLines] = useState([]);
73468
73793
  const [availableFactories, setAvailableFactories] = useState([]);
73469
- const [stats, setStats] = useState({
73470
- totalUsers: 0,
73471
- owners: 0,
73472
- it: 0,
73473
- plantHeads: 0,
73474
- industrialEngineers: 0,
73475
- supervisors: 0,
73476
- optifye: 0
73477
- });
73794
+ const [usageSummaryByUser, setUsageSummaryByUser] = useState({});
73478
73795
  const [isAddUserDialogOpen, setIsAddUserDialogOpen] = useState(false);
73479
- const normalizeIds = useCallback((value) => {
73480
- if (Array.isArray(value)) {
73481
- return value.filter((id3) => typeof id3 === "string" && id3.length > 0);
73482
- }
73483
- if (typeof value === "string" && value.length > 0) {
73484
- return [value];
73485
- }
73486
- return [];
73487
- }, []);
73488
- const factoryScopedRoleFactoryIds = useMemo(() => {
73489
- if (!isFactoryScopedRole(user?.role_level)) return [];
73490
- const scopedFactoryIds = normalizeIds(user?.access_scope?.factory_ids);
73491
- if (scopedFactoryIds.length > 0) {
73492
- return Array.from(new Set(scopedFactoryIds));
73493
- }
73494
- const propertyFactoryIds = normalizeIds(
73495
- user?.properties?.factory_ids ?? user?.properties?.factory_id
73496
- );
73497
- if (propertyFactoryIds.length > 0) {
73498
- return Array.from(new Set(propertyFactoryIds));
73499
- }
73500
- return entityConfig?.factoryId ? [entityConfig.factoryId] : [];
73501
- }, [user, entityConfig?.factoryId, normalizeIds]);
73502
73796
  const notifyScopeRefresh = useCallback(() => {
73503
73797
  if (typeof window !== "undefined") {
73504
73798
  window.dispatchEvent(new Event("rbac:refresh-scope"));
@@ -73507,37 +73801,17 @@ var TeamManagementView = ({
73507
73801
  const canAddUsers = canRoleManageUsers(user?.role_level);
73508
73802
  const canViewUsageStats = canRoleViewUsageStats(user?.role_level);
73509
73803
  const pageCompanyId = entityConfig?.companyId || user?.properties?.company_id;
73510
- const companyIdForUsage = pageCompanyId;
73511
- const usageDateRange = useMemo(() => {
73512
- const today = /* @__PURE__ */ new Date();
73513
- const dayOfWeek = today.getDay();
73514
- const daysSinceMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1;
73515
- const monday = new Date(today);
73516
- monday.setDate(today.getDate() - daysSinceMonday);
73517
- return {
73518
- startDate: monday.toISOString().slice(0, 10),
73519
- endDate: today.toISOString().slice(0, 10),
73520
- daysElapsed: daysSinceMonday + 1
73521
- // Include today
73522
- };
73523
- }, []);
73524
- const {
73525
- data: usageData,
73526
- isLoading: isUsageLoading
73527
- } = useCompanyUsersUsage(canViewUsageStats ? companyIdForUsage : void 0, {
73528
- startDate: usageDateRange.startDate,
73529
- endDate: usageDateRange.endDate
73530
- });
73804
+ const usageDateRange = useMemo(() => getCurrentWeekToDateUsageRange(), []);
73531
73805
  const avgDailyUsageMap = useMemo(() => {
73532
- if (!usageData?.users) {
73806
+ if (!canViewUsageStats) {
73533
73807
  return {};
73534
73808
  }
73535
73809
  const daysElapsed = usageDateRange.daysElapsed;
73536
- return usageData.users.reduce((acc, user2) => {
73537
- acc[user2.user_id] = Math.round(user2.total_duration_ms / daysElapsed);
73810
+ return Object.entries(usageSummaryByUser).reduce((acc, [userId, usage]) => {
73811
+ acc[userId] = calculateAverageDailyDuration(usage.active_duration_ms, daysElapsed);
73538
73812
  return acc;
73539
73813
  }, {});
73540
- }, [usageData, usageDateRange.daysElapsed]);
73814
+ }, [canViewUsageStats, usageSummaryByUser, usageDateRange.daysElapsed]);
73541
73815
  const pageTitle = "Team Management";
73542
73816
  const pageDescription = canViewUsageStats ? "Manage team members and view their daily usage" : "Manage supervisors in your factory";
73543
73817
  const hasAccess = user ? user.role_level === void 0 || canRoleAccessTeamManagement(user.role_level) : false;
@@ -73561,7 +73835,6 @@ var TeamManagementView = ({
73561
73835
  setIsLoading(true);
73562
73836
  setError(void 0);
73563
73837
  try {
73564
- const userManagementService = createUserManagementService(supabase);
73565
73838
  const { data: { session } } = await supabase.auth.getSession();
73566
73839
  if (!session?.access_token) {
73567
73840
  throw new Error("No authentication token available");
@@ -73571,116 +73844,31 @@ var TeamManagementView = ({
73571
73844
  if (!backendUrl) {
73572
73845
  throw new Error("Backend URL is not configured. Please set NEXT_PUBLIC_BACKEND_URL in your environment.");
73573
73846
  }
73574
- if ((user?.role_level === "optifye" || user?.role_level === "owner" || user?.role_level === "it") && companyId) {
73575
- const { data: factories } = await supabase.from("factories").select("id, factory_name, company_id").eq("company_id", companyId).order("factory_name");
73576
- const linesResponse = await fetch(`${backendUrl}/api/lines?company_id=${companyId}`, {
73577
- headers: {
73578
- "Authorization": `Bearer ${token}`,
73579
- "Content-Type": "application/json"
73580
- }
73581
- });
73582
- if (!linesResponse.ok) {
73583
- throw new Error(`Failed to fetch lines: ${linesResponse.statusText}`);
73584
- }
73585
- const linesData = await linesResponse.json();
73586
- const lines = linesData.lines || [];
73587
- setAvailableFactories(factories || []);
73588
- setAvailableLines(lines);
73589
- console.log("[TeamManagementView] Company-scoped team view - Company:", companyId, "Loaded factories:", factories?.length, "lines:", lines?.length);
73590
- } else if (isFactoryScopedRole(user?.role_level)) {
73591
- if (factoryScopedRoleFactoryIds.length > 0) {
73592
- if (companyId) {
73593
- const linesResponse = await fetch(`${backendUrl}/api/lines?company_id=${companyId}`, {
73594
- headers: {
73595
- "Authorization": `Bearer ${token}`,
73596
- "Content-Type": "application/json"
73597
- }
73598
- });
73599
- if (!linesResponse.ok) {
73600
- throw new Error(`Failed to fetch lines: ${linesResponse.statusText}`);
73601
- }
73602
- const linesData = await linesResponse.json();
73603
- const allLines = linesData.lines || [];
73604
- const lines = allLines.filter((line) => factoryScopedRoleFactoryIds.includes(line.factory_id));
73605
- setAvailableLines(lines);
73606
- console.log("[TeamManagementView] Factory-scoped team view - Factories:", factoryScopedRoleFactoryIds, "Loaded lines:", lines?.length);
73607
- }
73608
- } else if (entityConfig?.factoryId) {
73609
- const linesResponse = await fetch(`${backendUrl}/api/lines?factory_id=${entityConfig.factoryId}`, {
73610
- headers: {
73611
- "Authorization": `Bearer ${token}`,
73612
- "Content-Type": "application/json"
73613
- }
73614
- });
73615
- if (!linesResponse.ok) {
73616
- throw new Error(`Failed to fetch lines: ${linesResponse.statusText}`);
73617
- }
73618
- const linesData = await linesResponse.json();
73619
- const lines = linesData.lines || [];
73620
- setAvailableLines(lines);
73621
- console.log("[TeamManagementView] Plant Head - Using entityConfig factory:", entityConfig.factoryId, "Loaded lines:", lines?.length);
73622
- } else {
73623
- setAvailableLines([]);
73624
- console.warn("[TeamManagementView] Plant Head has no factory assignments");
73625
- }
73626
- setAvailableFactories([]);
73627
- } else if (companyId) {
73628
- const linesResponse = await fetch(`${backendUrl}/api/lines?company_id=${companyId}`, {
73847
+ const params = new URLSearchParams({
73848
+ start_date: usageDateRange.startDate,
73849
+ end_date: usageDateRange.endDate
73850
+ });
73851
+ const bootstrapResponse = await fetch(
73852
+ `${backendUrl}/api/users/company/${companyId}/bootstrap?${params.toString()}`,
73853
+ {
73629
73854
  headers: {
73630
73855
  "Authorization": `Bearer ${token}`,
73631
73856
  "Content-Type": "application/json"
73632
73857
  }
73633
- });
73634
- if (!linesResponse.ok) {
73635
- throw new Error(`Failed to fetch lines: ${linesResponse.statusText}`);
73636
73858
  }
73637
- const linesData = await linesResponse.json();
73638
- const lines = linesData.lines || [];
73639
- setAvailableLines(lines);
73640
- setAvailableFactories([]);
73641
- console.log("[TeamManagementView] Fallback - Company:", companyId, "Loaded lines:", lines?.length);
73642
- }
73643
- const usersPromise = isFactoryScopedRole(user?.role_level) ? (async () => {
73644
- if (factoryScopedRoleFactoryIds.length === 0) {
73645
- return [];
73646
- }
73647
- const results = await Promise.allSettled(
73648
- factoryScopedRoleFactoryIds.map((factoryId) => userManagementService.getFactoryUsers(factoryId))
73649
- );
73650
- const successful = results.filter(
73651
- (result) => result.status === "fulfilled"
73652
- ).flatMap((result) => result.value || []);
73653
- if (successful.length > 0) {
73654
- const byUserId = /* @__PURE__ */ new Map();
73655
- successful.forEach((u) => {
73656
- if (u?.user_id) {
73657
- byUserId.set(u.user_id, u);
73658
- }
73659
- });
73660
- return Array.from(byUserId.values());
73661
- }
73662
- const firstRejected = results.find(
73663
- (result) => result.status === "rejected"
73664
- );
73665
- if (firstRejected) {
73666
- throw firstRejected.reason;
73667
- }
73668
- return [];
73669
- })() : userManagementService.getCompanyUsers(companyId);
73670
- const [usersData, userStats] = await Promise.all([
73671
- usersPromise,
73672
- userManagementService.getUserStats(companyId)
73673
- ]);
73674
- setUsers(usersData);
73675
- setStats({
73676
- totalUsers: userStats.total,
73677
- owners: userStats.owners,
73678
- it: userStats.it,
73679
- plantHeads: userStats.plant_heads,
73680
- industrialEngineers: userStats.industrial_engineers,
73681
- supervisors: userStats.supervisors,
73682
- optifye: 0
73683
- });
73859
+ );
73860
+ if (!bootstrapResponse.ok) {
73861
+ const errorData = await bootstrapResponse.json().catch(() => ({}));
73862
+ throw new Error(errorData.detail || `Failed to fetch team bootstrap: ${bootstrapResponse.statusText}`);
73863
+ }
73864
+ const bootstrapData = await bootstrapResponse.json();
73865
+ setAvailableFactories((bootstrapData.factories || []).map((factory) => ({
73866
+ id: factory.id,
73867
+ factory_name: factory.factory_name
73868
+ })));
73869
+ setAvailableLines(bootstrapData.lines || []);
73870
+ setUsers(bootstrapData.users || []);
73871
+ setUsageSummaryByUser(bootstrapData.usage_summary_by_user || {});
73684
73872
  } catch (err) {
73685
73873
  console.error("Error loading team management data:", err);
73686
73874
  setError(err instanceof Error ? err.message : "Failed to load data");
@@ -73688,7 +73876,7 @@ var TeamManagementView = ({
73688
73876
  } finally {
73689
73877
  setIsLoading(false);
73690
73878
  }
73691
- }, [supabase, user, pageCompanyId, entityConfig?.factoryId, factoryScopedRoleFactoryIds]);
73879
+ }, [supabase, user, pageCompanyId, usageDateRange.endDate, usageDateRange.startDate]);
73692
73880
  useEffect(() => {
73693
73881
  const companyId = pageCompanyId;
73694
73882
  const canLoad = hasAccess && user && !!companyId;
@@ -73869,7 +74057,7 @@ var TeamManagementView = ({
73869
74057
  availableLines,
73870
74058
  availableFactories,
73871
74059
  avgDailyUsage: avgDailyUsageMap,
73872
- isUsageLoading,
74060
+ isUsageLoading: false,
73873
74061
  showUsageStats: canViewUsageStats
73874
74062
  }
73875
74063
  ) }),
@@ -80094,7 +80282,7 @@ var useOperationsOverviewRefresh = ({
80094
80282
  };
80095
80283
  }, [companyId, enabled, getIsPageActive, isLiveScope, lineIds.length, refreshFromResume, startPolling, stopPolling, supabase]);
80096
80284
  };
80097
- var parseTimeToMinutes3 = (value) => {
80285
+ var parseTimeToMinutes4 = (value) => {
80098
80286
  if (!value) return null;
80099
80287
  const parts = value.split(":");
80100
80288
  if (parts.length < 2) return null;
@@ -80129,8 +80317,8 @@ var classifyShiftBucket = ({
80129
80317
  return "day";
80130
80318
  }
80131
80319
  }
80132
- const startMinutes = parseTimeToMinutes3(startTime);
80133
- const endMinutes = parseTimeToMinutes3(endTime);
80320
+ const startMinutes = parseTimeToMinutes4(startTime);
80321
+ const endMinutes = parseTimeToMinutes4(endTime);
80134
80322
  if (startMinutes !== null) {
80135
80323
  if (startMinutes >= 4 * 60 && startMinutes < 18 * 60) return "day";
80136
80324
  return "night";
@@ -80189,8 +80377,8 @@ var getShiftWindowsForConfig = (shiftConfig, timezone) => {
80189
80377
  ];
80190
80378
  };
80191
80379
  var normalizeShiftWindowMinutes = (startTime, endTime) => {
80192
- const startMinutes = parseTimeToMinutes3(startTime);
80193
- const endMinutesRaw = parseTimeToMinutes3(endTime);
80380
+ const startMinutes = parseTimeToMinutes4(startTime);
80381
+ const endMinutesRaw = parseTimeToMinutes4(endTime);
80194
80382
  if (startMinutes === null || endMinutesRaw === null) {
80195
80383
  return null;
80196
80384
  }
@@ -80356,7 +80544,7 @@ var PlantHeadView = () => {
80356
80544
  startTime: shift.startTime,
80357
80545
  endTime: shift.endTime
80358
80546
  });
80359
- const startMinutes = parseTimeToMinutes3(shift.startTime);
80547
+ const startMinutes = parseTimeToMinutes4(shift.startTime);
80360
80548
  if (bucket === "day" && startMinutes !== null) {
80361
80549
  candidateStarts.push(startMinutes);
80362
80550
  }
@@ -80366,7 +80554,7 @@ var PlantHeadView = () => {
80366
80554
  scopedLineIds.forEach((lineId) => {
80367
80555
  const shiftConfig = shiftConfigMap.get(lineId) || staticShiftConfig;
80368
80556
  getShiftWindowsForConfig(shiftConfig).forEach((shift) => {
80369
- const startMinutes = parseTimeToMinutes3(shift.startTime);
80557
+ const startMinutes = parseTimeToMinutes4(shift.startTime);
80370
80558
  if (startMinutes !== null) {
80371
80559
  candidateStarts.push(startMinutes);
80372
80560
  }
@@ -80457,7 +80645,7 @@ var PlantHeadView = () => {
80457
80645
  startTime: shift.startTime,
80458
80646
  endTime: shift.endTime
80459
80647
  });
80460
- return bucket === "day" ? parseTimeToMinutes3(shift.startTime) : null;
80648
+ return bucket === "day" ? parseTimeToMinutes4(shift.startTime) : null;
80461
80649
  }).filter((value) => value !== null);
80462
80650
  }) : [];
80463
80651
  if (dayStartCandidates.length > 0) {