@optifye/dashboard-core 6.11.38 → 6.11.40

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
@@ -1,11 +1,11 @@
1
1
  'use strict';
2
2
 
3
+ var dateFns = require('date-fns');
4
+ var dateFnsTz = require('date-fns-tz');
3
5
  var React143 = require('react');
4
6
  var jsxRuntime = require('react/jsx-runtime');
5
7
  var router = require('next/router');
6
8
  var sonner = require('sonner');
7
- var dateFnsTz = require('date-fns-tz');
8
- var dateFns = require('date-fns');
9
9
  var mixpanel = require('mixpanel-browser');
10
10
  var events = require('events');
11
11
  var supabaseJs = require('@supabase/supabase-js');
@@ -1661,6 +1661,149 @@ function isValidPrefetchParams(params) {
1661
1661
  function isValidPrefetchStatus(status) {
1662
1662
  return typeof status === "string" && Object.values(PrefetchStatus).includes(status);
1663
1663
  }
1664
+ var getOperationalDate = (timezone, date = /* @__PURE__ */ new Date(), shiftStartTime = "06:00") => {
1665
+ const zonedDate = dateFnsTz.toZonedTime(date, timezone);
1666
+ const hours = zonedDate.getHours();
1667
+ const minutes = zonedDate.getMinutes();
1668
+ const [startHourRaw, startMinuteRaw] = shiftStartTime.split(":").map(Number);
1669
+ const startHour = Number.isFinite(startHourRaw) ? startHourRaw : 6;
1670
+ const startMinute = Number.isFinite(startMinuteRaw) ? startMinuteRaw : 0;
1671
+ const currentTotalMinutes = hours * 60 + minutes;
1672
+ const shiftStartTotalMinutes = startHour * 60 + startMinute;
1673
+ const operationalDate = currentTotalMinutes < shiftStartTotalMinutes ? dateFns.subDays(zonedDate, 1) : zonedDate;
1674
+ return dateFns.format(operationalDate, "yyyy-MM-dd");
1675
+ };
1676
+ function formatTimeInZone(time2, timezone, formatString = "HH:mm:ss") {
1677
+ const dateObj = typeof time2 === "string" ? dateFns.parseISO(time2) : time2;
1678
+ if (!dateFns.isValid(dateObj)) return "Invalid Date";
1679
+ return dateFnsTz.formatInTimeZone(dateObj, timezone, formatString);
1680
+ }
1681
+ function getCurrentTimeInZone(timezone, formatString) {
1682
+ const now4 = /* @__PURE__ */ new Date();
1683
+ if (formatString) {
1684
+ if (!dateFns.isValid(now4)) return "Invalid Date";
1685
+ return dateFnsTz.formatInTimeZone(now4, timezone, formatString);
1686
+ }
1687
+ return now4;
1688
+ }
1689
+ var pad2 = (value) => String(value).padStart(2, "0");
1690
+ var DATE_KEY_PREFIX_PATTERN = /^(\d{4}-\d{2}-\d{2})/;
1691
+ var buildDateKey = (year, monthIndex, day) => {
1692
+ return `${year}-${pad2(monthIndex + 1)}-${pad2(day)}`;
1693
+ };
1694
+ var getDateKeyFromDate = (date) => {
1695
+ return buildDateKey(date.getFullYear(), date.getMonth(), date.getDate());
1696
+ };
1697
+ var parseDateKeyToDate = (dateKey) => {
1698
+ const [year, month, day] = dateKey.split("-").map(Number);
1699
+ return new Date(year, month - 1, day, 12, 0, 0, 0);
1700
+ };
1701
+ var getDateKeyFromValue = (value) => {
1702
+ if (typeof value === "string") {
1703
+ const keyMatch = value.match(DATE_KEY_PREFIX_PATTERN);
1704
+ if (keyMatch?.[1]) {
1705
+ return keyMatch[1];
1706
+ }
1707
+ const parsed = dateFns.parseISO(value);
1708
+ if (dateFns.isValid(parsed)) {
1709
+ return getDateKeyFromDate(parsed);
1710
+ }
1711
+ return value;
1712
+ }
1713
+ return getDateKeyFromDate(value);
1714
+ };
1715
+ var formatDateKeyForDisplay = (dateKey, formatStr = "MMM d, yyyy") => {
1716
+ return dateFns.format(parseDateKeyToDate(dateKey), formatStr);
1717
+ };
1718
+ var getMonthKeyBounds = (year, monthIndex) => {
1719
+ const startKey = buildDateKey(year, monthIndex, 1);
1720
+ const lastDay = new Date(year, monthIndex + 1, 0).getDate();
1721
+ const endKey = buildDateKey(year, monthIndex, lastDay);
1722
+ return { startKey, endKey };
1723
+ };
1724
+ var getCurrentWeekToDateRange = (timezone, now4 = /* @__PURE__ */ new Date()) => {
1725
+ const todayKey = dateFnsTz.formatInTimeZone(now4, timezone || "UTC", "yyyy-MM-dd");
1726
+ const todayDate = dateFns.parseISO(`${todayKey}T00:00:00`);
1727
+ const dayOfWeek = dateFns.getDay(todayDate);
1728
+ const daysSinceMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1;
1729
+ const weekStart = dateFns.subDays(todayDate, daysSinceMonday);
1730
+ return {
1731
+ startKey: dateFns.format(weekStart, "yyyy-MM-dd"),
1732
+ endKey: dateFns.format(todayDate, "yyyy-MM-dd")
1733
+ };
1734
+ };
1735
+ var getCurrentWeekFullRange = (timezone, now4 = /* @__PURE__ */ new Date()) => {
1736
+ const currentWeekToDateRange = getCurrentWeekToDateRange(timezone, now4);
1737
+ const weekStart = parseDateKeyToDate(currentWeekToDateRange.startKey);
1738
+ return {
1739
+ startKey: currentWeekToDateRange.startKey,
1740
+ endKey: dateFns.format(dateFns.addDays(weekStart, 6), "yyyy-MM-dd")
1741
+ };
1742
+ };
1743
+ var normalizeDateKeyRange = (startKey, endKey, minKey, maxKey) => {
1744
+ const clampedStart = startKey < minKey ? minKey : startKey > maxKey ? maxKey : startKey;
1745
+ const clampedEnd = endKey < minKey ? minKey : endKey > maxKey ? maxKey : endKey;
1746
+ if (clampedStart <= clampedEnd) {
1747
+ return { startKey: clampedStart, endKey: clampedEnd };
1748
+ }
1749
+ return { startKey: clampedEnd, endKey: clampedStart };
1750
+ };
1751
+ var isFullMonthRange = (range, year, monthIndex) => {
1752
+ const bounds = getMonthKeyBounds(year, monthIndex);
1753
+ return range.startKey === bounds.startKey && range.endKey === bounds.endKey;
1754
+ };
1755
+ var getMonthlyTrendComparisonLabel = (range, year, monthIndex) => {
1756
+ if (isFullMonthRange(range, year, monthIndex)) {
1757
+ return "last month";
1758
+ }
1759
+ return range.startKey === range.endKey ? "previous day" : "previous range";
1760
+ };
1761
+ var getMonthWeekRanges = (year, monthIndex, timezone, maxKey) => {
1762
+ const totalDays = new Date(year, monthIndex + 1, 0).getDate();
1763
+ const ranges = [];
1764
+ let currentStartDay = 1;
1765
+ for (let day = 1; day <= totalDays; day += 1) {
1766
+ const zonedDate = dateFnsTz.toZonedTime(new Date(year, monthIndex, day), timezone);
1767
+ const dayOfWeek = zonedDate.getDay();
1768
+ const isEndOfWeek = dayOfWeek === 0 || day === totalDays;
1769
+ if (isEndOfWeek) {
1770
+ const startKey = buildDateKey(year, monthIndex, currentStartDay);
1771
+ const endKey = buildDateKey(year, monthIndex, day);
1772
+ if (maxKey && startKey > maxKey) {
1773
+ break;
1774
+ }
1775
+ const clampedEndKey = maxKey && endKey > maxKey ? maxKey : endKey;
1776
+ if (clampedEndKey < startKey) {
1777
+ break;
1778
+ }
1779
+ const labelStart = formatDateKeyForDisplay(startKey, "MMM d");
1780
+ const labelEnd = formatDateKeyForDisplay(clampedEndKey, "MMM d");
1781
+ ranges.push({
1782
+ startKey,
1783
+ endKey: clampedEndKey,
1784
+ label: `Week of ${labelStart} - ${labelEnd}`
1785
+ });
1786
+ currentStartDay = day + 1;
1787
+ }
1788
+ }
1789
+ return ranges;
1790
+ };
1791
+ var formatRangeLabel = (range, fullMonthLabel) => {
1792
+ if (!range.startKey || !range.endKey) return fullMonthLabel;
1793
+ if (range.startKey === range.endKey) {
1794
+ return formatDateKeyForDisplay(range.startKey, "MMM d, yyyy");
1795
+ }
1796
+ const startLabel = formatDateKeyForDisplay(range.startKey, "MMM d");
1797
+ const endLabel = formatDateKeyForDisplay(range.endKey, "MMM d, yyyy");
1798
+ return `${startLabel} - ${endLabel}`;
1799
+ };
1800
+ var filterDataByDateKeyRange = (data, range) => {
1801
+ if (!range.startKey || !range.endKey) return data;
1802
+ return (data || []).filter((item) => {
1803
+ const dateKey = item.dateKey || getDateKeyFromValue(item.date);
1804
+ return dateKey >= range.startKey && dateKey <= range.endKey;
1805
+ });
1806
+ };
1664
1807
 
1665
1808
  // src/lib/types/calendar.ts
1666
1809
  var DEFAULT_SHIFT_DATA = {
@@ -1692,6 +1835,9 @@ var hasAnyShiftData = (day) => {
1692
1835
  var getAvailableShiftIds = (day) => {
1693
1836
  return Object.keys(day.shifts).map(Number).sort((a, b) => a - b);
1694
1837
  };
1838
+ var getDayDateKey = (day) => {
1839
+ return day.dateKey || getDateKeyFromDate(day.date);
1840
+ };
1695
1841
 
1696
1842
  // src/components/dashboard/grid/workspace_grid_constants.ts
1697
1843
  var DEFAULT_WORKSPACE_POSITIONS = [
@@ -3347,130 +3493,6 @@ var memoizedOutputArrayAggregation = createMemoizedFunction(
3347
3493
  },
3348
3494
  (arrays) => arrays.map((arr) => arr.length).join("-")
3349
3495
  );
3350
- var getOperationalDate = (timezone, date = /* @__PURE__ */ new Date(), shiftStartTime = "06:00") => {
3351
- const zonedDate = dateFnsTz.toZonedTime(date, timezone);
3352
- const hours = zonedDate.getHours();
3353
- const minutes = zonedDate.getMinutes();
3354
- const [startHourRaw, startMinuteRaw] = shiftStartTime.split(":").map(Number);
3355
- const startHour = Number.isFinite(startHourRaw) ? startHourRaw : 6;
3356
- const startMinute = Number.isFinite(startMinuteRaw) ? startMinuteRaw : 0;
3357
- const currentTotalMinutes = hours * 60 + minutes;
3358
- const shiftStartTotalMinutes = startHour * 60 + startMinute;
3359
- const operationalDate = currentTotalMinutes < shiftStartTotalMinutes ? dateFns.subDays(zonedDate, 1) : zonedDate;
3360
- return dateFns.format(operationalDate, "yyyy-MM-dd");
3361
- };
3362
- function formatTimeInZone(time2, timezone, formatString = "HH:mm:ss") {
3363
- const dateObj = typeof time2 === "string" ? dateFns.parseISO(time2) : time2;
3364
- if (!dateFns.isValid(dateObj)) return "Invalid Date";
3365
- return dateFnsTz.formatInTimeZone(dateObj, timezone, formatString);
3366
- }
3367
- function getCurrentTimeInZone(timezone, formatString) {
3368
- const now4 = /* @__PURE__ */ new Date();
3369
- if (formatString) {
3370
- if (!dateFns.isValid(now4)) return "Invalid Date";
3371
- return dateFnsTz.formatInTimeZone(now4, timezone, formatString);
3372
- }
3373
- return now4;
3374
- }
3375
- var pad2 = (value) => String(value).padStart(2, "0");
3376
- var buildDateKey = (year, monthIndex, day) => {
3377
- return `${year}-${pad2(monthIndex + 1)}-${pad2(day)}`;
3378
- };
3379
- var getDateKeyFromDate = (date) => {
3380
- return date.toISOString().split("T")[0];
3381
- };
3382
- var parseDateKeyToDate = (dateKey) => {
3383
- const [year, month, day] = dateKey.split("-").map(Number);
3384
- return new Date(year, month - 1, day);
3385
- };
3386
- var formatDateKeyForDisplay = (dateKey, formatStr = "MMM d, yyyy") => {
3387
- return dateFns.format(parseDateKeyToDate(dateKey), formatStr);
3388
- };
3389
- var getMonthKeyBounds = (year, monthIndex) => {
3390
- const startKey = buildDateKey(year, monthIndex, 1);
3391
- const lastDay = new Date(year, monthIndex + 1, 0).getDate();
3392
- const endKey = buildDateKey(year, monthIndex, lastDay);
3393
- return { startKey, endKey };
3394
- };
3395
- var getCurrentWeekToDateRange = (timezone, now4 = /* @__PURE__ */ new Date()) => {
3396
- const todayKey = dateFnsTz.formatInTimeZone(now4, timezone || "UTC", "yyyy-MM-dd");
3397
- const todayDate = dateFns.parseISO(`${todayKey}T00:00:00`);
3398
- const dayOfWeek = dateFns.getDay(todayDate);
3399
- const daysSinceMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1;
3400
- const weekStart = dateFns.subDays(todayDate, daysSinceMonday);
3401
- return {
3402
- startKey: dateFns.format(weekStart, "yyyy-MM-dd"),
3403
- endKey: dateFns.format(todayDate, "yyyy-MM-dd")
3404
- };
3405
- };
3406
- var getCurrentWeekFullRange = (timezone, now4 = /* @__PURE__ */ new Date()) => {
3407
- const currentWeekToDateRange = getCurrentWeekToDateRange(timezone, now4);
3408
- const weekStart = parseDateKeyToDate(currentWeekToDateRange.startKey);
3409
- return {
3410
- startKey: currentWeekToDateRange.startKey,
3411
- endKey: dateFns.format(dateFns.addDays(weekStart, 6), "yyyy-MM-dd")
3412
- };
3413
- };
3414
- var normalizeDateKeyRange = (startKey, endKey, minKey, maxKey) => {
3415
- const clampedStart = startKey < minKey ? minKey : startKey > maxKey ? maxKey : startKey;
3416
- const clampedEnd = endKey < minKey ? minKey : endKey > maxKey ? maxKey : endKey;
3417
- if (clampedStart <= clampedEnd) {
3418
- return { startKey: clampedStart, endKey: clampedEnd };
3419
- }
3420
- return { startKey: clampedEnd, endKey: clampedStart };
3421
- };
3422
- var isFullMonthRange = (range, year, monthIndex) => {
3423
- const bounds = getMonthKeyBounds(year, monthIndex);
3424
- return range.startKey === bounds.startKey && range.endKey === bounds.endKey;
3425
- };
3426
- var getMonthWeekRanges = (year, monthIndex, timezone, maxKey) => {
3427
- const totalDays = new Date(year, monthIndex + 1, 0).getDate();
3428
- const ranges = [];
3429
- let currentStartDay = 1;
3430
- for (let day = 1; day <= totalDays; day += 1) {
3431
- const zonedDate = dateFnsTz.toZonedTime(new Date(year, monthIndex, day), timezone);
3432
- const dayOfWeek = zonedDate.getDay();
3433
- const isEndOfWeek = dayOfWeek === 0 || day === totalDays;
3434
- if (isEndOfWeek) {
3435
- const startKey = buildDateKey(year, monthIndex, currentStartDay);
3436
- const endKey = buildDateKey(year, monthIndex, day);
3437
- if (maxKey && startKey > maxKey) {
3438
- break;
3439
- }
3440
- const clampedEndKey = maxKey && endKey > maxKey ? maxKey : endKey;
3441
- if (clampedEndKey < startKey) {
3442
- break;
3443
- }
3444
- const labelStart = formatDateKeyForDisplay(startKey, "MMM d");
3445
- const labelEnd = formatDateKeyForDisplay(clampedEndKey, "MMM d");
3446
- ranges.push({
3447
- startKey,
3448
- endKey: clampedEndKey,
3449
- label: `Week of ${labelStart} - ${labelEnd}`
3450
- });
3451
- currentStartDay = day + 1;
3452
- }
3453
- }
3454
- return ranges;
3455
- };
3456
- var formatRangeLabel = (range, fullMonthLabel) => {
3457
- if (!range.startKey || !range.endKey) return fullMonthLabel;
3458
- if (range.startKey === range.endKey) {
3459
- return formatDateKeyForDisplay(range.startKey, "MMM d, yyyy");
3460
- }
3461
- const startLabel = formatDateKeyForDisplay(range.startKey, "MMM d");
3462
- const endLabel = formatDateKeyForDisplay(range.endKey, "MMM d, yyyy");
3463
- return `${startLabel} - ${endLabel}`;
3464
- };
3465
- var filterDataByDateKeyRange = (data, range) => {
3466
- if (!range.startKey || !range.endKey) return data;
3467
- return (data || []).filter((item) => {
3468
- const dateKey = typeof item.date === "string" ? item.date : getDateKeyFromDate(item.date);
3469
- return dateKey >= range.startKey && dateKey <= range.endKey;
3470
- });
3471
- };
3472
-
3473
- // src/lib/utils/shifts.ts
3474
3496
  var DEFAULT_DAY_SHIFT_START = "06:00";
3475
3497
  var DEFAULT_NIGHT_SHIFT_START = "18:00";
3476
3498
  var DEFAULT_TRANSITION_MINUTES = 15;
@@ -12290,6 +12312,7 @@ var toWorkspaceDetailedMetrics = ({
12290
12312
  return {
12291
12313
  workspace_id: data.workspace_id,
12292
12314
  workspace_name: data.workspace_name,
12315
+ workspace_display_name: typeof data.workspace_display_name === "string" ? data.workspace_display_name : typeof data.display_name === "string" ? data.display_name : null,
12293
12316
  line_id: data.line_id,
12294
12317
  line_name: data.line_name || "",
12295
12318
  line_assembly_enabled: data.line_assembly_enabled === true || data.assembly_enabled === true,
@@ -18438,13 +18461,27 @@ var useMonthlyTrend = (params) => {
18438
18461
  if (typeof params.shiftId === "number") {
18439
18462
  searchParams.append("shift_id", params.shiftId.toString());
18440
18463
  }
18464
+ if (params.startDate && params.endDate) {
18465
+ searchParams.append("start_date", params.startDate);
18466
+ searchParams.append("end_date", params.endDate);
18467
+ }
18441
18468
  if (params.entityType === "line") {
18442
18469
  searchParams.append("line_id", params.entityId);
18443
18470
  } else {
18444
18471
  searchParams.append("workspace_id", params.entityId);
18445
18472
  }
18446
18473
  return searchParams.toString();
18447
- }, [params.entityId, params.entityType, params.month, params.year, params.shiftId, entityConfig?.companyId]);
18474
+ }, [
18475
+ params.companyId,
18476
+ params.endDate,
18477
+ params.entityId,
18478
+ params.entityType,
18479
+ params.month,
18480
+ params.shiftId,
18481
+ params.startDate,
18482
+ params.year,
18483
+ entityConfig?.companyId
18484
+ ]);
18448
18485
  React143.useEffect(() => {
18449
18486
  let isMounted = true;
18450
18487
  if (!queryString || !supabase) {
@@ -47290,6 +47327,10 @@ var LineMonthlyHistory = ({
47290
47327
  const efficiencyImproved = efficiencyDelta >= 0;
47291
47328
  const EfficiencyTrendIcon = efficiencyImproved ? lucideReact.ArrowUp : lucideReact.ArrowDown;
47292
47329
  const efficiencyTrendText = `${Math.abs(efficiencyDelta).toFixed(1)}%`;
47330
+ const trendComparisonLabel = React143.useMemo(
47331
+ () => getMonthlyTrendComparisonLabel(normalizedRange, year, month),
47332
+ [month, normalizedRange, year]
47333
+ );
47293
47334
  const outputDelta = trendSummary?.avg_daily_output?.delta_pp ?? 0;
47294
47335
  const outputImproved = outputDelta >= 0;
47295
47336
  const OutputTrendIcon = outputImproved ? lucideReact.ArrowUp : lucideReact.ArrowDown;
@@ -47540,10 +47581,7 @@ var LineMonthlyHistory = ({
47540
47581
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-base font-bold text-gray-900 whitespace-nowrap", children: uptimeSummary?.avgDailyStoppages ?? 0 }),
47541
47582
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-1 ${stoppagesImproved ? "bg-emerald-50 text-emerald-600" : "bg-red-50 text-red-600"} px-1.5 py-0.5 rounded-full text-[10px] font-medium whitespace-nowrap flex-shrink-0`, children: [
47542
47583
  /* @__PURE__ */ jsxRuntime.jsx(StoppagesTrendIcon, { className: "w-3 h-3" }),
47543
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
47544
- stoppagesTrendText,
47545
- " vs last month"
47546
- ] })
47584
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: `${stoppagesTrendText} vs ${trendComparisonLabel}` })
47547
47585
  ] })
47548
47586
  ] })
47549
47587
  ] }),
@@ -47553,10 +47591,7 @@ var LineMonthlyHistory = ({
47553
47591
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-base font-bold text-gray-900 whitespace-nowrap", children: formatIdleTime(uptimeSummary?.avgIdleTime ?? 0) }),
47554
47592
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-1 ${idleImproved ? "bg-emerald-50 text-emerald-600" : "bg-red-50 text-red-600"} px-1.5 py-0.5 rounded-full text-[10px] font-medium whitespace-nowrap flex-shrink-0`, children: [
47555
47593
  /* @__PURE__ */ jsxRuntime.jsx(IdleTrendIcon, { className: "w-3 h-3" }),
47556
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
47557
- idleTrendText,
47558
- " vs last month"
47559
- ] })
47594
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: `${idleTrendText} vs ${trendComparisonLabel}` })
47560
47595
  ] })
47561
47596
  ] })
47562
47597
  ] })
@@ -47570,10 +47605,7 @@ var LineMonthlyHistory = ({
47570
47605
  ] }),
47571
47606
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-1 ${efficiencyImproved ? "bg-emerald-50 text-emerald-600" : "bg-red-50 text-red-600"} px-1.5 py-0.5 rounded-full text-[10px] font-medium whitespace-nowrap flex-shrink-0`, children: [
47572
47607
  /* @__PURE__ */ jsxRuntime.jsx(EfficiencyTrendIcon, { className: "w-3 h-3" }),
47573
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
47574
- efficiencyTrendText,
47575
- " vs last month"
47576
- ] })
47608
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: `${efficiencyTrendText} vs ${trendComparisonLabel}` })
47577
47609
  ] })
47578
47610
  ] })
47579
47611
  ] }),
@@ -47583,10 +47615,7 @@ var LineMonthlyHistory = ({
47583
47615
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xl font-bold text-gray-900", children: Math.round(avgOutput).toLocaleString() }),
47584
47616
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-1 ${outputImproved ? "bg-emerald-50 text-emerald-600" : "bg-red-50 text-red-600"} px-1.5 py-0.5 rounded-full text-[10px] font-medium whitespace-nowrap flex-shrink-0`, children: [
47585
47617
  /* @__PURE__ */ jsxRuntime.jsx(OutputTrendIcon, { className: "w-3 h-3" }),
47586
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
47587
- outputTrendText,
47588
- " vs last month"
47589
- ] })
47618
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: `${outputTrendText} vs ${trendComparisonLabel}` })
47590
47619
  ] })
47591
47620
  ] })
47592
47621
  ] })
@@ -47597,10 +47626,7 @@ var LineMonthlyHistory = ({
47597
47626
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-xs sm:text-sm font-bold text-gray-700 text-left", children: "Utilization" }),
47598
47627
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-1 ${utilizationImproved ? "bg-emerald-50 text-emerald-600" : "bg-red-50 text-red-600"} px-1.5 py-0.5 rounded-full text-[10px] font-medium whitespace-nowrap flex-shrink-0`, children: [
47599
47628
  /* @__PURE__ */ jsxRuntime.jsx(UtilizationTrendIcon, { className: "w-3 h-3" }),
47600
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
47601
- utilizationTrendText,
47602
- " vs last month"
47603
- ] })
47629
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: `${utilizationTrendText} vs ${trendComparisonLabel}` })
47604
47630
  ] })
47605
47631
  ] }),
47606
47632
  pieChartData.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full h-[140px] sm:h-[160px] flex items-center overflow-hidden", children: [
@@ -47952,19 +47978,33 @@ var LineMonthlyPdfGenerator = ({
47952
47978
  is_full_month: fullRange
47953
47979
  });
47954
47980
  const doc = new jsPDF.jsPDF();
47955
- doc.setFontSize(14);
47956
- doc.setFont("helvetica", "bold");
47957
- doc.setTextColor(50, 50, 50);
47958
- doc.text("OPTIFYE.AI", 20, 15);
47959
- doc.setFontSize(11);
47960
- doc.setFont("helvetica", "normal");
47961
- doc.setTextColor(80, 80, 80);
47962
- const reportText = isUptimeMode ? "MONTHLY UTILIZATION REPORT" : "MONTHLY PERFORMANCE REPORT";
47963
- const reportTextWidth = doc.getStringUnitWidth(reportText) * 11 / doc.internal.scaleFactor;
47964
- doc.text(reportText, doc.internal.pageSize.width - 20 - reportTextWidth, 15);
47965
- doc.setDrawColor(200, 200, 200);
47966
- doc.setLineWidth(0.5);
47967
- doc.line(20, 20, 190, 20);
47981
+ const pageHeight = typeof doc.internal.pageSize.height === "number" ? Number(doc.internal.pageSize.height) : 297;
47982
+ const footerY = pageHeight - 17;
47983
+ const maxContentY = footerY - 10;
47984
+ const generatedText = `Generated on ${(/* @__PURE__ */ new Date()).toLocaleString("en-IN", { timeZone: "Asia/Kolkata" })}`;
47985
+ const dailySectionTitle = isUptimeMode ? "Daily Utilization Summary" : "Daily Performance Summary";
47986
+ const drawPageChrome = () => {
47987
+ doc.setFontSize(14);
47988
+ doc.setFont("helvetica", "bold");
47989
+ doc.setTextColor(50, 50, 50);
47990
+ doc.text("OPTIFYE.AI", 20, 15);
47991
+ doc.setFontSize(11);
47992
+ doc.setFont("helvetica", "normal");
47993
+ doc.setTextColor(80, 80, 80);
47994
+ const reportText = isUptimeMode ? "MONTHLY UTILIZATION REPORT" : "MONTHLY PERFORMANCE REPORT";
47995
+ const reportTextWidth = doc.getStringUnitWidth(reportText) * 11 / doc.internal.scaleFactor;
47996
+ doc.text(reportText, doc.internal.pageSize.width - 20 - reportTextWidth, 15);
47997
+ doc.setDrawColor(200, 200, 200);
47998
+ doc.setLineWidth(0.5);
47999
+ doc.line(20, 20, 190, 20);
48000
+ };
48001
+ const drawFooter = () => {
48002
+ doc.setFontSize(9);
48003
+ doc.setTextColor(130, 130, 130);
48004
+ doc.text(generatedText, 20, footerY);
48005
+ doc.setTextColor(0, 0, 0);
48006
+ };
48007
+ drawPageChrome();
47968
48008
  doc.setFillColor(250, 250, 250);
47969
48009
  doc.roundedRect(15, 25, 180, 55, 3, 3, "F");
47970
48010
  doc.setFontSize(32);
@@ -47999,6 +48039,10 @@ var LineMonthlyPdfGenerator = ({
47999
48039
  if (shift.hasData !== void 0) return shift.hasData;
48000
48040
  return shift.total_workspaces > 0 || shift.avg_efficiency > 0 || shift.underperforming_workspaces > 0 || (shift.available_time_seconds ?? 0) > 0 || (shift.idle_time_seconds ?? 0) > 0 || (shift.output ?? 0) > 0;
48001
48041
  };
48042
+ const dailyEntries = validDays.map((dayData) => {
48043
+ const shift = getLineShiftData2(dayData, selectedShiftId);
48044
+ return { dayData, shift };
48045
+ }).filter(({ shift }) => hasShiftData(shift)).sort((left, right) => new Date(right.dayData.date).getTime() - new Date(left.dayData.date).getTime());
48002
48046
  const getUptimeTotals2 = (shift, hasData) => {
48003
48047
  if (!hasData || !shift) {
48004
48048
  return { availableSeconds: 0, productiveSeconds: 0, idleSeconds: 0 };
@@ -48125,46 +48169,45 @@ var LineMonthlyPdfGenerator = ({
48125
48169
  }
48126
48170
  const dailySeparatorY = isUptimeMode ? 180 : 165;
48127
48171
  const dailySectionStartY = isUptimeMode ? 185 : 170;
48128
- const dailyTitleY = isUptimeMode ? 195 : 180;
48129
- const dailyHeaderY = isUptimeMode ? 200 : 185;
48130
- const dailyHeaderTextY = isUptimeMode ? 205 : 190;
48131
- const dailyHeaderLineY = isUptimeMode ? 208 : 193;
48132
48172
  const dailyContentStartY = isUptimeMode ? 215 : 200;
48133
- const dailyMaxY = isUptimeMode ? 260 : 245;
48134
48173
  doc.setDrawColor(180, 180, 180);
48135
48174
  doc.setLineWidth(0.8);
48136
48175
  doc.line(20, dailySeparatorY, 190, dailySeparatorY);
48137
- doc.setFillColor(245, 245, 245);
48138
- doc.roundedRect(15, dailySectionStartY, 180, 85, 3, 3, "F");
48139
- doc.setFontSize(18);
48140
- doc.setFont("helvetica", "bold");
48141
- doc.setTextColor(40, 40, 40);
48142
- doc.text(isUptimeMode ? "Daily Utilization Summary" : "Daily Performance Summary", 20, dailyTitleY);
48143
- doc.setTextColor(0, 0, 0);
48144
- if (validDays.length > 0) {
48176
+ const renderDailyTablePage = (startIndex, sectionY, title) => {
48177
+ const rowHeight = 8;
48178
+ const tableHeaderY = sectionY + 15;
48179
+ const headerTextY = tableHeaderY + 5;
48180
+ const firstRowY = headerTextY + 10;
48181
+ const rowsPerPage = Math.max(1, Math.floor((maxContentY - firstRowY) / rowHeight));
48182
+ const pageEntries = dailyEntries.slice(startIndex, startIndex + rowsPerPage);
48183
+ const endY = firstRowY + pageEntries.length * rowHeight;
48184
+ const sectionHeight = Math.max(40, endY - sectionY + 5);
48185
+ doc.setFillColor(245, 245, 245);
48186
+ doc.roundedRect(15, sectionY, 180, sectionHeight, 3, 3, "F");
48187
+ doc.setFontSize(18);
48188
+ doc.setFont("helvetica", "bold");
48189
+ doc.setTextColor(40, 40, 40);
48190
+ doc.text(title, 20, sectionY + 10);
48191
+ doc.setTextColor(0, 0, 0);
48145
48192
  doc.setFontSize(10);
48146
48193
  doc.setFont("helvetica", "bold");
48147
48194
  doc.setFillColor(240, 240, 240);
48148
- doc.roundedRect(20, dailyHeaderY, 170, 7, 1, 1, "F");
48149
- doc.text("Date", 25, dailyHeaderTextY);
48195
+ doc.roundedRect(20, tableHeaderY, 170, 7, 1, 1, "F");
48196
+ doc.text("Date", 25, headerTextY);
48150
48197
  if (isUptimeMode) {
48151
- doc.text("Utilization", 95, dailyHeaderTextY);
48198
+ doc.text("Utilization", 95, headerTextY);
48152
48199
  } else {
48153
- doc.text("Actual", 60, dailyHeaderTextY);
48154
- doc.text("Standard", 95, dailyHeaderTextY);
48155
- doc.text("Efficiency", 135, dailyHeaderTextY);
48156
- doc.text("Status", 170, dailyHeaderTextY);
48200
+ doc.text("Actual", 60, headerTextY);
48201
+ doc.text("Standard", 95, headerTextY);
48202
+ doc.text("Efficiency", 135, headerTextY);
48203
+ doc.text("Status", 170, headerTextY);
48157
48204
  }
48158
48205
  doc.setLineWidth(0.2);
48159
48206
  doc.setDrawColor(220, 220, 220);
48160
- doc.line(20, dailyHeaderLineY, 190, dailyHeaderLineY);
48207
+ doc.line(20, headerTextY + 3, 190, headerTextY + 3);
48161
48208
  doc.setFont("helvetica", "normal");
48162
- let yPos = dailyContentStartY;
48163
- const recentDays = validDays.slice(-10).reverse();
48164
- recentDays.forEach((dayData, index) => {
48165
- if (yPos > dailyMaxY) return;
48166
- const shift = getLineShiftData2(dayData, selectedShiftId);
48167
- if (!hasShiftData(shift)) return;
48209
+ let yPos = firstRowY;
48210
+ pageEntries.forEach(({ dayData, shift }, index) => {
48168
48211
  if (isUptimeMode) {
48169
48212
  doc.setDrawColor(200, 200, 200);
48170
48213
  doc.setLineWidth(0.1);
@@ -48205,12 +48248,34 @@ var LineMonthlyPdfGenerator = ({
48205
48248
  }
48206
48249
  doc.setTextColor(0, 0, 0);
48207
48250
  }
48208
- yPos += 8;
48251
+ yPos += rowHeight;
48209
48252
  });
48210
48253
  if (!isUptimeMode) {
48211
48254
  doc.setLineWidth(0.2);
48212
48255
  doc.setDrawColor(220, 220, 220);
48213
- doc.roundedRect(20, dailyHeaderY, 170, yPos - dailyHeaderY - 3, 1, 1, "S");
48256
+ doc.roundedRect(20, tableHeaderY, 170, yPos - tableHeaderY - 3, 1, 1, "S");
48257
+ }
48258
+ return startIndex + pageEntries.length;
48259
+ };
48260
+ if (dailyEntries.length > 0) {
48261
+ let renderedEntries = renderDailyTablePage(0, dailySectionStartY, dailySectionTitle);
48262
+ while (renderedEntries < dailyEntries.length) {
48263
+ drawFooter();
48264
+ doc.addPage();
48265
+ drawPageChrome();
48266
+ doc.setFontSize(12);
48267
+ doc.setFont("helvetica", "bold");
48268
+ doc.setTextColor(40, 40, 40);
48269
+ doc.text(lineName || "Line", 20, 32);
48270
+ doc.setFont("helvetica", "normal");
48271
+ doc.setTextColor(90, 90, 90);
48272
+ doc.text(`${monthName} \u2022 ${shiftType} \u2022 ${reportStartStr} - ${reportEndStr}`, 20, 40);
48273
+ doc.setTextColor(0, 0, 0);
48274
+ renderedEntries = renderDailyTablePage(
48275
+ renderedEntries,
48276
+ 48,
48277
+ `${dailySectionTitle} (cont.)`
48278
+ );
48214
48279
  }
48215
48280
  } else {
48216
48281
  doc.setFontSize(12);
@@ -48223,20 +48288,9 @@ var LineMonthlyPdfGenerator = ({
48223
48288
  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
48289
  const showCycleTimePoorestPerformers = !isUptimeMode && poorestWorkspaces.some(isCycleTimeWorkspace);
48225
48290
  if (poorestWorkspaces && poorestWorkspaces.length > 0) {
48291
+ drawFooter();
48226
48292
  doc.addPage();
48227
- doc.setFontSize(14);
48228
- doc.setFont("helvetica", "bold");
48229
- doc.setTextColor(50, 50, 50);
48230
- doc.text("OPTIFYE.AI", 20, 15);
48231
- doc.setFontSize(11);
48232
- doc.setFont("helvetica", "normal");
48233
- doc.setTextColor(80, 80, 80);
48234
- const reportText2 = isUptimeMode ? "MONTHLY UTILIZATION REPORT" : "MONTHLY PERFORMANCE REPORT";
48235
- const reportTextWidth2 = doc.getStringUnitWidth(reportText2) * 11 / doc.internal.scaleFactor;
48236
- doc.text(reportText2, doc.internal.pageSize.width - 20 - reportTextWidth2, 15);
48237
- doc.setDrawColor(200, 200, 200);
48238
- doc.setLineWidth(0.5);
48239
- doc.line(20, 20, 190, 20);
48293
+ drawPageChrome();
48240
48294
  doc.setFontSize(18);
48241
48295
  doc.setFont("helvetica", "bold");
48242
48296
  doc.setTextColor(40, 40, 40);
@@ -48302,10 +48356,7 @@ var LineMonthlyPdfGenerator = ({
48302
48356
  doc.setDrawColor(220, 220, 220);
48303
48357
  doc.roundedRect(20, 45, 170, yPos2 - 45 - 5, 1, 1, "S");
48304
48358
  }
48305
- doc.setFontSize(9);
48306
- doc.setTextColor(130, 130, 130);
48307
- const generatedText = `Generated on ${(/* @__PURE__ */ new Date()).toLocaleString("en-IN", { timeZone: "Asia/Kolkata" })}`;
48308
- doc.text(generatedText, 20, 280);
48359
+ drawFooter();
48309
48360
  const fileName = `${lineName || "Line"}_${monthName.replace(" ", "_")}_${shiftType.replace(" ", "_")}.pdf`;
48310
48361
  doc.save(fileName);
48311
48362
  } catch (error) {
@@ -49875,6 +49926,11 @@ var formatHours = (value) => {
49875
49926
  if (Math.abs(rounded) < 0.05) return "0h";
49876
49927
  return Number.isInteger(rounded) ? `${rounded}h` : `${rounded.toFixed(1)}h`;
49877
49928
  };
49929
+ var roundToSingleDecimal = (value) => Math.round(value * 10) / 10;
49930
+ var formatCycleSeconds = (value) => {
49931
+ if (!Number.isFinite(value)) return "0.0s";
49932
+ return `${value.toFixed(1)}s`;
49933
+ };
49878
49934
  var CustomTooltip3 = ({ active, payload, label, isUptimeMode }) => {
49879
49935
  if (!active || !payload || payload.length === 0) return null;
49880
49936
  if (isUptimeMode) {
@@ -49996,21 +50052,30 @@ var WorkspaceMonthlyHistory = ({
49996
50052
  if (!shiftConfig) return null;
49997
50053
  return getShiftWorkDurationSeconds(shiftConfig, selectedShiftId);
49998
50054
  }, [shiftConfig, selectedShiftId]);
49999
- const chartData = React143.useMemo(() => {
50055
+ const analysisMonthlyDataByKey = React143.useMemo(
50056
+ () => new Map(analysisMonthlyData.map((day) => [getDayDateKey(day), day])),
50057
+ [analysisMonthlyData]
50058
+ );
50059
+ const monthlyDataByKey = React143.useMemo(
50060
+ () => new Map(data.map((day) => [getDayDateKey(day), day])),
50061
+ [data]
50062
+ );
50063
+ const rangeDateKeys = React143.useMemo(() => {
50000
50064
  const rangeStartDate = parseDateKeyToDate(normalizedRange.startKey);
50001
50065
  const rangeEndDate = parseDateKeyToDate(normalizedRange.endKey);
50002
- const dayNumbers = [];
50066
+ const keys = [];
50003
50067
  for (let d = new Date(rangeStartDate); d <= rangeEndDate; d.setDate(d.getDate() + 1)) {
50004
- dayNumbers.push(d.getDate());
50068
+ keys.push(buildDateKey(d.getFullYear(), d.getMonth(), d.getDate()));
50005
50069
  }
50070
+ return keys;
50071
+ }, [normalizedRange.endKey, normalizedRange.startKey]);
50072
+ const chartData = React143.useMemo(() => {
50006
50073
  const dailyData = [];
50007
50074
  if (isUptimeMode) {
50008
50075
  let maxHours = 0;
50009
- for (const day of dayNumbers) {
50010
- const dayData = analysisMonthlyData.find((d) => {
50011
- const date = new Date(d.date);
50012
- return date.getDate() === day;
50013
- });
50076
+ for (const dateKey of rangeDateKeys) {
50077
+ const dayData = analysisMonthlyDataByKey.get(dateKey);
50078
+ const dayNumber = Number(dateKey.slice(-2));
50014
50079
  const shiftData = dayData ? getShiftData(dayData, selectedShiftId) : null;
50015
50080
  const hasShiftData = Boolean(shiftData && hasRealData(shiftData));
50016
50081
  const availableSeconds = hasShiftData ? shiftData.availableTimeSeconds ?? shiftWorkSeconds ?? 0 : 0;
@@ -50037,8 +50102,8 @@ var WorkspaceMonthlyHistory = ({
50037
50102
  }
50038
50103
  maxHours = Math.max(maxHours, idleHours + productiveHours);
50039
50104
  dailyData.push({
50040
- hour: getOrdinal2(day),
50041
- timeRange: `Day ${day}`,
50105
+ hour: getOrdinal2(dayNumber),
50106
+ timeRange: `Day ${dayNumber}`,
50042
50107
  productiveHours,
50043
50108
  idleHours,
50044
50109
  utilization
@@ -50049,12 +50114,9 @@ var WorkspaceMonthlyHistory = ({
50049
50114
  }
50050
50115
  let maxOutput = 0;
50051
50116
  let lastSetTarget = 0;
50052
- for (let i = dayNumbers.length - 1; i >= 0; i--) {
50053
- const day = dayNumbers[i];
50054
- const dayData = analysisMonthlyData.find((d) => {
50055
- const date = new Date(d.date);
50056
- return date.getDate() === day;
50057
- });
50117
+ for (let i = rangeDateKeys.length - 1; i >= 0; i--) {
50118
+ const dateKey = rangeDateKeys[i];
50119
+ const dayData = analysisMonthlyDataByKey.get(dateKey);
50058
50120
  const shiftData = dayData ? getShiftData(dayData, selectedShiftId) : null;
50059
50121
  const idealOutput = shiftData ? shiftData.idealOutput : 0;
50060
50122
  if (idealOutput > 0) {
@@ -50062,20 +50124,18 @@ var WorkspaceMonthlyHistory = ({
50062
50124
  break;
50063
50125
  }
50064
50126
  }
50065
- for (const day of dayNumbers) {
50066
- const dayData = analysisMonthlyData.find((d) => {
50067
- const date = new Date(d.date);
50068
- return date.getDate() === day;
50069
- });
50127
+ for (const dateKey of rangeDateKeys) {
50128
+ const dayData = analysisMonthlyDataByKey.get(dateKey);
50129
+ const dayNumber = Number(dateKey.slice(-2));
50070
50130
  const shiftData = dayData ? getShiftData(dayData, selectedShiftId) : null;
50071
50131
  const output = shiftData && hasRealData(shiftData) ? shiftData.output : 0;
50072
50132
  const idealOutput = shiftData ? shiftData.idealOutput : 0;
50073
50133
  if (output > maxOutput) maxOutput = output;
50074
50134
  const color2 = output >= lastSetTarget ? "#00AB45" : "#E34329";
50075
50135
  dailyData.push({
50076
- hour: getOrdinal2(day),
50136
+ hour: getOrdinal2(dayNumber),
50077
50137
  // Using ordinal format (1st, 2nd, 3rd, etc.)
50078
- timeRange: `Day ${day}`,
50138
+ timeRange: `Day ${dayNumber}`,
50079
50139
  output,
50080
50140
  originalOutput: output,
50081
50141
  // For label display
@@ -50089,7 +50149,7 @@ var WorkspaceMonthlyHistory = ({
50089
50149
  const calculatedMax = Math.max(maxOutput, lastSetTarget);
50090
50150
  const yAxisMax = calculatedMax > 0 ? calculatedMax * 1.1 : 100;
50091
50151
  return { data: dailyData, maxOutput, lastSetTarget, yAxisMax };
50092
- }, [analysisMonthlyData, normalizedRange.startKey, normalizedRange.endKey, selectedShiftId, isUptimeMode, shiftWorkSeconds]);
50152
+ }, [analysisMonthlyDataByKey, rangeDateKeys, selectedShiftId, isUptimeMode, shiftWorkSeconds]);
50093
50153
  const yAxisTicks = React143.useMemo(() => {
50094
50154
  if (isUptimeMode) return void 0;
50095
50155
  const max = chartData.yAxisMax;
@@ -50174,7 +50234,7 @@ var WorkspaceMonthlyHistory = ({
50174
50234
  }
50175
50235
  const avgEfficiency = Math.round(totalEfficiency / filteredShifts.length);
50176
50236
  const avgDailyOutput = Math.round(totalOutput / filteredShifts.length);
50177
- const avgCycleTime = Math.round(totalCycleTime / filteredShifts.length);
50237
+ const avgCycleTime = roundToSingleDecimal(totalCycleTime / filteredShifts.length);
50178
50238
  const avgRank = ranks.length > 0 ? Math.round(ranks.reduce((a, b) => a + b, 0) / ranks.length) : null;
50179
50239
  return {
50180
50240
  avgEfficiency,
@@ -50185,12 +50245,16 @@ var WorkspaceMonthlyHistory = ({
50185
50245
  avgIdleTime: Math.round(totalIdleTime / filteredShifts.length)
50186
50246
  };
50187
50247
  }, [analysisMonthlyData, selectedShiftId, isUptimeMode, shiftWorkSeconds]);
50248
+ const trendComparisonLabel = React143.useMemo(
50249
+ () => getMonthlyTrendComparisonLabel(normalizedRange, year, month),
50250
+ [month, normalizedRange, year]
50251
+ );
50188
50252
  const efficiencyDelta = trendSummary?.avg_efficiency?.delta_pp ?? 0;
50189
50253
  const efficiencyImproved = efficiencyDelta >= 0;
50190
50254
  const assemblyRangeCycleTime = React143.useMemo(() => {
50191
50255
  const trendCycleTime = Number(trendSummary?.avg_cycle_time?.current);
50192
50256
  if (Number.isFinite(trendCycleTime) && trendCycleTime > 0) {
50193
- return Math.round(trendCycleTime);
50257
+ return trendCycleTime;
50194
50258
  }
50195
50259
  return metrics2?.avgCycleTime ?? 0;
50196
50260
  }, [trendSummary?.avg_cycle_time?.current, metrics2?.avgCycleTime]);
@@ -50200,17 +50264,17 @@ var WorkspaceMonthlyHistory = ({
50200
50264
  const cycleWorsened = cycleDelta > 0;
50201
50265
  const utilizationDelta = efficiencyDelta;
50202
50266
  const utilizationImproved = utilizationDelta >= 0;
50203
- const utilizationTrendText = `${Math.abs(utilizationDelta).toFixed(1)}% vs last month`;
50267
+ const utilizationTrendText = `${Math.abs(utilizationDelta).toFixed(1)}%`;
50204
50268
  const idleDeltaRaw = trendSummary?.avg_idle_time?.delta_seconds ?? 0;
50205
50269
  const idlePrev = trendSummary?.avg_idle_time?.previous ?? 0;
50206
50270
  const idleDelta = idlePrev ? idleDeltaRaw / idlePrev * 100 : 0;
50207
50271
  const idleImproved = idleDelta <= 0;
50208
- const idleTrendText = `${Math.abs(idleDelta).toFixed(1)}% vs last month`;
50272
+ const idleTrendText = `${Math.abs(idleDelta).toFixed(1)}%`;
50209
50273
  const stoppagesDeltaRaw = trendSummary?.avg_daily_stoppages?.delta_count ?? 0;
50210
50274
  const stoppagesPrev = trendSummary?.avg_daily_stoppages?.previous ?? 0;
50211
50275
  const stoppagesDelta = stoppagesPrev ? stoppagesDeltaRaw / stoppagesPrev * 100 : 0;
50212
50276
  const stoppagesImproved = stoppagesDelta <= 0;
50213
- const stoppagesTrendText = `${Math.abs(stoppagesDelta).toFixed(1)}% vs last month`;
50277
+ const stoppagesTrendText = `${Math.abs(stoppagesDelta).toFixed(1)}%`;
50214
50278
  const calendarData = React143.useMemo(() => {
50215
50279
  const startOfMonth2 = new Date(year, month, 1);
50216
50280
  const endOfMonth2 = new Date(year, month + 1, 0);
@@ -50219,20 +50283,15 @@ var WorkspaceMonthlyHistory = ({
50219
50283
  if (startOffset === -1) startOffset = 6;
50220
50284
  const calendar = Array(startOffset).fill(null);
50221
50285
  for (let day = 1; day <= totalDays; day++) {
50222
- const dayData = data.find((d) => {
50223
- const date = new Date(d.date);
50224
- return date.getDate() === day;
50225
- });
50286
+ const dateKey = buildDateKey(year, month, day);
50287
+ const dayData = monthlyDataByKey.get(dateKey);
50226
50288
  calendar.push(dayData || null);
50227
50289
  }
50228
50290
  return { calendar, startOffset };
50229
- }, [data, month, year]);
50291
+ }, [monthlyDataByKey, month, year]);
50230
50292
  const handleDayClick = React143.useCallback((day) => {
50231
50293
  if (!day) return;
50232
- const year2 = day.date.getFullYear();
50233
- const month2 = String(day.date.getMonth() + 1).padStart(2, "0");
50234
- const dayOfMonth = String(day.date.getDate()).padStart(2, "0");
50235
- const formattedDate = `${year2}-${month2}-${dayOfMonth}`;
50294
+ const formattedDate = getDayDateKey(day);
50236
50295
  trackCoreEvent("Workspace Monthly History Day Clicked", {
50237
50296
  source: "monthly_history",
50238
50297
  workspace_id: workspaceId,
@@ -50371,20 +50430,17 @@ var WorkspaceMonthlyHistory = ({
50371
50430
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-2xl font-bold text-gray-900", children: formatIdleTime(metrics2?.avgIdleTime ?? 0) }),
50372
50431
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-1 ${idleImproved ? "bg-emerald-50 text-emerald-600" : "bg-red-50 text-red-600"} px-2 py-0.5 rounded-full text-[10px] font-medium whitespace-nowrap flex-shrink-0`, children: [
50373
50432
  idleImproved ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDown, { className: "w-3 h-3" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "w-3 h-3" }),
50374
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: idleTrendText })
50433
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: `${idleTrendText} vs ${trendComparisonLabel}` })
50375
50434
  ] })
50376
50435
  ] })
50377
50436
  ] }),
50378
50437
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-lg shadow-sm border border-gray-100 p-4 flex flex-col justify-between", children: [
50379
50438
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-semibold text-gray-600 mb-1", children: "Avg Cycle Time" }),
50380
50439
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 flex-nowrap", children: [
50381
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-2xl font-bold text-gray-900", children: [
50382
- assemblyRangeCycleTime,
50383
- "s"
50384
- ] }),
50440
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-2xl font-bold text-gray-900", children: formatCycleSeconds(assemblyRangeCycleTime) }),
50385
50441
  /* @__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: [
50386
50442
  cycleWorsened ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "w-3 h-3" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDown, { className: "w-3 h-3" }),
50387
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: `${Math.abs(cycleDelta).toFixed(1)}% vs last month` })
50443
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: `${Math.abs(cycleDelta).toFixed(1)}% vs ${trendComparisonLabel}` })
50388
50444
  ] })
50389
50445
  ] })
50390
50446
  ] })
@@ -50395,10 +50451,10 @@ var WorkspaceMonthlyHistory = ({
50395
50451
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-2xl font-bold text-gray-900", children: isUptimeMode ? `${metrics2?.avgUtilization ?? 0}%` : `${metrics2?.avgEfficiency ?? 0}%` }),
50396
50452
  isUptimeMode ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-1 ${utilizationImproved ? "bg-emerald-50 text-emerald-600" : "bg-red-50 text-red-600"} px-2 py-0.5 rounded-full text-[10px] font-medium whitespace-nowrap flex-shrink-0`, children: [
50397
50453
  utilizationImproved ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "w-3 h-3" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDown, { className: "w-3 h-3" }),
50398
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: utilizationTrendText })
50454
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: `${utilizationTrendText} vs ${trendComparisonLabel}` })
50399
50455
  ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-1 ${efficiencyImproved ? "bg-emerald-50 text-emerald-600" : "bg-red-50 text-red-600"} px-2 py-0.5 rounded-full text-[10px] font-medium whitespace-nowrap flex-shrink-0`, children: [
50400
50456
  efficiencyImproved ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "w-3 h-3" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDown, { className: "w-3 h-3" }),
50401
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: `${Math.abs(efficiencyDelta).toFixed(1)}% vs last month` })
50457
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: `${Math.abs(efficiencyDelta).toFixed(1)}% vs ${trendComparisonLabel}` })
50402
50458
  ] })
50403
50459
  ] })
50404
50460
  ] }),
@@ -50408,7 +50464,7 @@ var WorkspaceMonthlyHistory = ({
50408
50464
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-2xl font-bold text-gray-900", children: formatIdleTime(metrics2?.avgIdleTime ?? 0) }),
50409
50465
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-1 ${idleImproved ? "bg-emerald-50 text-emerald-600" : "bg-red-50 text-red-600"} px-2 py-0.5 rounded-full text-[10px] font-medium whitespace-nowrap flex-shrink-0`, children: [
50410
50466
  idleImproved ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDown, { className: "w-3 h-3" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "w-3 h-3" }),
50411
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: idleTrendText })
50467
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: `${idleTrendText} vs ${trendComparisonLabel}` })
50412
50468
  ] })
50413
50469
  ] })
50414
50470
  ] }),
@@ -50421,10 +50477,10 @@ var WorkspaceMonthlyHistory = ({
50421
50477
  ] }),
50422
50478
  isUptimeMode ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-1 ${stoppagesImproved ? "bg-emerald-50 text-emerald-600" : "bg-red-50 text-red-600"} px-2 py-0.5 rounded-full text-[10px] font-medium whitespace-nowrap flex-shrink-0`, children: [
50423
50479
  stoppagesImproved ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDown, { className: "w-3 h-3" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "w-3 h-3" }),
50424
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: stoppagesTrendText })
50480
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: `${stoppagesTrendText} vs ${trendComparisonLabel}` })
50425
50481
  ] }) : /* @__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: [
50426
50482
  cycleWorsened ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "w-3 h-3" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDown, { className: "w-3 h-3" }),
50427
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: `${Math.abs(cycleDelta).toFixed(1)}% vs last month` })
50483
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: `${Math.abs(cycleDelta).toFixed(1)}% vs ${trendComparisonLabel}` })
50428
50484
  ] })
50429
50485
  ] })
50430
50486
  ] })
@@ -51136,6 +51192,7 @@ var getShiftDisplayName2 = (shiftId, availableShifts) => {
51136
51192
  var WorkspaceMonthlyPdfGenerator = ({
51137
51193
  workspaceId,
51138
51194
  workspaceName,
51195
+ lineName,
51139
51196
  monthlyData,
51140
51197
  analysisData,
51141
51198
  selectedMonth,
@@ -51180,13 +51237,20 @@ var WorkspaceMonthlyPdfGenerator = ({
51180
51237
  startKey: rangeStart || monthBounds.startKey,
51181
51238
  endKey: rangeEnd || monthBounds.endKey
51182
51239
  };
51240
+ const boundsSourceKey = rangeStart || rangeEnd || monthBounds.startKey;
51241
+ const boundsSourceDate = parseDateKeyToDate(boundsSourceKey);
51242
+ const effectiveBounds = getMonthKeyBounds(boundsSourceDate.getFullYear(), boundsSourceDate.getMonth());
51183
51243
  const normalizedRange = normalizeDateKeyRange(
51184
51244
  requestedRange.startKey,
51185
51245
  requestedRange.endKey,
51186
- monthBounds.startKey,
51187
- monthBounds.endKey
51246
+ effectiveBounds.startKey,
51247
+ effectiveBounds.endKey
51248
+ );
51249
+ const fullRange = isFullMonthRange(
51250
+ normalizedRange,
51251
+ boundsSourceDate.getFullYear(),
51252
+ boundsSourceDate.getMonth()
51188
51253
  );
51189
- const fullRange = isFullMonthRange(normalizedRange, selectedYear, selectedMonth);
51190
51254
  const reportStartDate = parseDateKeyToDate(normalizedRange.startKey);
51191
51255
  const reportEndDate = parseDateKeyToDate(normalizedRange.endKey);
51192
51256
  const reportStartStr = reportStartDate.toLocaleDateString("en-IN", {
@@ -51201,36 +51265,57 @@ var WorkspaceMonthlyPdfGenerator = ({
51201
51265
  year: "numeric",
51202
51266
  timeZone: "Asia/Kolkata"
51203
51267
  });
51268
+ const doc = new jsPDF.jsPDF();
51269
+ const pageHeight = typeof doc.internal.pageSize.height === "number" ? Number(doc.internal.pageSize.height) : 297;
51270
+ const footerY = pageHeight - 17;
51271
+ const maxContentY = footerY - 10;
51272
+ const generatedText = `Generated on ${(/* @__PURE__ */ new Date()).toLocaleString("en-IN", { timeZone: "Asia/Kolkata" })}`;
51273
+ const resolvedLineName = lineName?.trim() || "Line";
51274
+ const dailySectionTitle = isUptimeMode ? "Daily Utilization Summary" : "Daily Performance Summary";
51275
+ const monthName = reportStartDate.toLocaleDateString("en-IN", {
51276
+ month: "long",
51277
+ year: "numeric",
51278
+ timeZone: "Asia/Kolkata"
51279
+ });
51204
51280
  trackCoreEvent("Workspace Monthly PDF Export Clicked", {
51205
51281
  workspace_id: workspaceId,
51206
51282
  workspace_name: workspaceName,
51207
- month: selectedMonth,
51208
- year: selectedYear,
51283
+ line_name: resolvedLineName,
51284
+ month: reportStartDate.getMonth(),
51285
+ year: reportStartDate.getFullYear(),
51209
51286
  shift_id: selectedShiftId,
51210
51287
  range_start: normalizedRange.startKey,
51211
51288
  range_end: normalizedRange.endKey,
51212
51289
  is_full_month: fullRange
51213
51290
  });
51214
- const doc = new jsPDF.jsPDF();
51215
- doc.setFontSize(14);
51216
- doc.setFont("helvetica", "bold");
51217
- doc.setTextColor(50, 50, 50);
51218
- doc.text("OPTIFYE.AI", 20, 15);
51219
- doc.setFontSize(11);
51220
- doc.setFont("helvetica", "normal");
51221
- doc.setTextColor(80, 80, 80);
51222
- const reportText = isUptimeMode ? "MONTHLY UTILIZATION REPORT" : "MONTHLY PERFORMANCE REPORT";
51223
- const reportTextWidth = doc.getStringUnitWidth(reportText) * 11 / doc.internal.scaleFactor;
51224
- doc.text(reportText, doc.internal.pageSize.width - 20 - reportTextWidth, 15);
51225
- doc.setDrawColor(200, 200, 200);
51226
- doc.setLineWidth(0.5);
51227
- doc.line(20, 20, 190, 20);
51291
+ const drawPageChrome = () => {
51292
+ doc.setFontSize(14);
51293
+ doc.setFont("helvetica", "bold");
51294
+ doc.setTextColor(50, 50, 50);
51295
+ doc.text("OPTIFYE.AI", 20, 15);
51296
+ doc.setFontSize(11);
51297
+ doc.setFont("helvetica", "normal");
51298
+ doc.setTextColor(80, 80, 80);
51299
+ const reportText = isUptimeMode ? "MONTHLY UTILIZATION REPORT" : "MONTHLY PERFORMANCE REPORT";
51300
+ const reportTextWidth = doc.getStringUnitWidth(reportText) * 11 / doc.internal.scaleFactor;
51301
+ doc.text(reportText, doc.internal.pageSize.width - 20 - reportTextWidth, 15);
51302
+ doc.setDrawColor(200, 200, 200);
51303
+ doc.setLineWidth(0.5);
51304
+ doc.line(20, 20, 190, 20);
51305
+ };
51306
+ const drawFooter = () => {
51307
+ doc.setFontSize(9);
51308
+ doc.setTextColor(130, 130, 130);
51309
+ doc.text(generatedText, 20, footerY);
51310
+ doc.setTextColor(0, 0, 0);
51311
+ };
51312
+ drawPageChrome();
51228
51313
  doc.setFillColor(250, 250, 250);
51229
51314
  doc.roundedRect(15, 25, 180, 55, 3, 3, "F");
51230
51315
  doc.setFontSize(32);
51231
51316
  doc.setFont("helvetica", "bold");
51232
51317
  doc.setTextColor(0, 0, 0);
51233
- doc.text("Line 1", 20, 40);
51318
+ doc.text(resolvedLineName, 20, 40);
51234
51319
  doc.setFontSize(22);
51235
51320
  doc.setFont("helvetica", "normal");
51236
51321
  doc.setTextColor(40, 40, 40);
@@ -51238,11 +51323,6 @@ var WorkspaceMonthlyPdfGenerator = ({
51238
51323
  doc.setFontSize(13);
51239
51324
  doc.setFont("helvetica", "normal");
51240
51325
  doc.setTextColor(60, 60, 60);
51241
- const monthName = new Date(selectedYear, selectedMonth).toLocaleDateString("en-IN", {
51242
- month: "long",
51243
- year: "numeric",
51244
- timeZone: "Asia/Kolkata"
51245
- });
51246
51326
  const shiftType = getShiftDisplayName2(selectedShiftId, availableShifts);
51247
51327
  doc.text(`${monthName}`, 20, 65);
51248
51328
  doc.text(`${shiftType}`, 20, 73);
@@ -51254,11 +51334,12 @@ var WorkspaceMonthlyPdfGenerator = ({
51254
51334
  doc.setLineWidth(0.8);
51255
51335
  doc.line(20, 90, 190, 90);
51256
51336
  const reportData = analysisData ? analysisData : filterDataByDateKeyRange(monthlyData, normalizedRange);
51257
- const validDays = reportData.filter((day) => {
51258
- const date = new Date(day.date);
51259
- return date.getMonth() === selectedMonth && date.getFullYear() === selectedYear;
51260
- });
51337
+ const validDays = reportData;
51261
51338
  const validShifts = validDays.map((day) => getShiftData(day, selectedShiftId)).filter(hasShiftData);
51339
+ const dailyEntries = validDays.map((dayData) => {
51340
+ const shift = getShiftData(dayData, selectedShiftId);
51341
+ return { dayData, shift };
51342
+ }).filter(({ shift }) => hasShiftData(shift)).sort((left, right) => getDayDateKey(right.dayData).localeCompare(getDayDateKey(left.dayData)));
51262
51343
  const filteredShifts = isUptimeMode ? validShifts : validShifts.filter((shift) => (shift.efficiency ?? 0) >= 5);
51263
51344
  const monthlyMetrics = filteredShifts.length > 0 ? isUptimeMode ? (() => {
51264
51345
  const totalIdleTime = filteredShifts.reduce((sum, shift) => sum + shift.idleTime, 0);
@@ -51385,21 +51466,27 @@ var WorkspaceMonthlyPdfGenerator = ({
51385
51466
  doc.setLineWidth(0.8);
51386
51467
  const separatorY = isAssemblyWorkspaceAndNotUptime ? 150 : 180;
51387
51468
  doc.line(20, separatorY, 190, separatorY);
51388
- doc.setFillColor(245, 245, 245);
51389
51469
  const dailySectionY = isAssemblyWorkspaceAndNotUptime ? 155 : 185;
51390
- doc.roundedRect(15, dailySectionY, 180, 85, 3, 3, "F");
51391
- doc.setFontSize(18);
51392
- doc.setFont("helvetica", "bold");
51393
- doc.setTextColor(40, 40, 40);
51394
- doc.text(isUptimeMode ? "Daily Utilization Summary" : "Daily Performance Summary", 20, dailySectionY + 10);
51395
- doc.setTextColor(0, 0, 0);
51396
- if (validDays.length > 0) {
51470
+ const renderDailyTablePage = (startIndex, sectionY, title) => {
51471
+ const rowHeight = 8;
51472
+ const tableHeaderY = sectionY + 15;
51473
+ const textY = tableHeaderY + 5;
51474
+ const firstRowY = textY + 10;
51475
+ const rowsPerPage = Math.max(1, Math.floor((maxContentY - firstRowY) / rowHeight));
51476
+ const pageEntries = dailyEntries.slice(startIndex, startIndex + rowsPerPage);
51477
+ const endY = firstRowY + pageEntries.length * rowHeight;
51478
+ const sectionHeight = Math.max(40, endY - sectionY + 5);
51479
+ doc.setFillColor(245, 245, 245);
51480
+ doc.roundedRect(15, sectionY, 180, sectionHeight, 3, 3, "F");
51481
+ doc.setFontSize(18);
51482
+ doc.setFont("helvetica", "bold");
51483
+ doc.setTextColor(40, 40, 40);
51484
+ doc.text(title, 20, sectionY + 10);
51485
+ doc.setTextColor(0, 0, 0);
51397
51486
  doc.setFontSize(10);
51398
51487
  doc.setFont("helvetica", "bold");
51399
51488
  doc.setFillColor(240, 240, 240);
51400
- const tableHeaderY = dailySectionY + 15;
51401
51489
  doc.roundedRect(20, tableHeaderY, 170, 7, 1, 1, "F");
51402
- const textY = tableHeaderY + 5;
51403
51490
  doc.text("Date", 25, textY);
51404
51491
  doc.text(isUptimeMode ? "Productive" : isAssemblyWorkspace ? "Actual Cycle Time" : "Actual", 60, textY);
51405
51492
  doc.text(isUptimeMode ? "Idle" : isAssemblyWorkspace ? "Standard Cycle Time" : "Standard", 95, textY);
@@ -51409,12 +51496,8 @@ var WorkspaceMonthlyPdfGenerator = ({
51409
51496
  doc.setDrawColor(220, 220, 220);
51410
51497
  doc.line(20, textY + 3, 190, textY + 3);
51411
51498
  doc.setFont("helvetica", "normal");
51412
- let yPos = textY + 10;
51413
- const recentDays = validDays.slice(-10).reverse();
51414
- recentDays.forEach((dayData, index) => {
51415
- if (yPos > 260) return;
51416
- const shift = getShiftData(dayData, selectedShiftId);
51417
- if (!hasShiftData(shift)) return;
51499
+ let yPos = firstRowY;
51500
+ pageEntries.forEach(({ dayData, shift }, index) => {
51418
51501
  if (index % 2 === 0) {
51419
51502
  doc.setFillColor(252, 252, 252);
51420
51503
  doc.roundedRect(20, yPos - 4, 170, 7, 1, 1, "F");
@@ -51447,11 +51530,35 @@ var WorkspaceMonthlyPdfGenerator = ({
51447
51530
  doc.text(`${shift.efficiency.toFixed(1)}%`, 135, yPos);
51448
51531
  drawStatusMark(doc, 171, yPos - 0.3, shift.efficiency >= effectiveLegend.green_min);
51449
51532
  }
51450
- yPos += 8;
51533
+ yPos += rowHeight;
51451
51534
  });
51452
51535
  doc.setLineWidth(0.2);
51453
51536
  doc.setDrawColor(220, 220, 220);
51454
51537
  doc.roundedRect(20, tableHeaderY, 170, yPos - tableHeaderY - 3, 1, 1, "S");
51538
+ return startIndex + pageEntries.length;
51539
+ };
51540
+ if (dailyEntries.length > 0) {
51541
+ let renderedEntries = renderDailyTablePage(0, dailySectionY, dailySectionTitle);
51542
+ while (renderedEntries < dailyEntries.length) {
51543
+ drawFooter();
51544
+ doc.addPage();
51545
+ drawPageChrome();
51546
+ doc.setFontSize(12);
51547
+ doc.setFont("helvetica", "bold");
51548
+ doc.setTextColor(40, 40, 40);
51549
+ doc.text(resolvedLineName, 20, 32);
51550
+ doc.setFont("helvetica", "normal");
51551
+ doc.text(getWorkspaceDisplayName(workspaceName), 20, 40);
51552
+ doc.setFontSize(10);
51553
+ doc.setTextColor(90, 90, 90);
51554
+ doc.text(`${monthName} \u2022 ${shiftType} \u2022 ${reportStartStr} - ${reportEndStr}`, 20, 48);
51555
+ doc.setTextColor(0, 0, 0);
51556
+ renderedEntries = renderDailyTablePage(
51557
+ renderedEntries,
51558
+ 56,
51559
+ `${dailySectionTitle} (cont.)`
51560
+ );
51561
+ }
51455
51562
  } else {
51456
51563
  doc.setFontSize(12);
51457
51564
  doc.setFont("helvetica", "normal");
@@ -51459,10 +51566,7 @@ var WorkspaceMonthlyPdfGenerator = ({
51459
51566
  doc.text("No daily data available for this month", 25, dailySectionY + 30);
51460
51567
  doc.setTextColor(0, 0, 0);
51461
51568
  }
51462
- doc.setFontSize(9);
51463
- doc.setTextColor(130, 130, 130);
51464
- const generatedText = `Generated on ${(/* @__PURE__ */ new Date()).toLocaleString("en-IN", { timeZone: "Asia/Kolkata" })}`;
51465
- doc.text(generatedText, 20, 280);
51569
+ drawFooter();
51466
51570
  const fileName = `${getWorkspaceDisplayName(workspaceName)}_${monthName.replace(" ", "_")}_${shiftType.replace(" ", "_")}.pdf`;
51467
51571
  doc.save(fileName);
51468
51572
  } catch (error) {
@@ -63611,7 +63715,9 @@ var KPIDetailView = ({
63611
63715
  month: currentMonth,
63612
63716
  year: currentYear,
63613
63717
  shiftId: selectedShiftId,
63614
- companyId: resolvedCompanyId
63718
+ companyId: resolvedCompanyId,
63719
+ startDate: isFullRange ? void 0 : rangeStart,
63720
+ endDate: isFullRange ? void 0 : rangeEnd
63615
63721
  });
63616
63722
  const configuredTimezone = timezone || dashboardConfig.dateTimeConfig?.defaultTimezone || "UTC";
63617
63723
  React143.useMemo(() => getCurrentTimeInZone(configuredTimezone), [configuredTimezone]);
@@ -71066,15 +71172,26 @@ var WorkspaceDetailView = ({
71066
71172
  const range = React143.useMemo(() => ({ startKey: rangeStart, endKey: rangeEnd }), [rangeStart, rangeEnd]);
71067
71173
  const isFullRange = React143.useMemo(() => isFullMonthRange(range, selectedYear, selectedMonth), [range, selectedYear, selectedMonth]);
71068
71174
  React143.useEffect(() => {
71175
+ const hasExternalRange = typeof urlRangeStart === "string" || typeof urlRangeEnd === "string";
71176
+ if (!hasExternalRange) {
71177
+ return;
71178
+ }
71179
+ const targetStartKey = typeof urlRangeStart === "string" ? urlRangeStart : typeof urlRangeEnd === "string" ? urlRangeEnd : monthBounds.startKey;
71180
+ const targetStartDate = parseDateKeyToDate(targetStartKey);
71181
+ const targetMonth = targetStartDate.getMonth();
71182
+ const targetYear = targetStartDate.getFullYear();
71183
+ const targetBounds = getMonthKeyBounds(targetYear, targetMonth);
71069
71184
  const normalized = normalizeDateKeyRange(
71070
- typeof urlRangeStart === "string" ? urlRangeStart : monthBounds.startKey,
71071
- typeof urlRangeEnd === "string" ? urlRangeEnd : monthBounds.endKey,
71072
- monthBounds.startKey,
71073
- monthBounds.endKey
71185
+ typeof urlRangeStart === "string" ? urlRangeStart : targetBounds.startKey,
71186
+ typeof urlRangeEnd === "string" ? urlRangeEnd : targetBounds.endKey,
71187
+ targetBounds.startKey,
71188
+ targetBounds.endKey
71074
71189
  );
71190
+ setSelectedMonth(targetMonth);
71191
+ setSelectedYear(targetYear);
71075
71192
  setRangeStart(normalized.startKey);
71076
71193
  setRangeEnd(normalized.endKey);
71077
- }, [urlRangeStart, urlRangeEnd, monthBounds.startKey, monthBounds.endKey]);
71194
+ }, [urlRangeStart, urlRangeEnd, monthBounds.startKey]);
71078
71195
  const isHistoricView = Boolean(date && parsedShiftId !== void 0);
71079
71196
  const initialTab = getInitialTab(sourceType, defaultTab, fromMonthly, date);
71080
71197
  const [activeTab, setActiveTab] = React143.useState(initialTab);
@@ -71094,7 +71211,9 @@ var WorkspaceDetailView = ({
71094
71211
  month: selectedMonth,
71095
71212
  year: selectedYear,
71096
71213
  shiftId: selectedShift,
71097
- companyId: dashboardConfig?.entityConfig?.companyId
71214
+ companyId: dashboardConfig?.entityConfig?.companyId,
71215
+ startDate: isFullRange ? void 0 : rangeStart,
71216
+ endDate: isFullRange ? void 0 : rangeEnd
71098
71217
  });
71099
71218
  const {
71100
71219
  isFastSlowClipFiltersEnabled,
@@ -71361,6 +71480,7 @@ var WorkspaceDetailView = ({
71361
71480
  line_name: "",
71362
71481
  line_assembly_enabled: cachedOverviewMetrics.assembly_enabled === true,
71363
71482
  workspace_name: cachedOverviewMetrics.workspace_name,
71483
+ workspace_display_name: cachedOverviewMetrics.displayName ?? null,
71364
71484
  workspace_id: cachedOverviewMetrics.workspace_uuid || "",
71365
71485
  company_id: cachedOverviewMetrics.company_id,
71366
71486
  company_name: "",
@@ -71593,12 +71713,13 @@ var WorkspaceDetailView = ({
71593
71713
  console.warn("Skipping invalid metric:", metric);
71594
71714
  return;
71595
71715
  }
71596
- const dateObj = new Date(metric.date);
71597
- const dateKey = dateObj.toISOString().split("T")[0];
71716
+ const dateKey = getDateKeyFromValue(metric.date);
71717
+ const dateObj = parseDateKeyToDate(dateKey);
71598
71718
  let dayEntry = dayDataMap.get(dateKey);
71599
71719
  if (!dayEntry) {
71600
71720
  dayEntry = {
71601
71721
  date: dateObj,
71722
+ dateKey,
71602
71723
  shifts: {}
71603
71724
  // Multi-shift structure: Record<number, CalendarShiftData>
71604
71725
  };
@@ -71643,6 +71764,7 @@ var WorkspaceDetailView = ({
71643
71764
  });
71644
71765
  const processedData = Array.from(dayDataMap.values()).filter((entry) => Object.keys(entry.shifts).length > 0).map((entry) => ({
71645
71766
  date: entry.date,
71767
+ dateKey: entry.dateKey,
71646
71768
  shifts: entry.shifts
71647
71769
  }));
71648
71770
  console.log(`[handleMonthlyDataLoaded] Transformed data for calendar:`, {
@@ -71675,7 +71797,17 @@ var WorkspaceDetailView = ({
71675
71797
  const analysisMonthlyData = React143.useMemo(() => {
71676
71798
  return filterDataByDateKeyRange(monthlyData, range);
71677
71799
  }, [monthlyData, range]);
71678
- const formattedWorkspaceName = displayName || formatWorkspaceName3(workspace?.workspace_name || "", resolvedLineId);
71800
+ const formattedWorkspaceName = displayName || workspace?.workspace_display_name || formatWorkspaceName3(workspace?.workspace_name || "", resolvedLineId);
71801
+ const resolvedLineName = React143.useMemo(() => {
71802
+ const workspaceLineName = workspace?.line_name?.trim();
71803
+ if (workspaceLineName) {
71804
+ return workspaceLineName;
71805
+ }
71806
+ if (resolvedLineId && dashboardConfig?.entityConfig) {
71807
+ return getLineDisplayName(dashboardConfig.entityConfig, resolvedLineId);
71808
+ }
71809
+ return "Line";
71810
+ }, [dashboardConfig?.entityConfig, resolvedLineId, workspace?.line_name]);
71679
71811
  const workspaceCycleTimeEligibility = workspace ? {
71680
71812
  line_assembly_enabled: workspace.line_assembly_enabled,
71681
71813
  action_family: workspace.action_family,
@@ -72220,6 +72352,7 @@ var WorkspaceDetailView = ({
72220
72352
  {
72221
72353
  workspaceId,
72222
72354
  workspaceName: formattedWorkspaceName,
72355
+ lineName: resolvedLineName,
72223
72356
  monthlyData,
72224
72357
  analysisData: analysisMonthlyData,
72225
72358
  selectedMonth,
@@ -81849,6 +81982,8 @@ exports.getCurrentWeekFullRange = getCurrentWeekFullRange;
81849
81982
  exports.getCurrentWeekToDateRange = getCurrentWeekToDateRange;
81850
81983
  exports.getDashboardHeaderTimeInZone = getDashboardHeaderTimeInZone;
81851
81984
  exports.getDateKeyFromDate = getDateKeyFromDate;
81985
+ exports.getDateKeyFromValue = getDateKeyFromValue;
81986
+ exports.getDayDateKey = getDayDateKey;
81852
81987
  exports.getDaysDifferenceInZone = getDaysDifferenceInZone;
81853
81988
  exports.getDefaultCameraStreamUrl = getDefaultCameraStreamUrl;
81854
81989
  exports.getDefaultLineId = getDefaultLineId;
@@ -81859,6 +81994,7 @@ exports.getManufacturingInsights = getManufacturingInsights;
81859
81994
  exports.getMetricsTablePrefix = getMetricsTablePrefix;
81860
81995
  exports.getMonthKeyBounds = getMonthKeyBounds;
81861
81996
  exports.getMonthWeekRanges = getMonthWeekRanges;
81997
+ exports.getMonthlyTrendComparisonLabel = getMonthlyTrendComparisonLabel;
81862
81998
  exports.getNextUpdateInterval = getNextUpdateInterval;
81863
81999
  exports.getOperationalDate = getOperationalDate;
81864
82000
  exports.getRoleAssignmentKind = getRoleAssignmentKind;