@optifye/dashboard-core 6.11.39 → 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,136 +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 getMonthlyTrendComparisonLabel = (range, year, monthIndex) => {
3427
- if (isFullMonthRange(range, year, monthIndex)) {
3428
- return "last month";
3429
- }
3430
- return range.startKey === range.endKey ? "previous day" : "previous range";
3431
- };
3432
- var getMonthWeekRanges = (year, monthIndex, timezone, maxKey) => {
3433
- const totalDays = new Date(year, monthIndex + 1, 0).getDate();
3434
- const ranges = [];
3435
- let currentStartDay = 1;
3436
- for (let day = 1; day <= totalDays; day += 1) {
3437
- const zonedDate = dateFnsTz.toZonedTime(new Date(year, monthIndex, day), timezone);
3438
- const dayOfWeek = zonedDate.getDay();
3439
- const isEndOfWeek = dayOfWeek === 0 || day === totalDays;
3440
- if (isEndOfWeek) {
3441
- const startKey = buildDateKey(year, monthIndex, currentStartDay);
3442
- const endKey = buildDateKey(year, monthIndex, day);
3443
- if (maxKey && startKey > maxKey) {
3444
- break;
3445
- }
3446
- const clampedEndKey = maxKey && endKey > maxKey ? maxKey : endKey;
3447
- if (clampedEndKey < startKey) {
3448
- break;
3449
- }
3450
- const labelStart = formatDateKeyForDisplay(startKey, "MMM d");
3451
- const labelEnd = formatDateKeyForDisplay(clampedEndKey, "MMM d");
3452
- ranges.push({
3453
- startKey,
3454
- endKey: clampedEndKey,
3455
- label: `Week of ${labelStart} - ${labelEnd}`
3456
- });
3457
- currentStartDay = day + 1;
3458
- }
3459
- }
3460
- return ranges;
3461
- };
3462
- var formatRangeLabel = (range, fullMonthLabel) => {
3463
- if (!range.startKey || !range.endKey) return fullMonthLabel;
3464
- if (range.startKey === range.endKey) {
3465
- return formatDateKeyForDisplay(range.startKey, "MMM d, yyyy");
3466
- }
3467
- const startLabel = formatDateKeyForDisplay(range.startKey, "MMM d");
3468
- const endLabel = formatDateKeyForDisplay(range.endKey, "MMM d, yyyy");
3469
- return `${startLabel} - ${endLabel}`;
3470
- };
3471
- var filterDataByDateKeyRange = (data, range) => {
3472
- if (!range.startKey || !range.endKey) return data;
3473
- return (data || []).filter((item) => {
3474
- const dateKey = typeof item.date === "string" ? item.date : getDateKeyFromDate(item.date);
3475
- return dateKey >= range.startKey && dateKey <= range.endKey;
3476
- });
3477
- };
3478
-
3479
- // src/lib/utils/shifts.ts
3480
3496
  var DEFAULT_DAY_SHIFT_START = "06:00";
3481
3497
  var DEFAULT_NIGHT_SHIFT_START = "18:00";
3482
3498
  var DEFAULT_TRANSITION_MINUTES = 15;
@@ -49910,6 +49926,7 @@ var formatHours = (value) => {
49910
49926
  if (Math.abs(rounded) < 0.05) return "0h";
49911
49927
  return Number.isInteger(rounded) ? `${rounded}h` : `${rounded.toFixed(1)}h`;
49912
49928
  };
49929
+ var roundToSingleDecimal = (value) => Math.round(value * 10) / 10;
49913
49930
  var formatCycleSeconds = (value) => {
49914
49931
  if (!Number.isFinite(value)) return "0.0s";
49915
49932
  return `${value.toFixed(1)}s`;
@@ -50035,21 +50052,30 @@ var WorkspaceMonthlyHistory = ({
50035
50052
  if (!shiftConfig) return null;
50036
50053
  return getShiftWorkDurationSeconds(shiftConfig, selectedShiftId);
50037
50054
  }, [shiftConfig, selectedShiftId]);
50038
- 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(() => {
50039
50064
  const rangeStartDate = parseDateKeyToDate(normalizedRange.startKey);
50040
50065
  const rangeEndDate = parseDateKeyToDate(normalizedRange.endKey);
50041
- const dayNumbers = [];
50066
+ const keys = [];
50042
50067
  for (let d = new Date(rangeStartDate); d <= rangeEndDate; d.setDate(d.getDate() + 1)) {
50043
- dayNumbers.push(d.getDate());
50068
+ keys.push(buildDateKey(d.getFullYear(), d.getMonth(), d.getDate()));
50044
50069
  }
50070
+ return keys;
50071
+ }, [normalizedRange.endKey, normalizedRange.startKey]);
50072
+ const chartData = React143.useMemo(() => {
50045
50073
  const dailyData = [];
50046
50074
  if (isUptimeMode) {
50047
50075
  let maxHours = 0;
50048
- for (const day of dayNumbers) {
50049
- const dayData = analysisMonthlyData.find((d) => {
50050
- const date = new Date(d.date);
50051
- return date.getDate() === day;
50052
- });
50076
+ for (const dateKey of rangeDateKeys) {
50077
+ const dayData = analysisMonthlyDataByKey.get(dateKey);
50078
+ const dayNumber = Number(dateKey.slice(-2));
50053
50079
  const shiftData = dayData ? getShiftData(dayData, selectedShiftId) : null;
50054
50080
  const hasShiftData = Boolean(shiftData && hasRealData(shiftData));
50055
50081
  const availableSeconds = hasShiftData ? shiftData.availableTimeSeconds ?? shiftWorkSeconds ?? 0 : 0;
@@ -50076,8 +50102,8 @@ var WorkspaceMonthlyHistory = ({
50076
50102
  }
50077
50103
  maxHours = Math.max(maxHours, idleHours + productiveHours);
50078
50104
  dailyData.push({
50079
- hour: getOrdinal2(day),
50080
- timeRange: `Day ${day}`,
50105
+ hour: getOrdinal2(dayNumber),
50106
+ timeRange: `Day ${dayNumber}`,
50081
50107
  productiveHours,
50082
50108
  idleHours,
50083
50109
  utilization
@@ -50088,12 +50114,9 @@ var WorkspaceMonthlyHistory = ({
50088
50114
  }
50089
50115
  let maxOutput = 0;
50090
50116
  let lastSetTarget = 0;
50091
- for (let i = dayNumbers.length - 1; i >= 0; i--) {
50092
- const day = dayNumbers[i];
50093
- const dayData = analysisMonthlyData.find((d) => {
50094
- const date = new Date(d.date);
50095
- return date.getDate() === day;
50096
- });
50117
+ for (let i = rangeDateKeys.length - 1; i >= 0; i--) {
50118
+ const dateKey = rangeDateKeys[i];
50119
+ const dayData = analysisMonthlyDataByKey.get(dateKey);
50097
50120
  const shiftData = dayData ? getShiftData(dayData, selectedShiftId) : null;
50098
50121
  const idealOutput = shiftData ? shiftData.idealOutput : 0;
50099
50122
  if (idealOutput > 0) {
@@ -50101,20 +50124,18 @@ var WorkspaceMonthlyHistory = ({
50101
50124
  break;
50102
50125
  }
50103
50126
  }
50104
- for (const day of dayNumbers) {
50105
- const dayData = analysisMonthlyData.find((d) => {
50106
- const date = new Date(d.date);
50107
- return date.getDate() === day;
50108
- });
50127
+ for (const dateKey of rangeDateKeys) {
50128
+ const dayData = analysisMonthlyDataByKey.get(dateKey);
50129
+ const dayNumber = Number(dateKey.slice(-2));
50109
50130
  const shiftData = dayData ? getShiftData(dayData, selectedShiftId) : null;
50110
50131
  const output = shiftData && hasRealData(shiftData) ? shiftData.output : 0;
50111
50132
  const idealOutput = shiftData ? shiftData.idealOutput : 0;
50112
50133
  if (output > maxOutput) maxOutput = output;
50113
50134
  const color2 = output >= lastSetTarget ? "#00AB45" : "#E34329";
50114
50135
  dailyData.push({
50115
- hour: getOrdinal2(day),
50136
+ hour: getOrdinal2(dayNumber),
50116
50137
  // Using ordinal format (1st, 2nd, 3rd, etc.)
50117
- timeRange: `Day ${day}`,
50138
+ timeRange: `Day ${dayNumber}`,
50118
50139
  output,
50119
50140
  originalOutput: output,
50120
50141
  // For label display
@@ -50128,7 +50149,7 @@ var WorkspaceMonthlyHistory = ({
50128
50149
  const calculatedMax = Math.max(maxOutput, lastSetTarget);
50129
50150
  const yAxisMax = calculatedMax > 0 ? calculatedMax * 1.1 : 100;
50130
50151
  return { data: dailyData, maxOutput, lastSetTarget, yAxisMax };
50131
- }, [analysisMonthlyData, normalizedRange.startKey, normalizedRange.endKey, selectedShiftId, isUptimeMode, shiftWorkSeconds]);
50152
+ }, [analysisMonthlyDataByKey, rangeDateKeys, selectedShiftId, isUptimeMode, shiftWorkSeconds]);
50132
50153
  const yAxisTicks = React143.useMemo(() => {
50133
50154
  if (isUptimeMode) return void 0;
50134
50155
  const max = chartData.yAxisMax;
@@ -50213,7 +50234,7 @@ var WorkspaceMonthlyHistory = ({
50213
50234
  }
50214
50235
  const avgEfficiency = Math.round(totalEfficiency / filteredShifts.length);
50215
50236
  const avgDailyOutput = Math.round(totalOutput / filteredShifts.length);
50216
- const avgCycleTime = Math.round(totalCycleTime / filteredShifts.length);
50237
+ const avgCycleTime = roundToSingleDecimal(totalCycleTime / filteredShifts.length);
50217
50238
  const avgRank = ranks.length > 0 ? Math.round(ranks.reduce((a, b) => a + b, 0) / ranks.length) : null;
50218
50239
  return {
50219
50240
  avgEfficiency,
@@ -50233,7 +50254,7 @@ var WorkspaceMonthlyHistory = ({
50233
50254
  const assemblyRangeCycleTime = React143.useMemo(() => {
50234
50255
  const trendCycleTime = Number(trendSummary?.avg_cycle_time?.current);
50235
50256
  if (Number.isFinite(trendCycleTime) && trendCycleTime > 0) {
50236
- return Math.round(trendCycleTime);
50257
+ return trendCycleTime;
50237
50258
  }
50238
50259
  return metrics2?.avgCycleTime ?? 0;
50239
50260
  }, [trendSummary?.avg_cycle_time?.current, metrics2?.avgCycleTime]);
@@ -50262,20 +50283,15 @@ var WorkspaceMonthlyHistory = ({
50262
50283
  if (startOffset === -1) startOffset = 6;
50263
50284
  const calendar = Array(startOffset).fill(null);
50264
50285
  for (let day = 1; day <= totalDays; day++) {
50265
- const dayData = data.find((d) => {
50266
- const date = new Date(d.date);
50267
- return date.getDate() === day;
50268
- });
50286
+ const dateKey = buildDateKey(year, month, day);
50287
+ const dayData = monthlyDataByKey.get(dateKey);
50269
50288
  calendar.push(dayData || null);
50270
50289
  }
50271
50290
  return { calendar, startOffset };
50272
- }, [data, month, year]);
50291
+ }, [monthlyDataByKey, month, year]);
50273
50292
  const handleDayClick = React143.useCallback((day) => {
50274
50293
  if (!day) return;
50275
- const year2 = day.date.getFullYear();
50276
- const month2 = String(day.date.getMonth() + 1).padStart(2, "0");
50277
- const dayOfMonth = String(day.date.getDate()).padStart(2, "0");
50278
- const formattedDate = `${year2}-${month2}-${dayOfMonth}`;
50294
+ const formattedDate = getDayDateKey(day);
50279
50295
  trackCoreEvent("Workspace Monthly History Day Clicked", {
50280
50296
  source: "monthly_history",
50281
50297
  workspace_id: workspaceId,
@@ -51221,13 +51237,20 @@ var WorkspaceMonthlyPdfGenerator = ({
51221
51237
  startKey: rangeStart || monthBounds.startKey,
51222
51238
  endKey: rangeEnd || monthBounds.endKey
51223
51239
  };
51240
+ const boundsSourceKey = rangeStart || rangeEnd || monthBounds.startKey;
51241
+ const boundsSourceDate = parseDateKeyToDate(boundsSourceKey);
51242
+ const effectiveBounds = getMonthKeyBounds(boundsSourceDate.getFullYear(), boundsSourceDate.getMonth());
51224
51243
  const normalizedRange = normalizeDateKeyRange(
51225
51244
  requestedRange.startKey,
51226
51245
  requestedRange.endKey,
51227
- monthBounds.startKey,
51228
- monthBounds.endKey
51246
+ effectiveBounds.startKey,
51247
+ effectiveBounds.endKey
51248
+ );
51249
+ const fullRange = isFullMonthRange(
51250
+ normalizedRange,
51251
+ boundsSourceDate.getFullYear(),
51252
+ boundsSourceDate.getMonth()
51229
51253
  );
51230
- const fullRange = isFullMonthRange(normalizedRange, selectedYear, selectedMonth);
51231
51254
  const reportStartDate = parseDateKeyToDate(normalizedRange.startKey);
51232
51255
  const reportEndDate = parseDateKeyToDate(normalizedRange.endKey);
51233
51256
  const reportStartStr = reportStartDate.toLocaleDateString("en-IN", {
@@ -51249,12 +51272,17 @@ var WorkspaceMonthlyPdfGenerator = ({
51249
51272
  const generatedText = `Generated on ${(/* @__PURE__ */ new Date()).toLocaleString("en-IN", { timeZone: "Asia/Kolkata" })}`;
51250
51273
  const resolvedLineName = lineName?.trim() || "Line";
51251
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
+ });
51252
51280
  trackCoreEvent("Workspace Monthly PDF Export Clicked", {
51253
51281
  workspace_id: workspaceId,
51254
51282
  workspace_name: workspaceName,
51255
51283
  line_name: resolvedLineName,
51256
- month: selectedMonth,
51257
- year: selectedYear,
51284
+ month: reportStartDate.getMonth(),
51285
+ year: reportStartDate.getFullYear(),
51258
51286
  shift_id: selectedShiftId,
51259
51287
  range_start: normalizedRange.startKey,
51260
51288
  range_end: normalizedRange.endKey,
@@ -51295,11 +51323,6 @@ var WorkspaceMonthlyPdfGenerator = ({
51295
51323
  doc.setFontSize(13);
51296
51324
  doc.setFont("helvetica", "normal");
51297
51325
  doc.setTextColor(60, 60, 60);
51298
- const monthName = new Date(selectedYear, selectedMonth).toLocaleDateString("en-IN", {
51299
- month: "long",
51300
- year: "numeric",
51301
- timeZone: "Asia/Kolkata"
51302
- });
51303
51326
  const shiftType = getShiftDisplayName2(selectedShiftId, availableShifts);
51304
51327
  doc.text(`${monthName}`, 20, 65);
51305
51328
  doc.text(`${shiftType}`, 20, 73);
@@ -51311,15 +51334,12 @@ var WorkspaceMonthlyPdfGenerator = ({
51311
51334
  doc.setLineWidth(0.8);
51312
51335
  doc.line(20, 90, 190, 90);
51313
51336
  const reportData = analysisData ? analysisData : filterDataByDateKeyRange(monthlyData, normalizedRange);
51314
- const validDays = reportData.filter((day) => {
51315
- const date = new Date(day.date);
51316
- return date.getMonth() === selectedMonth && date.getFullYear() === selectedYear;
51317
- });
51337
+ const validDays = reportData;
51318
51338
  const validShifts = validDays.map((day) => getShiftData(day, selectedShiftId)).filter(hasShiftData);
51319
51339
  const dailyEntries = validDays.map((dayData) => {
51320
51340
  const shift = getShiftData(dayData, selectedShiftId);
51321
51341
  return { dayData, shift };
51322
- }).filter(({ shift }) => hasShiftData(shift)).sort((left, right) => new Date(right.dayData.date).getTime() - new Date(left.dayData.date).getTime());
51342
+ }).filter(({ shift }) => hasShiftData(shift)).sort((left, right) => getDayDateKey(right.dayData).localeCompare(getDayDateKey(left.dayData)));
51323
51343
  const filteredShifts = isUptimeMode ? validShifts : validShifts.filter((shift) => (shift.efficiency ?? 0) >= 5);
51324
51344
  const monthlyMetrics = filteredShifts.length > 0 ? isUptimeMode ? (() => {
51325
51345
  const totalIdleTime = filteredShifts.reduce((sum, shift) => sum + shift.idleTime, 0);
@@ -71152,15 +71172,26 @@ var WorkspaceDetailView = ({
71152
71172
  const range = React143.useMemo(() => ({ startKey: rangeStart, endKey: rangeEnd }), [rangeStart, rangeEnd]);
71153
71173
  const isFullRange = React143.useMemo(() => isFullMonthRange(range, selectedYear, selectedMonth), [range, selectedYear, selectedMonth]);
71154
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);
71155
71184
  const normalized = normalizeDateKeyRange(
71156
- typeof urlRangeStart === "string" ? urlRangeStart : monthBounds.startKey,
71157
- typeof urlRangeEnd === "string" ? urlRangeEnd : monthBounds.endKey,
71158
- monthBounds.startKey,
71159
- monthBounds.endKey
71185
+ typeof urlRangeStart === "string" ? urlRangeStart : targetBounds.startKey,
71186
+ typeof urlRangeEnd === "string" ? urlRangeEnd : targetBounds.endKey,
71187
+ targetBounds.startKey,
71188
+ targetBounds.endKey
71160
71189
  );
71190
+ setSelectedMonth(targetMonth);
71191
+ setSelectedYear(targetYear);
71161
71192
  setRangeStart(normalized.startKey);
71162
71193
  setRangeEnd(normalized.endKey);
71163
- }, [urlRangeStart, urlRangeEnd, monthBounds.startKey, monthBounds.endKey]);
71194
+ }, [urlRangeStart, urlRangeEnd, monthBounds.startKey]);
71164
71195
  const isHistoricView = Boolean(date && parsedShiftId !== void 0);
71165
71196
  const initialTab = getInitialTab(sourceType, defaultTab, fromMonthly, date);
71166
71197
  const [activeTab, setActiveTab] = React143.useState(initialTab);
@@ -71682,12 +71713,13 @@ var WorkspaceDetailView = ({
71682
71713
  console.warn("Skipping invalid metric:", metric);
71683
71714
  return;
71684
71715
  }
71685
- const dateObj = new Date(metric.date);
71686
- const dateKey = dateObj.toISOString().split("T")[0];
71716
+ const dateKey = getDateKeyFromValue(metric.date);
71717
+ const dateObj = parseDateKeyToDate(dateKey);
71687
71718
  let dayEntry = dayDataMap.get(dateKey);
71688
71719
  if (!dayEntry) {
71689
71720
  dayEntry = {
71690
71721
  date: dateObj,
71722
+ dateKey,
71691
71723
  shifts: {}
71692
71724
  // Multi-shift structure: Record<number, CalendarShiftData>
71693
71725
  };
@@ -71732,6 +71764,7 @@ var WorkspaceDetailView = ({
71732
71764
  });
71733
71765
  const processedData = Array.from(dayDataMap.values()).filter((entry) => Object.keys(entry.shifts).length > 0).map((entry) => ({
71734
71766
  date: entry.date,
71767
+ dateKey: entry.dateKey,
71735
71768
  shifts: entry.shifts
71736
71769
  }));
71737
71770
  console.log(`[handleMonthlyDataLoaded] Transformed data for calendar:`, {
@@ -81949,6 +81982,8 @@ exports.getCurrentWeekFullRange = getCurrentWeekFullRange;
81949
81982
  exports.getCurrentWeekToDateRange = getCurrentWeekToDateRange;
81950
81983
  exports.getDashboardHeaderTimeInZone = getDashboardHeaderTimeInZone;
81951
81984
  exports.getDateKeyFromDate = getDateKeyFromDate;
81985
+ exports.getDateKeyFromValue = getDateKeyFromValue;
81986
+ exports.getDayDateKey = getDayDateKey;
81952
81987
  exports.getDaysDifferenceInZone = getDaysDifferenceInZone;
81953
81988
  exports.getDefaultCameraStreamUrl = getDefaultCameraStreamUrl;
81954
81989
  exports.getDefaultLineId = getDefaultLineId;