@optifye/dashboard-core 6.10.37 → 6.10.38

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
@@ -132,14 +132,18 @@ var DEFAULT_SHIFT_DATA = {
132
132
  pph: 0,
133
133
  pphThreshold: 0,
134
134
  idealOutput: 0,
135
+ targetOutput: 0,
135
136
  rank: 0,
136
137
  idleTime: 0,
138
+ activeTimeSeconds: 0,
139
+ availableTimeSeconds: 0,
137
140
  hasData: false
138
141
  };
139
142
  var getShiftData = (day, shiftId) => {
140
143
  const shift = day.shifts[shiftId];
141
144
  if (shift) {
142
- return { ...shift, hasData: true };
145
+ const hasData = shift.hasData ?? true;
146
+ return { ...DEFAULT_SHIFT_DATA, ...shift, hasData };
143
147
  }
144
148
  return { ...DEFAULT_SHIFT_DATA };
145
149
  };
@@ -1596,7 +1600,7 @@ var dashboardService = {
1596
1600
  }
1597
1601
  const lineIdsToQuery = configuredLineIds;
1598
1602
  const [line1Result, metricsResult2] = await Promise.all([
1599
- supabase.from(linesTable).select("id, line_name, factory_id, factories!lines_factory_id_fkey(factory_name), company_id, companies!lines_company_id_fkey(company_name:name)").eq("id", defaultLineId).single(),
1603
+ supabase.from(linesTable).select("id, line_name, factory_id, monitoring_mode, factories!lines_factory_id_fkey(factory_name), company_id, companies!lines_company_id_fkey(company_name:name)").eq("id", defaultLineId).single(),
1600
1604
  supabase.from(lineMetricsTable).select("*").in("line_id", lineIdsToQuery).eq("shift_id", queryShiftId).eq("date", queryDate)
1601
1605
  ]);
1602
1606
  if (line1Result.error) throw line1Result.error;
@@ -1651,6 +1655,7 @@ var dashboardService = {
1651
1655
  factory_name: line1Data.factories?.factory_name ?? "N/A",
1652
1656
  date: queryDate,
1653
1657
  shift_id: queryShiftId,
1658
+ monitoring_mode: line1Data.monitoring_mode ?? void 0,
1654
1659
  metrics: {
1655
1660
  avg_efficiency: avgEfficiency,
1656
1661
  avg_cycle_time: combinedMetricsData.avg_cycle_time / numLines,
@@ -1674,7 +1679,7 @@ var dashboardService = {
1674
1679
  throw new Error("Company ID must be configured for detailed line requests.");
1675
1680
  }
1676
1681
  const [lineResult, metricsResult] = await Promise.all([
1677
- supabase.from(linesTable).select("id, line_name, factory_id, factories!lines_factory_id_fkey(factory_name), company_id, companies!lines_company_id_fkey(company_name:name)").eq("id", lineIdToQuery).single(),
1682
+ supabase.from(linesTable).select("id, line_name, factory_id, monitoring_mode, factories!lines_factory_id_fkey(factory_name), company_id, companies!lines_company_id_fkey(company_name:name)").eq("id", lineIdToQuery).single(),
1678
1683
  supabase.from(lineMetricsTable).select("*").eq("line_id", lineIdToQuery).eq("shift_id", queryShiftId).eq("date", queryDate).maybeSingle()
1679
1684
  ]);
1680
1685
  if (lineResult.error) throw lineResult.error;
@@ -1693,6 +1698,7 @@ var dashboardService = {
1693
1698
  factory_name: lineData.factories?.factory_name ?? "N/A",
1694
1699
  date: queryDate,
1695
1700
  shift_id: queryShiftId,
1701
+ monitoring_mode: lineData.monitoring_mode ?? void 0,
1696
1702
  metrics: {
1697
1703
  avg_efficiency: metrics2?.avg_efficiency ?? 0,
1698
1704
  avg_cycle_time: metrics2?.avg_cycle_time || 0,
@@ -1722,8 +1728,6 @@ var dashboardService = {
1722
1728
  const entityConfig = config.entityConfig ?? DEFAULT_ENTITY_CONFIG;
1723
1729
  const shiftConfig = config.shiftConfig ?? DEFAULT_SHIFT_CONFIG;
1724
1730
  const companyId = entityConfig.companyId;
1725
- const metricsTablePrefixStr = getMetricsTablePrefix();
1726
- const metricsTable = `${metricsTablePrefixStr}_${companyId ? companyId.replace(/-/g, "_") : "unknown_company"}`;
1727
1731
  const startDate = new Date(year, month, 1);
1728
1732
  const endDate = new Date(year, month + 1, 0);
1729
1733
  const formatDate2 = (date) => {
@@ -1735,33 +1739,34 @@ var dashboardService = {
1735
1739
  const formattedStartDate = formatDate2(startDate);
1736
1740
  const formattedEndDate = formatDate2(endDate);
1737
1741
  try {
1738
- const { data, error } = await supabase.from(metricsTable).select(`
1739
- date,
1740
- shift_id,
1741
- efficiency,
1742
- total_output,
1743
- avg_cycle_time,
1744
- avg_pph,
1745
- pph_threshold,
1746
- workspace_rank,
1747
- idle_time,
1748
- total_day_output
1749
- `).eq("workspace_id", workspaceUuid).gte("date", formattedStartDate).lte("date", formattedEndDate).order("date", { ascending: true }).order("shift_id", { ascending: true });
1750
- if (error) throw error;
1751
- if (!data) return [];
1752
- const transformedData = data.map((item) => ({
1742
+ const searchParams = new URLSearchParams();
1743
+ searchParams.set("workspace_id", workspaceUuid);
1744
+ searchParams.set("company_id", companyId || "");
1745
+ searchParams.set("start_date", formattedStartDate);
1746
+ searchParams.set("end_date", formattedEndDate);
1747
+ const data = await fetchBackendJson(
1748
+ supabase,
1749
+ `/api/dashboard/workspace-monthly?${searchParams.toString()}`
1750
+ );
1751
+ const entries = data?.entries ?? [];
1752
+ const transformedData = entries.map((item) => ({
1753
1753
  date: item.date,
1754
1754
  shift_id: item.shift_id,
1755
1755
  shift_type: item.shift_id === (shiftConfig.dayShift?.id ?? 0) ? "Day" : item.shift_id === (shiftConfig.nightShift?.id ?? 1) ? "Night" : "Unknown",
1756
- avg_efficiency: item.efficiency || 0,
1756
+ avg_efficiency: item.avg_efficiency || 0,
1757
1757
  total_output: item.total_output || 0,
1758
1758
  avg_cycle_time: item.avg_cycle_time || 0,
1759
- ideal_output: item.total_day_output || 0,
1760
- // Use daily target directly from performance_metrics table
1759
+ ideal_output: item.ideal_output || 0,
1760
+ total_day_output: item.total_day_output ?? item.ideal_output ?? 0,
1761
1761
  avg_pph: item.avg_pph || 0,
1762
1762
  pph_threshold: item.pph_threshold || 0,
1763
1763
  workspace_rank: item.workspace_rank || 0,
1764
- idle_time: item.idle_time || 0
1764
+ idle_time: item.idle_time || 0,
1765
+ active_time_seconds: item.active_time_seconds,
1766
+ idle_time_seconds: item.idle_time_seconds,
1767
+ available_time_seconds: item.available_time_seconds,
1768
+ shift_start: item.shift_start ?? null,
1769
+ shift_end: item.shift_end ?? null
1765
1770
  }));
1766
1771
  return transformedData;
1767
1772
  } catch (err) {
@@ -1769,16 +1774,14 @@ var dashboardService = {
1769
1774
  throw err;
1770
1775
  }
1771
1776
  },
1772
- async getLineMonthlyData(lineIdInput, month, year) {
1777
+ async getLineMonthlyData(lineIdInput, month, year, options) {
1773
1778
  const supabase = _getSupabaseInstance();
1774
1779
  const config = _getDashboardConfigInstance();
1775
- const dbConfig = config.databaseConfig ?? DEFAULT_DATABASE_CONFIG;
1776
1780
  const entityConfig = config.entityConfig ?? DEFAULT_ENTITY_CONFIG;
1777
- const lineMetricsTable = getTable(dbConfig, "lineMetrics");
1778
1781
  const configuredLineIds = getConfiguredLineIds(entityConfig);
1779
1782
  const factoryViewId = entityConfig.factoryViewId ?? "factory";
1780
- const startDate = new Date(year, month, 1);
1781
- const endDate = new Date(year, month + 1, 0);
1783
+ const startDate = options?.startDate ? new Date(options.startDate) : new Date(year, month, 1);
1784
+ const endDate = options?.endDate ? new Date(options.endDate) : new Date(year, month + 1, 0);
1782
1785
  const formatDate2 = (date) => {
1783
1786
  const year2 = date.getFullYear();
1784
1787
  const month2 = String(date.getMonth() + 1).padStart(2, "0");
@@ -1787,31 +1790,28 @@ var dashboardService = {
1787
1790
  };
1788
1791
  const formattedStartDate = formatDate2(startDate);
1789
1792
  const formattedEndDate = formatDate2(endDate);
1790
- let query = supabase.from(lineMetricsTable).select("date, shift_id, avg_efficiency, underperforming_workspaces, total_workspaces, current_output, ideal_output, line_threshold").gte("date", formattedStartDate).lte("date", formattedEndDate);
1793
+ const params = new URLSearchParams({
1794
+ start_date: formattedStartDate,
1795
+ end_date: formattedEndDate,
1796
+ company_id: entityConfig.companyId || ""
1797
+ });
1791
1798
  if (lineIdInput === factoryViewId) {
1792
1799
  if (!isValidFactoryViewConfiguration(entityConfig)) {
1793
1800
  throw new Error("Factory View requires at least one configured line for monthly data.");
1794
1801
  }
1795
- query = query.in("line_id", configuredLineIds);
1802
+ params.set("line_ids", configuredLineIds.join(","));
1796
1803
  } else {
1797
- query = query.eq("line_id", lineIdInput);
1804
+ params.set("line_id", lineIdInput);
1805
+ }
1806
+ if (options?.shiftIds && options.shiftIds.length > 0) {
1807
+ params.set("shift_ids", options.shiftIds.join(","));
1798
1808
  }
1799
- query = query.order("date", { ascending: true }).order("shift_id", { ascending: true });
1800
1809
  try {
1801
- const { data, error } = await query;
1802
- if (error) throw error;
1803
- if (!data) return [];
1804
- const transformedData = data.map((item) => ({
1805
- date: item.date,
1806
- shift_id: item.shift_id,
1807
- avg_efficiency: item.avg_efficiency || 0,
1808
- underperforming_workspaces: item.underperforming_workspaces || 0,
1809
- total_workspaces: item.total_workspaces || 0,
1810
- current_output: item.current_output || 0,
1811
- ideal_output: item.ideal_output || 0,
1812
- line_threshold: item.line_threshold || 0
1813
- }));
1814
- return transformedData;
1810
+ const response = await fetchBackendJson(
1811
+ supabase,
1812
+ `/api/dashboard/line-monthly?${params.toString()}`
1813
+ );
1814
+ return response?.entries || [];
1815
1815
  } catch (err) {
1816
1816
  console.error("Exception in getLineMonthlyData:", err);
1817
1817
  throw err;
@@ -4078,11 +4078,163 @@ var authRateLimitService = {
4078
4078
  clearRateLimit,
4079
4079
  clearAllRateLimits
4080
4080
  };
4081
+
4082
+ // src/lib/utils/rateLimit.ts
4083
+ var rateLimitMap2 = /* @__PURE__ */ new Map();
4084
+ function checkRateLimit2(identifier, options = {}) {
4085
+ const windowMs = options.windowMs || 60 * 1e3;
4086
+ const maxRequests = options.maxRequests || 3;
4087
+ const now4 = Date.now();
4088
+ const userLimit = rateLimitMap2.get(identifier);
4089
+ if (userLimit && userLimit.resetTime < now4) {
4090
+ rateLimitMap2.delete(identifier);
4091
+ }
4092
+ if (!userLimit || userLimit.resetTime < now4) {
4093
+ rateLimitMap2.set(identifier, {
4094
+ count: 1,
4095
+ resetTime: now4 + windowMs
4096
+ });
4097
+ return { allowed: true };
4098
+ }
4099
+ if (userLimit.count >= maxRequests) {
4100
+ const retryAfter = Math.ceil((userLimit.resetTime - now4) / 1e3);
4101
+ return { allowed: false, retryAfter };
4102
+ }
4103
+ userLimit.count++;
4104
+ return { allowed: true };
4105
+ }
4106
+ function clearRateLimit2(identifier) {
4107
+ rateLimitMap2.delete(identifier);
4108
+ }
4109
+ function clearAllRateLimits2() {
4110
+ rateLimitMap2.clear();
4111
+ }
4112
+
4113
+ // src/lib/utils/sentryContext.ts
4114
+ function getSentry() {
4115
+ try {
4116
+ return __require("@sentry/nextjs");
4117
+ } catch {
4118
+ return null;
4119
+ }
4120
+ }
4121
+ function setSentryUserContext(user) {
4122
+ const sentry = getSentry();
4123
+ if (!sentry) return;
4124
+ if (user) {
4125
+ sentry.setUser({
4126
+ id: user.id,
4127
+ email: user.email
4128
+ });
4129
+ sentry.setTags({
4130
+ company_id: user.company_id || "unknown",
4131
+ role: user.role || "unknown",
4132
+ role_level: user.role_level || "unknown"
4133
+ });
4134
+ } else {
4135
+ sentry.setUser(null);
4136
+ sentry.setTags({
4137
+ company_id: void 0,
4138
+ role: void 0,
4139
+ role_level: void 0
4140
+ });
4141
+ }
4142
+ }
4143
+ function setSentryWorkspaceContext(config) {
4144
+ const sentry = getSentry();
4145
+ if (!sentry) return;
4146
+ sentry.setTags({
4147
+ workspace_company: config.companyId || "unknown",
4148
+ workspace_factory: config.factoryId || "unknown",
4149
+ factory_name: config.factoryName || "unknown"
4150
+ });
4151
+ }
4152
+ function clearSentryContext() {
4153
+ const sentry = getSentry();
4154
+ if (!sentry) return;
4155
+ sentry.setUser(null);
4156
+ sentry.setTags({
4157
+ company_id: void 0,
4158
+ role: void 0,
4159
+ role_level: void 0,
4160
+ workspace_company: void 0,
4161
+ workspace_factory: void 0,
4162
+ factory_name: void 0
4163
+ });
4164
+ }
4165
+ function applyScopeExtras(scope, extras) {
4166
+ if (!extras) return;
4167
+ if (scope.setExtras) {
4168
+ scope.setExtras(extras);
4169
+ return;
4170
+ }
4171
+ if (scope.setExtra) {
4172
+ Object.entries(extras).forEach(([key, value]) => scope.setExtra?.(key, value));
4173
+ }
4174
+ }
4175
+ function captureSentryMessage(message, level = "warning", extras) {
4176
+ const sentry = getSentry();
4177
+ if (!sentry || !sentry.captureMessage) return;
4178
+ if (sentry.withScope) {
4179
+ sentry.withScope((scope) => {
4180
+ scope.setLevel?.(level);
4181
+ applyScopeExtras(scope, extras);
4182
+ sentry.captureMessage?.(message);
4183
+ });
4184
+ return;
4185
+ }
4186
+ sentry.captureMessage(message, level);
4187
+ }
4188
+ function captureSentryException(error, extras) {
4189
+ const sentry = getSentry();
4190
+ if (!sentry || !sentry.captureException) return;
4191
+ if (sentry.withScope) {
4192
+ sentry.withScope((scope) => {
4193
+ scope.setLevel?.("error");
4194
+ applyScopeExtras(scope, extras);
4195
+ sentry.captureException?.(error);
4196
+ });
4197
+ return;
4198
+ }
4199
+ sentry.captureException(error);
4200
+ }
4201
+
4202
+ // src/lib/services/mixpanelService.ts
4081
4203
  var isMixpanelInitialized = false;
4082
4204
  var currentUserProperties;
4205
+ var MIXPANEL_WARNING_RATE_LIMIT = {
4206
+ windowMs: 10 * 60 * 1e3,
4207
+ // 10 minutes
4208
+ maxRequests: 2
4209
+ };
4210
+ var MIXPANEL_ERROR_RATE_LIMIT = {
4211
+ windowMs: 10 * 60 * 1e3,
4212
+ // 10 minutes
4213
+ maxRequests: 5
4214
+ };
4215
+ var baseMixpanelExtras = () => ({
4216
+ isInitialized: isMixpanelInitialized,
4217
+ environment: process.env.NODE_ENV
4218
+ });
4219
+ var shouldReportMixpanel = (key, isError) => {
4220
+ const options = isError ? MIXPANEL_ERROR_RATE_LIMIT : MIXPANEL_WARNING_RATE_LIMIT;
4221
+ return checkRateLimit2(`mixpanel:${key}`, options).allowed;
4222
+ };
4223
+ var reportMixpanelWarning = (key, message, extras) => {
4224
+ if (!shouldReportMixpanel(key, false)) return;
4225
+ captureSentryMessage(message, "warning", { ...baseMixpanelExtras(), ...extras });
4226
+ };
4227
+ var reportMixpanelError = (key, error, extras) => {
4228
+ if (!shouldReportMixpanel(key, true)) return;
4229
+ captureSentryException(error, { ...baseMixpanelExtras(), ...extras });
4230
+ };
4083
4231
  var initializeCoreMixpanel = (token, debugOrOptions, trackPageViewArg) => {
4084
4232
  if (!token) {
4085
4233
  console.warn("Mixpanel token not provided for initialization. Mixpanel will not be enabled.");
4234
+ reportMixpanelWarning("init_missing_token", "Mixpanel init skipped: missing token", {
4235
+ operation: "init",
4236
+ hasToken: false
4237
+ });
4086
4238
  return;
4087
4239
  }
4088
4240
  if (isMixpanelInitialized) {
@@ -4130,25 +4282,61 @@ var initializeCoreMixpanel = (token, debugOrOptions, trackPageViewArg) => {
4130
4282
  initOptions[key] = sessionOpts[key];
4131
4283
  }
4132
4284
  });
4133
- mixpanel__default.default.init(token, initOptions);
4134
- isMixpanelInitialized = true;
4285
+ try {
4286
+ mixpanel__default.default.init(token, initOptions);
4287
+ isMixpanelInitialized = true;
4288
+ } catch (err) {
4289
+ reportMixpanelError("init_failed", err, {
4290
+ operation: "init",
4291
+ hasToken: true,
4292
+ persistence: initOptions.persistence,
4293
+ trackPageView: initOptions.track_pageview
4294
+ });
4295
+ return;
4296
+ }
4135
4297
  if (initOptions.debug) {
4136
4298
  console.log("Mixpanel initialized in dashboard-core.");
4137
4299
  }
4138
4300
  };
4139
4301
  var trackCorePageView = (pageName, properties) => {
4140
- if (!isMixpanelInitialized) return;
4141
- mixpanel__default.default.track("Page View", { page: pageName, ...properties });
4302
+ if (!isMixpanelInitialized) {
4303
+ reportMixpanelWarning("track_pageview_not_initialized", "Mixpanel not initialized: page view dropped", {
4304
+ operation: "track_pageview",
4305
+ pageName
4306
+ });
4307
+ return;
4308
+ }
4309
+ try {
4310
+ mixpanel__default.default.track("Page View", { page: pageName, ...properties });
4311
+ } catch (err) {
4312
+ reportMixpanelError("track_pageview_failed", err, {
4313
+ operation: "track_pageview",
4314
+ pageName
4315
+ });
4316
+ }
4142
4317
  };
4143
4318
  var trackCoreEvent = (eventName, properties) => {
4144
- if (!isMixpanelInitialized) return;
4319
+ if (!isMixpanelInitialized) {
4320
+ reportMixpanelWarning("track_event_not_initialized", "Mixpanel not initialized: event dropped", {
4321
+ operation: "track_event",
4322
+ eventName
4323
+ });
4324
+ return;
4325
+ }
4145
4326
  const mergedProps = {
4146
4327
  // Precedence order: explicit properties passed by caller should override
4147
4328
  // automatically appended user properties to avoid accidental overwrites.
4148
4329
  ...currentUserProperties || {},
4149
4330
  ...properties || {}
4150
4331
  };
4151
- mixpanel__default.default.track(eventName, mergedProps);
4332
+ try {
4333
+ mixpanel__default.default.track(eventName, mergedProps);
4334
+ } catch (err) {
4335
+ reportMixpanelError("track_event_failed", err, {
4336
+ operation: "track_event",
4337
+ eventName
4338
+ });
4339
+ }
4152
4340
  };
4153
4341
  var startCoreSessionRecording = () => {
4154
4342
  try {
@@ -4158,6 +4346,9 @@ var startCoreSessionRecording = () => {
4158
4346
  }
4159
4347
  } catch (err) {
4160
4348
  console.error("[Mixpanel] Unable to start session recording:", err);
4349
+ reportMixpanelError("start_session_recording_failed", err, {
4350
+ operation: "start_session_recording"
4351
+ });
4161
4352
  }
4162
4353
  };
4163
4354
  var stopCoreSessionRecording = () => {
@@ -4168,6 +4359,9 @@ var stopCoreSessionRecording = () => {
4168
4359
  }
4169
4360
  } catch (err) {
4170
4361
  console.error("[Mixpanel] Unable to stop session recording:", err);
4362
+ reportMixpanelError("stop_session_recording_failed", err, {
4363
+ operation: "stop_session_recording"
4364
+ });
4171
4365
  }
4172
4366
  };
4173
4367
  var getCoreSessionRecordingProperties = () => {
@@ -4191,16 +4385,38 @@ var getCoreSessionReplayUrl = () => {
4191
4385
  return null;
4192
4386
  };
4193
4387
  var identifyCoreUser = (userId, userProperties) => {
4194
- if (!isMixpanelInitialized) return;
4195
- mixpanel__default.default.identify(userId);
4196
- if (userProperties) {
4197
- mixpanel__default.default.people.set(userProperties);
4388
+ if (!isMixpanelInitialized) {
4389
+ reportMixpanelWarning("identify_not_initialized", "Mixpanel not initialized: identify dropped", {
4390
+ operation: "identify",
4391
+ userIdPresent: Boolean(userId),
4392
+ hasUserProperties: Boolean(userProperties)
4393
+ });
4394
+ return;
4395
+ }
4396
+ try {
4397
+ mixpanel__default.default.identify(userId);
4398
+ if (userProperties) {
4399
+ mixpanel__default.default.people.set(userProperties);
4400
+ }
4401
+ } catch (err) {
4402
+ reportMixpanelError("identify_failed", err, {
4403
+ operation: "identify",
4404
+ userIdPresent: Boolean(userId),
4405
+ hasUserProperties: Boolean(userProperties)
4406
+ });
4407
+ return;
4198
4408
  }
4199
4409
  currentUserProperties = { ...userProperties };
4200
4410
  };
4201
4411
  var resetCoreMixpanel = () => {
4202
4412
  if (!isMixpanelInitialized) return;
4203
- mixpanel__default.default.reset();
4413
+ try {
4414
+ mixpanel__default.default.reset();
4415
+ } catch (err) {
4416
+ reportMixpanelError("reset_failed", err, {
4417
+ operation: "reset"
4418
+ });
4419
+ }
4204
4420
  };
4205
4421
 
4206
4422
  // src/lib/services/sseClient.ts
@@ -9681,7 +9897,11 @@ var useLineWorkspaceMetrics = (lineId, options) => {
9681
9897
  trend: item.trend_score === 1 ? 2 : 0,
9682
9898
  predicted_output: item.ideal_output || 0,
9683
9899
  efficiency: item.efficiency || 0,
9684
- action_threshold: item.total_day_output || 0
9900
+ action_threshold: item.total_day_output || 0,
9901
+ idle_time: item.idle_time ?? void 0,
9902
+ idle_time_hourly: item.idle_time_hourly || null,
9903
+ shift_start: item.shift_start || void 0,
9904
+ shift_end: item.shift_end || void 0
9685
9905
  })).sort((a, b) => a.workspace_name.localeCompare(b.workspace_name));
9686
9906
  setWorkspaces(transformedData);
9687
9907
  setInitialized(true);
@@ -10611,23 +10831,29 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
10611
10831
  workspaceMetricsStore.setDetailed(detailedMetrics);
10612
10832
  }
10613
10833
  });
10614
- const transformedWorkspaceData = allWorkspaceMetrics.map((item) => ({
10615
- company_id: item.company_id || companyId,
10616
- line_id: item.line_id,
10617
- shift_id: item.shift_id,
10618
- date: item.date,
10619
- workspace_uuid: item.workspace_id,
10620
- workspace_name: item.workspace_name,
10621
- action_count: item.total_output || 0,
10622
- pph: item.avg_pph || 0,
10623
- performance_score: item.performance_score || 0,
10624
- avg_cycle_time: item.avg_cycle_time || 0,
10625
- trend: item.trend_score === 1 ? 2 : 0,
10626
- predicted_output: item.ideal_output || 0,
10627
- efficiency: item.efficiency || 0,
10628
- action_threshold: item.total_day_output || 0,
10629
- show_exclamation: item.show_exclamation ?? void 0
10630
- })).sort((a, b) => {
10834
+ const transformedWorkspaceData = allWorkspaceMetrics.map((item) => {
10835
+ const idleTimeValue = typeof item.idle_time === "number" ? item.idle_time : Number(item.idle_time);
10836
+ const idleTimeSeconds = Number.isFinite(idleTimeValue) ? idleTimeValue : void 0;
10837
+ return {
10838
+ company_id: item.company_id || companyId,
10839
+ line_id: item.line_id,
10840
+ shift_id: item.shift_id,
10841
+ date: item.date,
10842
+ workspace_uuid: item.workspace_id,
10843
+ workspace_name: item.workspace_name,
10844
+ action_count: item.total_output || 0,
10845
+ pph: item.avg_pph || 0,
10846
+ performance_score: item.performance_score || 0,
10847
+ avg_cycle_time: item.avg_cycle_time || 0,
10848
+ trend: item.trend_score === 1 ? 2 : 0,
10849
+ predicted_output: item.ideal_output || 0,
10850
+ efficiency: item.efficiency || 0,
10851
+ action_threshold: item.total_day_output || 0,
10852
+ show_exclamation: item.show_exclamation ?? void 0,
10853
+ monitoring_mode: item.monitoring_mode ?? void 0,
10854
+ idle_time: idleTimeSeconds
10855
+ };
10856
+ }).sort((a, b) => {
10631
10857
  if (a.line_id !== b.line_id) return a.line_id.localeCompare(b.line_id);
10632
10858
  const wsNumA = parseInt(a.workspace_name?.replace(/[^0-9]/g, "") || "0");
10633
10859
  const wsNumB = parseInt(b.workspace_name?.replace(/[^0-9]/g, "") || "0");
@@ -11472,7 +11698,8 @@ var useRealtimeLineMetrics = ({
11472
11698
  factory: {
11473
11699
  id: lineDetailsPayload.factory_id || "",
11474
11700
  factory_name: lineDetailsPayload.factory_name || ""
11475
- }
11701
+ },
11702
+ monitoring_mode: lineDetailsPayload.monitoring_mode ?? void 0
11476
11703
  });
11477
11704
  } else {
11478
11705
  setLineDetails(null);
@@ -11499,7 +11726,8 @@ var useRealtimeLineMetrics = ({
11499
11726
  shift_start: "06:00",
11500
11727
  shift_end: "14:00",
11501
11728
  last_updated: (/* @__PURE__ */ new Date()).toISOString(),
11502
- poorest_performing_workspaces: poorestPerformingWorkspaces
11729
+ poorest_performing_workspaces: poorestPerformingWorkspaces,
11730
+ idle_time_hourly: {}
11503
11731
  });
11504
11732
  } else {
11505
11733
  setMetrics({
@@ -16108,70 +16336,121 @@ function useHasLineAccess(lineId, configLineIds) {
16108
16336
  return accessibleLineIds.includes(lineId);
16109
16337
  }, [lineId, accessibleLineIds]);
16110
16338
  }
16111
- function useLineSupervisor(lineId) {
16339
+ function useLineSupervisor(lineId, options) {
16112
16340
  const supabase = useSupabase();
16341
+ const entityConfig = useEntityConfig();
16342
+ const enabled = options?.enabled ?? true;
16343
+ const useBackend = options?.useBackend ?? true;
16344
+ const resolvedCompanyId = options?.companyId || entityConfig.companyId;
16113
16345
  const [supervisor, setSupervisor] = React26.useState(null);
16114
16346
  const [supervisors, setSupervisors] = React26.useState([]);
16115
16347
  const [isLoading, setIsLoading] = React26.useState(true);
16116
16348
  const [error, setError] = React26.useState(null);
16117
16349
  const fetchSupervisor = React26.useCallback(async () => {
16118
- if (!lineId || !supabase) {
16350
+ if (!enabled || !lineId || !supabase) {
16119
16351
  setIsLoading(false);
16120
16352
  return;
16121
16353
  }
16122
16354
  try {
16123
16355
  setIsLoading(true);
16124
16356
  setError(null);
16125
- const { data, error: fetchError } = await supabase.from("user_roles").select("user_id, email, first_name, last_name, properties, profile_photo_url").eq("role_level", "supervisor");
16126
- if (fetchError) {
16127
- console.error("[useLineSupervisor] Query error:", fetchError);
16128
- throw fetchError;
16129
- }
16130
- if (data && data.length > 0) {
16131
- const supervisorsForLine = data.filter((supervisorData) => {
16132
- const lineIds = supervisorData.properties?.line_id || supervisorData.properties?.line_ids || [];
16133
- const isAssigned = Array.isArray(lineIds) && lineIds.includes(lineId);
16134
- return isAssigned;
16357
+ const mapSupervisor = (row) => {
16358
+ const email = row.email || "";
16359
+ let displayName;
16360
+ if (row.first_name) {
16361
+ displayName = row.last_name ? `${row.first_name} ${row.last_name}` : row.first_name;
16362
+ } else {
16363
+ displayName = (typeof email === "string" && email.includes("@") ? email.split("@")[0] : email) || email;
16364
+ }
16365
+ return {
16366
+ userId: row.user_id,
16367
+ email,
16368
+ displayName,
16369
+ profilePhotoUrl: row.profile_photo_url
16370
+ };
16371
+ };
16372
+ const fetchFromBackend = async () => {
16373
+ if (!resolvedCompanyId) {
16374
+ throw new Error("Company ID is not configured");
16375
+ }
16376
+ const searchParams = new URLSearchParams({
16377
+ company_id: resolvedCompanyId,
16378
+ line_ids: lineId
16135
16379
  });
16136
- if (supervisorsForLine.length > 0) {
16137
- const allSupervisors = supervisorsForLine.map((supervisorData) => {
16138
- let displayName;
16139
- if (supervisorData.first_name) {
16140
- displayName = supervisorData.last_name ? `${supervisorData.first_name} ${supervisorData.last_name}` : supervisorData.first_name;
16141
- } else {
16142
- displayName = supervisorData.email.split("@")[0] || supervisorData.email;
16143
- }
16144
- return {
16145
- userId: supervisorData.user_id,
16146
- email: supervisorData.email,
16147
- displayName,
16148
- profilePhotoUrl: supervisorData.profile_photo_url
16149
- };
16380
+ const data = await fetchBackendJson(supabase, `/api/dashboard/line-supervisors?${searchParams.toString()}`);
16381
+ const supervisorsForLine = (data?.supervisors || []).filter((row) => Array.isArray(row.line_ids) && row.line_ids.includes(lineId)).map(mapSupervisor);
16382
+ setSupervisors(supervisorsForLine);
16383
+ setSupervisor(supervisorsForLine[0] || null);
16384
+ };
16385
+ const fetchFromSupabase = async () => {
16386
+ const { data, error: fetchError } = await supabase.from("user_roles").select("user_id, email, first_name, last_name, properties, profile_photo_url").eq("role_level", "supervisor");
16387
+ if (fetchError) {
16388
+ console.error("[useLineSupervisor] Query error:", fetchError);
16389
+ throw fetchError;
16390
+ }
16391
+ if (data && data.length > 0) {
16392
+ const supervisorsForLine = data.filter((supervisorData) => {
16393
+ const lineIds = supervisorData.properties?.line_id || supervisorData.properties?.line_ids || [];
16394
+ const isAssigned = Array.isArray(lineIds) && lineIds.includes(lineId);
16395
+ return isAssigned;
16150
16396
  });
16151
- setSupervisors(allSupervisors);
16152
- setSupervisor(allSupervisors[0]);
16397
+ if (supervisorsForLine.length > 0) {
16398
+ const allSupervisors = supervisorsForLine.map(mapSupervisor);
16399
+ setSupervisors(allSupervisors);
16400
+ setSupervisor(allSupervisors[0]);
16401
+ } else {
16402
+ setSupervisors([]);
16403
+ setSupervisor(null);
16404
+ }
16153
16405
  } else {
16154
16406
  setSupervisors([]);
16155
16407
  setSupervisor(null);
16156
16408
  }
16409
+ };
16410
+ if (useBackend && resolvedCompanyId) {
16411
+ await fetchFromBackend();
16157
16412
  } else {
16158
- setSupervisors([]);
16159
- setSupervisor(null);
16413
+ await fetchFromSupabase();
16160
16414
  }
16161
16415
  } catch (err) {
16162
16416
  console.error("[useLineSupervisor] Error fetching supervisors:", err);
16163
- setError(err instanceof Error ? err : new Error("Failed to fetch supervisors"));
16164
- setSupervisors([]);
16165
- setSupervisor(null);
16417
+ try {
16418
+ if (!useBackend) {
16419
+ throw err;
16420
+ }
16421
+ await (async () => {
16422
+ const { data } = await supabase.from("user_roles").select("user_id, email, first_name, last_name, properties, profile_photo_url").eq("role_level", "supervisor");
16423
+ const supervisorsForLine = (data || []).filter((supervisorData) => {
16424
+ const lineIds = supervisorData.properties?.line_id || supervisorData.properties?.line_ids || [];
16425
+ return Array.isArray(lineIds) && lineIds.includes(lineId);
16426
+ }).map((row) => {
16427
+ const email = row.email || "";
16428
+ const displayName = row.first_name ? row.last_name ? `${row.first_name} ${row.last_name}` : row.first_name : (typeof email === "string" && email.includes("@") ? email.split("@")[0] : email) || email;
16429
+ return {
16430
+ userId: row.user_id,
16431
+ email,
16432
+ displayName,
16433
+ profilePhotoUrl: row.profile_photo_url
16434
+ };
16435
+ });
16436
+ setSupervisors(supervisorsForLine);
16437
+ setSupervisor(supervisorsForLine[0] || null);
16438
+ })();
16439
+ } catch (fallbackError) {
16440
+ console.error("[useLineSupervisor] Fallback supervisor fetch failed:", fallbackError);
16441
+ setError(fallbackError instanceof Error ? fallbackError : new Error("Failed to fetch supervisors"));
16442
+ setSupervisors([]);
16443
+ setSupervisor(null);
16444
+ }
16166
16445
  } finally {
16167
16446
  setIsLoading(false);
16168
16447
  }
16169
- }, [lineId, supabase]);
16448
+ }, [enabled, lineId, supabase, useBackend, resolvedCompanyId]);
16170
16449
  React26.useEffect(() => {
16171
16450
  fetchSupervisor();
16172
16451
  }, [fetchSupervisor]);
16173
16452
  React26.useEffect(() => {
16174
- if (!lineId || !supabase) {
16453
+ if (!enabled || !lineId || !supabase) {
16175
16454
  return;
16176
16455
  }
16177
16456
  let channel = null;
@@ -16196,7 +16475,7 @@ function useLineSupervisor(lineId) {
16196
16475
  supabase.removeChannel(channel);
16197
16476
  }
16198
16477
  };
16199
- }, [lineId, supabase, fetchSupervisor]);
16478
+ }, [enabled, lineId, supabase, fetchSupervisor]);
16200
16479
  const supervisorName = supervisors.length > 0 ? supervisors.map((s) => s.displayName).join(", ") : null;
16201
16480
  return {
16202
16481
  supervisorName,
@@ -19722,37 +20001,6 @@ var videoPreloader = new VideoPreloader();
19722
20001
  var preloadVideoUrl = videoPreloader.preloadVideo;
19723
20002
  var preloadVideosUrl = videoPreloader.preloadVideos;
19724
20003
 
19725
- // src/lib/utils/rateLimit.ts
19726
- var rateLimitMap2 = /* @__PURE__ */ new Map();
19727
- function checkRateLimit2(identifier, options = {}) {
19728
- const windowMs = options.windowMs || 60 * 1e3;
19729
- const maxRequests = options.maxRequests || 3;
19730
- const now4 = Date.now();
19731
- const userLimit = rateLimitMap2.get(identifier);
19732
- if (userLimit && userLimit.resetTime < now4) {
19733
- rateLimitMap2.delete(identifier);
19734
- }
19735
- if (!userLimit || userLimit.resetTime < now4) {
19736
- rateLimitMap2.set(identifier, {
19737
- count: 1,
19738
- resetTime: now4 + windowMs
19739
- });
19740
- return { allowed: true };
19741
- }
19742
- if (userLimit.count >= maxRequests) {
19743
- const retryAfter = Math.ceil((userLimit.resetTime - now4) / 1e3);
19744
- return { allowed: false, retryAfter };
19745
- }
19746
- userLimit.count++;
19747
- return { allowed: true };
19748
- }
19749
- function clearRateLimit2(identifier) {
19750
- rateLimitMap2.delete(identifier);
19751
- }
19752
- function clearAllRateLimits2() {
19753
- rateLimitMap2.clear();
19754
- }
19755
-
19756
20004
  // src/lib/utils/s3VideoPreloader.ts
19757
20005
  var videoCache = /* @__PURE__ */ new Map();
19758
20006
  var MAX_CONCURRENT_PRELOADS = 2;
@@ -20020,59 +20268,6 @@ var createThrottledReload = (interval = 5e3, maxReloads = 3) => {
20020
20268
  };
20021
20269
  var throttledReloadDashboard = createThrottledReload(5e3, 3);
20022
20270
 
20023
- // src/lib/utils/sentryContext.ts
20024
- function getSentry() {
20025
- try {
20026
- return __require("@sentry/nextjs");
20027
- } catch {
20028
- return null;
20029
- }
20030
- }
20031
- function setSentryUserContext(user) {
20032
- const sentry = getSentry();
20033
- if (!sentry) return;
20034
- if (user) {
20035
- sentry.setUser({
20036
- id: user.id,
20037
- email: user.email
20038
- });
20039
- sentry.setTags({
20040
- company_id: user.company_id || "unknown",
20041
- role: user.role || "unknown",
20042
- role_level: user.role_level || "unknown"
20043
- });
20044
- } else {
20045
- sentry.setUser(null);
20046
- sentry.setTags({
20047
- company_id: void 0,
20048
- role: void 0,
20049
- role_level: void 0
20050
- });
20051
- }
20052
- }
20053
- function setSentryWorkspaceContext(config) {
20054
- const sentry = getSentry();
20055
- if (!sentry) return;
20056
- sentry.setTags({
20057
- workspace_company: config.companyId || "unknown",
20058
- workspace_factory: config.factoryId || "unknown",
20059
- factory_name: config.factoryName || "unknown"
20060
- });
20061
- }
20062
- function clearSentryContext() {
20063
- const sentry = getSentry();
20064
- if (!sentry) return;
20065
- sentry.setUser(null);
20066
- sentry.setTags({
20067
- company_id: void 0,
20068
- role: void 0,
20069
- role_level: void 0,
20070
- workspace_company: void 0,
20071
- workspace_factory: void 0,
20072
- factory_name: void 0
20073
- });
20074
- }
20075
-
20076
20271
  // src/lib/utils/index.ts
20077
20272
  var formatIdleTime = (idleTimeInSeconds) => {
20078
20273
  if (!idleTimeInSeconds || idleTimeInSeconds <= 0) {
@@ -32164,6 +32359,29 @@ var buildUptimeSeries = ({
32164
32359
  hasData: activeMinutes + idleMinutes > 0
32165
32360
  };
32166
32361
  };
32362
+ var getUptimeUtilizationPercent = (shift) => {
32363
+ const efficiency = shift.efficiency;
32364
+ if (Number.isFinite(efficiency)) {
32365
+ return Math.round(Math.max(0, Math.min(100, Number(efficiency))));
32366
+ }
32367
+ const idleSeconds = Number.isFinite(shift.idleTime) ? Number(shift.idleTime) : 0;
32368
+ const activeSeconds = Number.isFinite(shift.activeTimeSeconds) ? Number(shift.activeTimeSeconds) : null;
32369
+ let availableSeconds = Number.isFinite(shift.availableTimeSeconds) ? Number(shift.availableTimeSeconds) : null;
32370
+ if (availableSeconds === null) {
32371
+ if ((activeSeconds ?? 0) > 0 || idleSeconds > 0) {
32372
+ availableSeconds = (activeSeconds ?? 0) + idleSeconds;
32373
+ } else {
32374
+ return 0;
32375
+ }
32376
+ }
32377
+ if (availableSeconds <= 0) return 0;
32378
+ const clampedIdleSeconds = Math.min(Math.max(idleSeconds, 0), availableSeconds);
32379
+ const productiveSeconds = Math.max(
32380
+ activeSeconds ?? availableSeconds - clampedIdleSeconds,
32381
+ 0
32382
+ );
32383
+ return Math.round(productiveSeconds / availableSeconds * 100);
32384
+ };
32167
32385
  var getTimeFromTimeString = (timeStr) => {
32168
32386
  if (!timeStr) {
32169
32387
  return { hour: 0, minute: 0, decimalHour: 0 };
@@ -32178,6 +32396,7 @@ var getTimeFromTimeString = (timeStr) => {
32178
32396
  };
32179
32397
  var HourlyUptimeChartComponent = ({
32180
32398
  idleTimeHourly,
32399
+ hourlyAggregates,
32181
32400
  shiftStart,
32182
32401
  shiftEnd,
32183
32402
  shiftDate,
@@ -32195,6 +32414,7 @@ var HourlyUptimeChartComponent = ({
32195
32414
  timezone,
32196
32415
  elapsedMinutes
32197
32416
  }), [idleTimeHourly, shiftStart, shiftEnd, shiftDate, timezone, elapsedMinutes]);
32417
+ const hasAggregateData = Boolean(hourlyAggregates && hourlyAggregates.length > 0);
32198
32418
  const shiftStartTime = React26__namespace.default.useMemo(
32199
32419
  () => getTimeFromTimeString(shiftStart),
32200
32420
  [shiftStart]
@@ -32261,7 +32481,20 @@ var HourlyUptimeChartComponent = ({
32261
32481
  return `${formatTime5(startHour, startMinute)} - ${formatTime5(endHour, endMinute)}`;
32262
32482
  }, [shiftDuration, shiftStartTime.decimalHour, shiftEndTime]);
32263
32483
  const chartData = React26__namespace.default.useMemo(() => {
32264
- if (!uptimeSeries.points.length || shiftDuration <= 0) return [];
32484
+ if (shiftDuration <= 0) return [];
32485
+ if (hasAggregateData) {
32486
+ return hourlyAggregates.map((entry, hourIndex) => ({
32487
+ hourIndex,
32488
+ hour: formatHour(hourIndex),
32489
+ timeRange: formatTimeRange2(hourIndex),
32490
+ idleMinutes: entry.idleMinutes ?? 0,
32491
+ productiveMinutes: entry.productiveMinutes ?? 0,
32492
+ productivePercent: Math.max(0, Math.min(100, entry.productivePercent)),
32493
+ idlePercent: Math.max(0, Math.min(100, entry.idlePercent)),
32494
+ idleArray: []
32495
+ }));
32496
+ }
32497
+ if (!uptimeSeries.points.length) return [];
32265
32498
  const elapsedShiftMinutes = uptimeSeries.elapsedMinutes ?? uptimeSeries.shiftMinutes;
32266
32499
  return Array.from({ length: shiftDuration }, (_, hourIndex) => {
32267
32500
  const start = hourIndex * 60;
@@ -32286,7 +32519,7 @@ var HourlyUptimeChartComponent = ({
32286
32519
  idleArray
32287
32520
  };
32288
32521
  });
32289
- }, [uptimeSeries.points, uptimeSeries.elapsedMinutes, uptimeSeries.shiftMinutes, shiftDuration, formatHour, formatTimeRange2]);
32522
+ }, [hasAggregateData, hourlyAggregates, uptimeSeries.points, uptimeSeries.elapsedMinutes, uptimeSeries.shiftMinutes, shiftDuration, formatHour, formatTimeRange2]);
32290
32523
  const maxYValue = 100;
32291
32524
  const yAxisTicks = [0, 25, 50, 75, 100];
32292
32525
  React26__namespace.default.useEffect(() => {
@@ -32311,7 +32544,7 @@ var HourlyUptimeChartComponent = ({
32311
32544
  clearTimeout(fallbackTimeout);
32312
32545
  };
32313
32546
  }, []);
32314
- if (!uptimeSeries.hasData) {
32547
+ if (!hasAggregateData && !uptimeSeries.hasData) {
32315
32548
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-full h-full flex items-center justify-center text-sm text-gray-500 ${className}`, children: "No uptime data available." });
32316
32549
  }
32317
32550
  const renderLegend = () => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center text-sm text-gray-600 bg-white py-1", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-4 border border-gray-100 rounded-full px-3 py-1", children: [
@@ -32377,6 +32610,27 @@ var HourlyUptimeChartComponent = ({
32377
32610
  content: (props) => {
32378
32611
  if (!props.active || !props.payload || props.payload.length === 0) return null;
32379
32612
  const data = props.payload[0].payload;
32613
+ if (hasAggregateData) {
32614
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-xl shadow-xl border border-gray-100 p-4 min-w-[220px]", children: [
32615
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between mb-3", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-semibold text-gray-900 text-sm", children: data.timeRange }) }),
32616
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
32617
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
32618
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-gray-500", children: "Productive" }),
32619
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-semibold text-gray-900 text-sm", children: [
32620
+ Number(data.productivePercent || 0).toFixed(2),
32621
+ "%"
32622
+ ] })
32623
+ ] }),
32624
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
32625
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-gray-500", children: "Idle Time" }),
32626
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-semibold text-orange-600 text-sm", children: [
32627
+ Number(data.idlePercent || 0).toFixed(2),
32628
+ "%"
32629
+ ] })
32630
+ ] })
32631
+ ] })
32632
+ ] });
32633
+ }
32380
32634
  const idleRanges = [];
32381
32635
  if (data.idleArray) {
32382
32636
  let currentRange = null;
@@ -41498,7 +41752,7 @@ var DEFAULT_LINE_SHIFT_DATA = {
41498
41752
  var getLineShiftData = (day, shiftId) => {
41499
41753
  const shift = day.shifts[shiftId];
41500
41754
  if (shift) {
41501
- return { ...shift, hasData: true };
41755
+ return { ...shift };
41502
41756
  }
41503
41757
  return { ...DEFAULT_LINE_SHIFT_DATA };
41504
41758
  };
@@ -41510,6 +41764,7 @@ var LineHistoryCalendar = ({
41510
41764
  lineId,
41511
41765
  selectedShiftId,
41512
41766
  legend,
41767
+ monitoringMode,
41513
41768
  rangeStart,
41514
41769
  rangeEnd,
41515
41770
  onDateSelect,
@@ -41519,6 +41774,7 @@ var LineHistoryCalendar = ({
41519
41774
  const { dateTimeConfig } = useDashboardConfig();
41520
41775
  const configuredTimezone = dateTimeConfig?.defaultTimezone || "Asia/Kolkata";
41521
41776
  const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;
41777
+ const isUptimeMode = monitoringMode === "uptime";
41522
41778
  const todayInZone = React26.useMemo(() => {
41523
41779
  const currentTimeInZone = getCurrentTimeInZone(configuredTimezone);
41524
41780
  return typeof currentTimeInZone === "string" ? new Date(currentTimeInZone) : currentTimeInZone;
@@ -41588,7 +41844,11 @@ var LineHistoryCalendar = ({
41588
41844
  const nowString = `${istNow.getFullYear()}-${String(istNow.getMonth() + 1).padStart(2, "0")}-${String(istNow.getDate()).padStart(2, "0")}`;
41589
41845
  const dateString = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
41590
41846
  if (dateString > nowString) return null;
41591
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 bg-black/80 rounded-md sm:rounded-lg p-1 sm:p-1.5 lg:p-2 text-white opacity-0 group-hover:opacity-100 transition-opacity", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-[8px] sm:text-[10px] lg:text-xs space-y-0.5 sm:space-y-1", children: [
41847
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 bg-black/80 rounded-md sm:rounded-lg p-1 sm:p-1.5 lg:p-2 text-white opacity-0 group-hover:opacity-100 transition-opacity", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[8px] sm:text-[10px] lg:text-xs space-y-0.5 sm:space-y-1", children: isUptimeMode ? /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
41848
+ "Utilization: ",
41849
+ Math.round(shift.avg_efficiency || 0),
41850
+ "%"
41851
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
41592
41852
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
41593
41853
  "Eff: ",
41594
41854
  Math.round(shift.avg_efficiency || 0),
@@ -41606,7 +41866,7 @@ var LineHistoryCalendar = ({
41606
41866
  "/",
41607
41867
  shift.total_workspaces || 0
41608
41868
  ] })
41609
- ] }) });
41869
+ ] }) }) });
41610
41870
  };
41611
41871
  const renderDayCell = (day) => {
41612
41872
  if (!day) return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full border border-gray-100 dark:border-gray-700 rounded-md sm:rounded-lg bg-gray-50 dark:bg-gray-800" });
@@ -41880,42 +42140,88 @@ var DEFAULT_PERFORMANCE_DATA = {
41880
42140
  total_workspaces: 0,
41881
42141
  hasData: false,
41882
42142
  output: 0,
41883
- idealOutput: 0
42143
+ idealOutput: 0,
42144
+ idle_time_seconds: 0,
42145
+ active_time_seconds: 0,
42146
+ available_time_seconds: 0,
42147
+ avg_idle_time_seconds: 0
41884
42148
  };
41885
42149
  var getOrdinal = (n) => {
41886
42150
  const suffix = ["th", "st", "nd", "rd"];
41887
42151
  const v = n % 100;
41888
42152
  return n + (suffix[(v - 20) % 10] || suffix[v] || suffix[0]);
41889
42153
  };
41890
- var CustomTooltip2 = ({ active, payload, label }) => {
41891
- if (active && payload && payload.length) {
42154
+ var CustomTooltip2 = ({ active, payload, label, isUptimeMode }) => {
42155
+ if (!active || !payload || payload.length === 0) return null;
42156
+ if (isUptimeMode) {
42157
+ const productive = payload.find((item) => item.dataKey === "productivePercent")?.value ?? 0;
42158
+ const idle = payload.find((item) => item.dataKey === "idlePercent")?.value ?? 0;
42159
+ const total = productive + idle;
42160
+ const utilization = total > 0 ? Math.round(productive / total * 100) : 0;
41892
42161
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white/95 backdrop-blur-sm p-3 rounded-lg shadow-lg border border-gray-100", children: [
41893
42162
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-gray-800 mb-1", children: label }),
41894
42163
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
41895
42164
  /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-gray-600", children: [
41896
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "Actual:" }),
42165
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "Productive:" }),
41897
42166
  " ",
41898
- Math.round(payload[0].value),
41899
- " units"
42167
+ productive.toFixed(1),
42168
+ "%"
41900
42169
  ] }),
41901
- payload[0].payload.idealOutput > 0 && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-gray-600", children: [
41902
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "Target:" }),
42170
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-gray-600", children: [
42171
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "Idle:" }),
42172
+ " ",
42173
+ idle.toFixed(1),
42174
+ "%"
42175
+ ] }),
42176
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-gray-600", children: [
42177
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "Utilization:" }),
41903
42178
  " ",
41904
- Math.round(payload[0].payload.idealOutput),
41905
- " units"
42179
+ utilization,
42180
+ "%"
41906
42181
  ] })
41907
42182
  ] })
41908
42183
  ] });
41909
42184
  }
41910
- return null;
42185
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white/95 backdrop-blur-sm p-3 rounded-lg shadow-lg border border-gray-100", children: [
42186
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-gray-800 mb-1", children: label }),
42187
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
42188
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-gray-600", children: [
42189
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "Actual:" }),
42190
+ " ",
42191
+ Math.round(payload[0].value),
42192
+ " units"
42193
+ ] }),
42194
+ payload[0].payload.idealOutput > 0 && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-gray-600", children: [
42195
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "Target:" }),
42196
+ " ",
42197
+ Math.round(payload[0].payload.idealOutput),
42198
+ " units"
42199
+ ] })
42200
+ ] })
42201
+ ] });
41911
42202
  };
41912
42203
  var getShiftData2 = (day, shiftId) => {
41913
42204
  const shift = day.shifts[shiftId];
41914
42205
  if (shift) {
41915
- return { ...shift, hasData: true };
42206
+ return { ...shift };
41916
42207
  }
41917
42208
  return { ...DEFAULT_PERFORMANCE_DATA };
41918
42209
  };
42210
+ var getUptimeTotals = (shift, hasShiftData) => {
42211
+ if (!shift) {
42212
+ return { availableSeconds: 0, productiveSeconds: 0, idleSeconds: 0 };
42213
+ }
42214
+ const idleSecondsRaw = Math.max(shift.idle_time_seconds || 0, 0);
42215
+ const activeSecondsRaw = Math.max(shift.active_time_seconds || 0, 0);
42216
+ const knownSeconds = activeSecondsRaw + idleSecondsRaw;
42217
+ if (knownSeconds <= 0) {
42218
+ return { availableSeconds: 0, productiveSeconds: 0, idleSeconds: 0 };
42219
+ }
42220
+ const availableSeconds = knownSeconds;
42221
+ const idleSeconds = Math.min(idleSecondsRaw, availableSeconds);
42222
+ const productiveSeconds = activeSecondsRaw > 0 ? Math.min(activeSecondsRaw, availableSeconds) : Math.max(availableSeconds - idleSeconds, 0);
42223
+ return { availableSeconds, productiveSeconds, idleSeconds };
42224
+ };
41919
42225
  var LineMonthlyHistory = ({
41920
42226
  month,
41921
42227
  year,
@@ -41925,6 +42231,7 @@ var LineMonthlyHistory = ({
41925
42231
  rangeEnd,
41926
42232
  timezone,
41927
42233
  legend,
42234
+ monitoringMode,
41928
42235
  underperformingWorkspaces = {},
41929
42236
  lineId,
41930
42237
  selectedShiftId = 0,
@@ -41941,6 +42248,7 @@ var LineMonthlyHistory = ({
41941
42248
  const navigation = useNavigation();
41942
42249
  const { isIdleTimeVlmEnabled } = useIdleTimeVlmConfig();
41943
42250
  const idleTimeVlmEnabled = isIdleTimeVlmEnabled(lineId);
42251
+ const isUptimeMode = monitoringMode === "uptime";
41944
42252
  const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;
41945
42253
  const chartKey = React26.useMemo(() => `${lineId}-${month}-${year}-${selectedShiftId}-${rangeStart}-${rangeEnd}`, [lineId, month, year, selectedShiftId, rangeStart, rangeEnd]);
41946
42254
  const monthBounds = React26.useMemo(() => getMonthKeyBounds(year, month), [year, month]);
@@ -41971,7 +42279,7 @@ var LineMonthlyHistory = ({
41971
42279
  });
41972
42280
  const hasRealData = (shift) => {
41973
42281
  if (shift.hasData !== void 0) return shift.hasData;
41974
- return shift.avg_efficiency > 0 || shift.underperforming_workspaces > 0 || shift.total_workspaces > 0 || (shift.output || 0) > 0 || (shift.idealOutput || 0) > 0;
42282
+ return shift.avg_efficiency > 0 || shift.underperforming_workspaces > 0 || shift.total_workspaces > 0 || (shift.output || 0) > 0 || (shift.idealOutput || 0) > 0 || (shift.idle_time_seconds || 0) > 0 || (shift.active_time_seconds || 0) > 0 || (shift.available_time_seconds || 0) > 0;
41975
42283
  };
41976
42284
  const averages = (analysisMonthlyData || []).reduce(
41977
42285
  (acc, day) => {
@@ -42003,10 +42311,54 @@ var LineMonthlyHistory = ({
42003
42311
  },
42004
42312
  { underperformingDays: 0, totalDays: 0 }
42005
42313
  );
42314
+ const uptimeSummary = React26.useMemo(() => {
42315
+ if (!isUptimeMode) return null;
42316
+ const validDays = (analysisMonthlyData || []).map((day) => getShiftData2(day, selectedShiftId)).filter((shiftData) => shiftData && hasRealData(shiftData)).map((shiftData) => ({ shiftData, totals: getUptimeTotals(shiftData) }));
42317
+ if (!validDays.length) {
42318
+ return { avgUtilization: 0, avgIdleTime: 0, avgDailyStoppages: 0 };
42319
+ }
42320
+ let totalUtilization = 0;
42321
+ let totalIdleSeconds = 0;
42322
+ let idleSamples = 0;
42323
+ let totalStoppages = 0;
42324
+ validDays.forEach(({ shiftData, totals }) => {
42325
+ const utilization = Number.isFinite(shiftData?.avg_efficiency) ? Number(shiftData.avg_efficiency) : 0;
42326
+ totalUtilization += utilization;
42327
+ if (shiftData?.avg_idle_time_seconds !== void 0 && shiftData.avg_idle_time_seconds !== null) {
42328
+ totalIdleSeconds += shiftData.avg_idle_time_seconds;
42329
+ idleSamples += 1;
42330
+ } else if ((shiftData?.total_workspaces || 0) > 0) {
42331
+ totalIdleSeconds += totals.idleSeconds / (shiftData?.total_workspaces || 1);
42332
+ idleSamples += 1;
42333
+ }
42334
+ totalStoppages += shiftData?.output || 0;
42335
+ });
42336
+ return {
42337
+ avgUtilization: Math.round(totalUtilization / validDays.length),
42338
+ avgIdleTime: idleSamples > 0 ? Math.round(totalIdleSeconds / idleSamples) : 0,
42339
+ avgDailyStoppages: Math.round(totalStoppages / validDays.length)
42340
+ };
42341
+ }, [analysisMonthlyData, selectedShiftId, isUptimeMode]);
42006
42342
  const efficiencyDelta = trendSummary?.avg_efficiency?.delta_pp ?? 0;
42007
42343
  const efficiencyImproved = efficiencyDelta >= 0;
42008
42344
  const EfficiencyTrendIcon = efficiencyImproved ? lucideReact.ArrowUp : lucideReact.ArrowDown;
42009
- const efficiencyTrendText = `${Math.abs(efficiencyDelta).toFixed(1)}% vs last month`;
42345
+ const efficiencyTrendText = `${Math.abs(efficiencyDelta).toFixed(1)}%`;
42346
+ const utilizationDelta = efficiencyDelta;
42347
+ const utilizationImproved = utilizationDelta >= 0;
42348
+ const UtilizationTrendIcon = utilizationImproved ? lucideReact.ArrowUp : lucideReact.ArrowDown;
42349
+ const utilizationTrendText = `${Math.abs(utilizationDelta).toFixed(1)}%`;
42350
+ const idleDeltaRaw = trendSummary?.avg_idle_time?.delta_seconds ?? 0;
42351
+ const idlePrev = trendSummary?.avg_idle_time?.previous ?? 0;
42352
+ const idleDelta = idlePrev ? idleDeltaRaw / idlePrev * 100 : 0;
42353
+ const idleImproved = idleDelta <= 0;
42354
+ const IdleTrendIcon = idleImproved ? lucideReact.ArrowDown : lucideReact.ArrowUp;
42355
+ const idleTrendText = `${Math.abs(idleDelta).toFixed(1)}%`;
42356
+ const stoppagesDeltaRaw = trendSummary?.avg_daily_stoppages?.delta_count ?? 0;
42357
+ const stoppagesPrev = trendSummary?.avg_daily_stoppages?.previous ?? 0;
42358
+ const stoppagesDelta = stoppagesPrev ? stoppagesDeltaRaw / stoppagesPrev * 100 : 0;
42359
+ const stoppagesImproved = stoppagesDelta <= 0;
42360
+ const StoppagesTrendIcon = stoppagesImproved ? lucideReact.ArrowDown : lucideReact.ArrowUp;
42361
+ const stoppagesTrendText = `${Math.abs(stoppagesDelta).toFixed(1)}%`;
42010
42362
  const chartData = React26.useMemo(() => {
42011
42363
  const rangeStartDate = parseDateKeyToDate(normalizedRange.startKey);
42012
42364
  const rangeEndDate = parseDateKeyToDate(normalizedRange.endKey);
@@ -42014,6 +42366,37 @@ var LineMonthlyHistory = ({
42014
42366
  for (let d = new Date(rangeStartDate); d <= rangeEndDate; d.setDate(d.getDate() + 1)) {
42015
42367
  dayNumbers.push(d.getDate());
42016
42368
  }
42369
+ if (isUptimeMode) {
42370
+ const dailyData2 = [];
42371
+ let maxHours = 0;
42372
+ const todayInZone = getCurrentTimeInZone(timezone);
42373
+ const todayDate = typeof todayInZone === "string" ? new Date(todayInZone) : todayInZone;
42374
+ const todayKey = `${todayDate.getFullYear()}-${String(todayDate.getMonth() + 1).padStart(2, "0")}-${String(todayDate.getDate()).padStart(2, "0")}`;
42375
+ for (const day of dayNumbers) {
42376
+ const dayKey = `${rangeStartDate.getFullYear()}-${String(rangeStartDate.getMonth() + 1).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
42377
+ const isFutureDay = dayKey > todayKey;
42378
+ const dayData = analysisMonthlyData.find((d) => {
42379
+ const date = new Date(d.date);
42380
+ return date.getDate() === day;
42381
+ });
42382
+ const shiftData = dayData ? getShiftData2(dayData, selectedShiftId) : null;
42383
+ const hasShiftData = Boolean(shiftData && hasRealData(shiftData));
42384
+ const efficiencyValue = hasShiftData && Number.isFinite(shiftData?.avg_efficiency) ? Number(shiftData?.avg_efficiency) : 0;
42385
+ const utilization = !isFutureDay ? efficiencyValue : 0;
42386
+ const productivePercent = isFutureDay ? 0 : Math.max(0, Math.min(100, utilization));
42387
+ const idlePercent = isFutureDay ? 0 : Math.max(0, Math.min(100, 100 - productivePercent));
42388
+ maxHours = 100;
42389
+ dailyData2.push({
42390
+ hour: getOrdinal(day),
42391
+ timeRange: `Day ${day}`,
42392
+ productivePercent,
42393
+ idlePercent,
42394
+ utilization: Math.round(productivePercent)
42395
+ });
42396
+ }
42397
+ const yAxisMax2 = maxHours > 0 ? 100 : 1;
42398
+ return { data: dailyData2, maxOutput: 0, lastSetTarget: 0, yAxisMax: yAxisMax2 };
42399
+ }
42017
42400
  const dailyData = [];
42018
42401
  let maxOutput = 0;
42019
42402
  let lastSetTarget = 0;
@@ -42054,7 +42437,7 @@ var LineMonthlyHistory = ({
42054
42437
  const calculatedMax = Math.max(maxOutput, lastSetTarget);
42055
42438
  const yAxisMax = calculatedMax > 0 ? calculatedMax * 1.1 : 100;
42056
42439
  return { data: dailyData, maxOutput, lastSetTarget, yAxisMax };
42057
- }, [analysisMonthlyData, normalizedRange.startKey, normalizedRange.endKey, selectedShiftId]);
42440
+ }, [analysisMonthlyData, normalizedRange.startKey, normalizedRange.endKey, selectedShiftId, isUptimeMode, timezone]);
42058
42441
  const yAxisTicks = React26.useMemo(() => {
42059
42442
  const max = chartData.yAxisMax;
42060
42443
  const target = chartData.lastSetTarget;
@@ -42076,6 +42459,20 @@ var LineMonthlyHistory = ({
42076
42459
  }
42077
42460
  return Array.from(new Set(ticks)).filter((v) => v >= 0 && v <= max).sort((a, b) => a - b);
42078
42461
  }, [chartData.yAxisMax, chartData.lastSetTarget]);
42462
+ const pieChartData = React26.useMemo(() => {
42463
+ if (!isUptimeMode) return [];
42464
+ const validShifts = (analysisMonthlyData || []).map((day) => getShiftData2(day, selectedShiftId)).filter(hasRealData);
42465
+ if (!validShifts.length) return [];
42466
+ const efficiencyValues = validShifts.map((shift) => Number.isFinite(shift.avg_efficiency) ? Number(shift.avg_efficiency) : null).filter((value) => value !== null);
42467
+ if (!efficiencyValues.length) return [];
42468
+ const avgEfficiency2 = efficiencyValues.reduce((sum, value) => sum + value, 0) / efficiencyValues.length;
42469
+ const productivePercent = Math.round(Math.max(0, Math.min(100, avgEfficiency2)));
42470
+ const idlePercent = Math.max(0, Math.min(100, 100 - productivePercent));
42471
+ return [
42472
+ { name: "Productive", value: productivePercent },
42473
+ { name: "Idle", value: idlePercent }
42474
+ ];
42475
+ }, [analysisMonthlyData, selectedShiftId, isUptimeMode]);
42079
42476
  if (isLoading) {
42080
42477
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center h-[calc(100vh-10rem)]", children: /* @__PURE__ */ jsxRuntime.jsx(
42081
42478
  OptifyeLogoLoader_default,
@@ -42180,11 +42577,39 @@ var LineMonthlyHistory = ({
42180
42577
  rangeStart: normalizedRange.startKey,
42181
42578
  rangeEnd: normalizedRange.endKey,
42182
42579
  legend,
42580
+ monitoringMode,
42183
42581
  onDateSelect: onCalendarDateSelect
42184
42582
  }
42185
42583
  ) }),
42186
42584
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2 sm:gap-3 lg:gap-4", children: [
42187
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-4", children: [
42585
+ isUptimeMode ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-4", children: [
42586
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-lg shadow-sm border border-gray-100 p-3 flex flex-col justify-between", children: [
42587
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-bold text-gray-700 mb-1", children: "Avg Daily Stoppages" }),
42588
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 flex-nowrap", children: [
42589
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-base font-bold text-gray-900 whitespace-nowrap", children: uptimeSummary?.avgDailyStoppages ?? 0 }),
42590
+ /* @__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: [
42591
+ /* @__PURE__ */ jsxRuntime.jsx(StoppagesTrendIcon, { className: "w-3 h-3" }),
42592
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
42593
+ stoppagesTrendText,
42594
+ " vs last month"
42595
+ ] })
42596
+ ] })
42597
+ ] })
42598
+ ] }),
42599
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-lg shadow-sm border border-gray-100 p-3 flex flex-col justify-between", children: [
42600
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-bold text-gray-700 mb-1", children: "Avg Idle Time" }),
42601
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 flex-nowrap", children: [
42602
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-base font-bold text-gray-900 whitespace-nowrap", children: formatIdleTime(uptimeSummary?.avgIdleTime ?? 0) }),
42603
+ /* @__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: [
42604
+ /* @__PURE__ */ jsxRuntime.jsx(IdleTrendIcon, { className: "w-3 h-3" }),
42605
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
42606
+ idleTrendText,
42607
+ " vs last month"
42608
+ ] })
42609
+ ] })
42610
+ ] })
42611
+ ] })
42612
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-4", children: [
42188
42613
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-lg shadow-sm border border-gray-100 p-4 flex flex-col justify-between", children: [
42189
42614
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-bold text-gray-700 mb-2", children: "Avg. Efficiency" }),
42190
42615
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 flex-nowrap", children: [
@@ -42194,7 +42619,10 @@ var LineMonthlyHistory = ({
42194
42619
  ] }),
42195
42620
  /* @__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: [
42196
42621
  /* @__PURE__ */ jsxRuntime.jsx(EfficiencyTrendIcon, { className: "w-3 h-3" }),
42197
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: efficiencyTrendText })
42622
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
42623
+ efficiencyTrendText,
42624
+ " vs last month"
42625
+ ] })
42198
42626
  ] })
42199
42627
  ] })
42200
42628
  ] }),
@@ -42207,7 +42635,110 @@ var LineMonthlyHistory = ({
42207
42635
  ] }) })
42208
42636
  ] })
42209
42637
  ] }),
42210
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `grid grid-cols-1 ${idleTimeVlmEnabled ? "sm:grid-cols-2" : "sm:grid-cols-1"} gap-2 sm:gap-3 lg:gap-4`, children: [
42638
+ isUptimeMode ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `grid grid-cols-1 ${idleTimeVlmEnabled ? "sm:grid-cols-2" : "sm:grid-cols-1"} gap-2 sm:gap-3 lg:gap-4`, children: [
42639
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-lg sm:rounded-xl shadow-sm border border-gray-100 p-2 sm:p-3 lg:p-4 flex flex-col min-h-[160px] sm:min-h-[180px] lg:min-h-[220px]", children: [
42640
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-0.5 sm:mb-1", children: [
42641
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-xs sm:text-sm font-bold text-gray-700 text-left", children: "Utilization" }),
42642
+ /* @__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: [
42643
+ /* @__PURE__ */ jsxRuntime.jsx(UtilizationTrendIcon, { className: "w-3 h-3" }),
42644
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
42645
+ utilizationTrendText,
42646
+ " vs last month"
42647
+ ] })
42648
+ ] })
42649
+ ] }),
42650
+ pieChartData.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full h-[140px] sm:h-[160px] flex items-center overflow-hidden", children: [
42651
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 h-full min-w-0 relative flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs(
42652
+ "div",
42653
+ {
42654
+ className: "relative w-full aspect-square max-h-full",
42655
+ style: { maxWidth: "min(100%, 220px)", containerType: "inline-size" },
42656
+ children: [
42657
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsx(recharts.PieChart, { children: /* @__PURE__ */ jsxRuntime.jsx(
42658
+ recharts.Pie,
42659
+ {
42660
+ data: pieChartData,
42661
+ cx: "50%",
42662
+ cy: "50%",
42663
+ innerRadius: "60%",
42664
+ outerRadius: "80%",
42665
+ dataKey: "value",
42666
+ startAngle: 90,
42667
+ endAngle: -270,
42668
+ isAnimationActive: true,
42669
+ animationBegin: 0,
42670
+ animationDuration: 1e3,
42671
+ animationEasing: "ease-out",
42672
+ children: pieChartData.map((_entry, index) => /* @__PURE__ */ jsxRuntime.jsx(
42673
+ recharts.Cell,
42674
+ {
42675
+ fill: index === 0 ? "#00AB45" : "#e5e7eb",
42676
+ strokeWidth: 0
42677
+ },
42678
+ `cell-${index}`
42679
+ ))
42680
+ }
42681
+ ) }, `${chartKey}-pie`) }),
42682
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 flex flex-col items-center justify-center pointer-events-none", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", style: { width: "100%" }, children: [
42683
+ /* @__PURE__ */ jsxRuntime.jsxs(
42684
+ "div",
42685
+ {
42686
+ className: "font-bold text-gray-900 leading-none",
42687
+ style: { fontSize: "clamp(1rem, 14cqw, 1.4rem)" },
42688
+ children: [
42689
+ pieChartData[0]?.value ?? 0,
42690
+ "%"
42691
+ ]
42692
+ }
42693
+ ),
42694
+ /* @__PURE__ */ jsxRuntime.jsx(
42695
+ "div",
42696
+ {
42697
+ className: "text-gray-500 leading-tight mt-0.5",
42698
+ style: { fontSize: "clamp(0.6rem, 7cqw, 0.75rem)" },
42699
+ children: "Utilization"
42700
+ }
42701
+ )
42702
+ ] }) })
42703
+ ]
42704
+ }
42705
+ ) }),
42706
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-[45%] max-w-[120px] pl-2 flex flex-col justify-center h-full overflow-y-auto", children: /* @__PURE__ */ jsxRuntime.jsxs("ul", { className: "text-xs space-y-1.5", children: [
42707
+ /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "flex items-start", children: [
42708
+ /* @__PURE__ */ jsxRuntime.jsx(
42709
+ "span",
42710
+ {
42711
+ className: "inline-block w-2.5 h-2.5 rounded-full mr-1.5 mt-0.5 flex-shrink-0",
42712
+ style: { backgroundColor: "#00AB45" }
42713
+ }
42714
+ ),
42715
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-600 leading-tight text-[10px] sm:text-xs break-words", children: "Productive" })
42716
+ ] }),
42717
+ /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "flex items-start", children: [
42718
+ /* @__PURE__ */ jsxRuntime.jsx(
42719
+ "span",
42720
+ {
42721
+ className: "inline-block w-2.5 h-2.5 rounded-full mr-1.5 mt-0.5 flex-shrink-0",
42722
+ style: { backgroundColor: "#e5e7eb" }
42723
+ }
42724
+ ),
42725
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-600 leading-tight text-[10px] sm:text-xs break-words", children: "Idle" })
42726
+ ] })
42727
+ ] }) })
42728
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 flex items-center justify-center text-xs text-gray-400 italic", children: "No utilization data" })
42729
+ ] }),
42730
+ idleTimeVlmEnabled && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-lg sm:rounded-xl shadow-sm border border-gray-100 p-2 sm:p-3 lg:p-4 flex flex-col min-h-[160px] sm:min-h-[180px] lg:min-h-[220px]", children: [
42731
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-xs sm:text-sm font-bold text-gray-700 mb-0.5 sm:mb-1 text-left", children: "Idle time Breakdown" }),
42732
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-0 relative -ml-2 sm:-ml-4", children: /* @__PURE__ */ jsxRuntime.jsx(
42733
+ IdleTimeReasonChart,
42734
+ {
42735
+ data: idleReasonsChartData,
42736
+ isLoading: idleReasonsLoading,
42737
+ error: idleReasonsError
42738
+ }
42739
+ ) })
42740
+ ] })
42741
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `grid grid-cols-1 ${idleTimeVlmEnabled ? "sm:grid-cols-2" : "sm:grid-cols-1"} gap-2 sm:gap-3 lg:gap-4`, children: [
42211
42742
  idleTimeVlmEnabled && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-lg sm:rounded-xl shadow-sm border border-gray-100 p-2 sm:p-3 lg:p-4 flex flex-col min-h-[160px] sm:min-h-[180px] lg:min-h-[220px]", children: [
42212
42743
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-xs sm:text-sm font-bold text-gray-700 mb-0.5 sm:mb-1 text-left", children: "Idle time Breakdown" }),
42213
42744
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-0 relative -ml-2 sm:-ml-4", children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -42246,7 +42777,7 @@ var LineMonthlyHistory = ({
42246
42777
  ] })
42247
42778
  ] }),
42248
42779
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-lg sm:rounded-xl shadow-sm border border-gray-100 p-2 sm:p-3 lg:p-4", children: [
42249
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-xs sm:text-sm font-bold text-gray-700 mb-1 sm:mb-2 text-left", children: "Daily Output" }),
42780
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-xs sm:text-sm font-bold text-gray-700 mb-1 sm:mb-2 text-left", children: isUptimeMode ? "Daily Utilization" : "Daily Output" }),
42250
42781
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[160px] sm:h-[180px] lg:h-[220px]", children: /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(
42251
42782
  recharts.BarChart,
42252
42783
  {
@@ -42270,8 +42801,9 @@ var LineMonthlyHistory = ({
42270
42801
  {
42271
42802
  domain: [0, chartData.yAxisMax],
42272
42803
  width: 40,
42273
- ticks: yAxisTicks,
42274
- tick: (props) => {
42804
+ ticks: isUptimeMode ? [0, 25, 50, 75, 100] : yAxisTicks,
42805
+ tickFormatter: isUptimeMode ? (value) => `${value}%` : void 0,
42806
+ tick: isUptimeMode ? void 0 : (props) => {
42275
42807
  const { x, y, payload } = props;
42276
42808
  const value = Math.round(payload.value);
42277
42809
  const targetValue = Math.round(chartData.lastSetTarget);
@@ -42295,10 +42827,10 @@ var LineMonthlyHistory = ({
42295
42827
  recharts.Tooltip,
42296
42828
  {
42297
42829
  cursor: false,
42298
- content: CustomTooltip2
42830
+ content: (props) => /* @__PURE__ */ jsxRuntime.jsx(CustomTooltip2, { ...props, isUptimeMode })
42299
42831
  }
42300
42832
  ),
42301
- chartData.lastSetTarget > 0 && /* @__PURE__ */ jsxRuntime.jsx(
42833
+ !isUptimeMode && chartData.lastSetTarget > 0 && /* @__PURE__ */ jsxRuntime.jsx(
42302
42834
  recharts.ReferenceLine,
42303
42835
  {
42304
42836
  y: chartData.lastSetTarget,
@@ -42307,7 +42839,34 @@ var LineMonthlyHistory = ({
42307
42839
  strokeWidth: 2
42308
42840
  }
42309
42841
  ),
42310
- /* @__PURE__ */ jsxRuntime.jsx(
42842
+ isUptimeMode ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
42843
+ /* @__PURE__ */ jsxRuntime.jsx(
42844
+ recharts.Bar,
42845
+ {
42846
+ dataKey: "productivePercent",
42847
+ stackId: "uptime",
42848
+ radius: [4, 4, 0, 0],
42849
+ fill: "#00AB45",
42850
+ isAnimationActive: true,
42851
+ animationBegin: 0,
42852
+ animationDuration: 800,
42853
+ animationEasing: "ease-out"
42854
+ }
42855
+ ),
42856
+ /* @__PURE__ */ jsxRuntime.jsx(
42857
+ recharts.Bar,
42858
+ {
42859
+ dataKey: "idlePercent",
42860
+ stackId: "uptime",
42861
+ radius: [4, 4, 0, 0],
42862
+ fill: "#e5e7eb",
42863
+ isAnimationActive: true,
42864
+ animationBegin: 0,
42865
+ animationDuration: 800,
42866
+ animationEasing: "ease-out"
42867
+ }
42868
+ )
42869
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(
42311
42870
  recharts.Bar,
42312
42871
  {
42313
42872
  dataKey: "output",
@@ -42339,7 +42898,13 @@ var DEFAULT_PERFORMANCE_DATA2 = {
42339
42898
  avg_efficiency: 0,
42340
42899
  underperforming_workspaces: 0,
42341
42900
  total_workspaces: 0,
42342
- hasData: false
42901
+ hasData: false,
42902
+ output: 0,
42903
+ idealOutput: 0,
42904
+ idle_time_seconds: 0,
42905
+ active_time_seconds: 0,
42906
+ available_time_seconds: 0,
42907
+ avg_idle_time_seconds: 0
42343
42908
  };
42344
42909
  var getLineShiftData2 = (day, shiftId) => {
42345
42910
  const shift = day.shifts[shiftId];
@@ -42360,6 +42925,7 @@ var LineMonthlyPdfGenerator = ({
42360
42925
  lineName,
42361
42926
  monthlyData,
42362
42927
  analysisData,
42928
+ monitoringMode,
42363
42929
  underperformingWorkspaces,
42364
42930
  legend,
42365
42931
  selectedMonth,
@@ -42376,6 +42942,7 @@ var LineMonthlyPdfGenerator = ({
42376
42942
  const generatePDF = async () => {
42377
42943
  setIsGenerating(true);
42378
42944
  try {
42945
+ const isUptimeMode = monitoringMode === "uptime";
42379
42946
  const monthBounds = getMonthKeyBounds(selectedYear, selectedMonth);
42380
42947
  const requestedRange = {
42381
42948
  startKey: rangeStart || monthBounds.startKey,
@@ -42420,7 +42987,7 @@ var LineMonthlyPdfGenerator = ({
42420
42987
  doc.setFontSize(11);
42421
42988
  doc.setFont("helvetica", "normal");
42422
42989
  doc.setTextColor(80, 80, 80);
42423
- const reportText = "MONTHLY PERFORMANCE REPORT";
42990
+ const reportText = isUptimeMode ? "MONTHLY UTILIZATION REPORT" : "MONTHLY PERFORMANCE REPORT";
42424
42991
  const reportTextWidth = doc.getStringUnitWidth(reportText) * 11 / doc.internal.scaleFactor;
42425
42992
  doc.text(reportText, doc.internal.pageSize.width - 20 - reportTextWidth, 15);
42426
42993
  doc.setDrawColor(200, 200, 200);
@@ -42449,16 +43016,66 @@ var LineMonthlyPdfGenerator = ({
42449
43016
  doc.setTextColor(0, 0, 0);
42450
43017
  doc.setDrawColor(180, 180, 180);
42451
43018
  doc.setLineWidth(0.8);
42452
- doc.line(20, 85, 190, 85);
43019
+ const mainSeparatorY = isUptimeMode ? 90 : 85;
43020
+ doc.line(20, mainSeparatorY, 190, mainSeparatorY);
42453
43021
  const reportData = analysisData ? analysisData : filterDataByDateKeyRange(monthlyData, normalizedRange);
42454
43022
  const validDays = reportData.filter((day) => {
42455
43023
  const date = new Date(day.date);
42456
43024
  return date.getMonth() === selectedMonth && date.getFullYear() === selectedYear;
42457
43025
  });
42458
- const validShifts = validDays.map(
42459
- (day) => getLineShiftData2(day, selectedShiftId)
42460
- ).filter((shift) => shift.avg_efficiency >= 5);
42461
- const monthlyMetrics = validShifts.length > 0 ? {
43026
+ const hasShiftData = (shift) => {
43027
+ if (shift.hasData !== void 0) return shift.hasData;
43028
+ 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;
43029
+ };
43030
+ const getUptimeTotals2 = (shift, hasData) => {
43031
+ if (!hasData || !shift) {
43032
+ return { availableSeconds: 0, productiveSeconds: 0, idleSeconds: 0 };
43033
+ }
43034
+ const idleSecondsRaw = Math.max(shift.idle_time_seconds || 0, 0);
43035
+ const activeSecondsRaw = Math.max(shift.active_time_seconds || 0, 0);
43036
+ const knownSeconds = activeSecondsRaw + idleSecondsRaw;
43037
+ if (knownSeconds <= 0) {
43038
+ return { availableSeconds: 0, productiveSeconds: 0, idleSeconds: 0 };
43039
+ }
43040
+ const availableSeconds = knownSeconds;
43041
+ const idleSeconds = Math.min(idleSecondsRaw, availableSeconds);
43042
+ const productiveSeconds = activeSecondsRaw > 0 ? Math.min(activeSecondsRaw, availableSeconds) : Math.max(availableSeconds - idleSeconds, 0);
43043
+ return { availableSeconds, productiveSeconds, idleSeconds };
43044
+ };
43045
+ const validShifts = validDays.map((day) => getLineShiftData2(day, selectedShiftId)).filter((shift) => isUptimeMode ? hasShiftData(shift) : shift.avg_efficiency >= 5);
43046
+ const monthlyMetrics = validShifts.length > 0 ? isUptimeMode ? (() => {
43047
+ let utilizationSum = 0;
43048
+ let idleTimeSum = 0;
43049
+ let stoppagesSum = 0;
43050
+ let idleSamples = 0;
43051
+ let underperformingDays = 0;
43052
+ validShifts.forEach((shift) => {
43053
+ const totals = getUptimeTotals2(shift, hasShiftData(shift));
43054
+ const utilization = Number.isFinite(shift.avg_efficiency) ? Number(shift.avg_efficiency) : 0;
43055
+ utilizationSum += utilization;
43056
+ if (utilization < effectiveLegend.green_min) {
43057
+ underperformingDays += 1;
43058
+ }
43059
+ if (shift.avg_idle_time_seconds !== void 0 && shift.avg_idle_time_seconds !== null) {
43060
+ idleTimeSum += shift.avg_idle_time_seconds;
43061
+ idleSamples += 1;
43062
+ } else if ((shift.total_workspaces || 0) > 0) {
43063
+ idleTimeSum += totals.idleSeconds / (shift.total_workspaces || 1);
43064
+ idleSamples += 1;
43065
+ }
43066
+ stoppagesSum += shift.output || 0;
43067
+ });
43068
+ const avgUtilization = Math.round(utilizationSum / validShifts.length);
43069
+ const avgIdleTime = idleSamples > 0 ? Math.round(idleTimeSum / idleSamples) : 0;
43070
+ const avgDailyStoppages = Math.round(stoppagesSum / validShifts.length);
43071
+ return {
43072
+ avgUtilization,
43073
+ avgIdleTime,
43074
+ avgDailyStoppages,
43075
+ totalDays: validShifts.length,
43076
+ underperformingDays
43077
+ };
43078
+ })() : {
42462
43079
  avgEfficiency: validShifts.reduce((sum, shift) => sum + shift.avg_efficiency, 0) / validShifts.length,
42463
43080
  avgUnderperforming: validShifts.reduce((sum, shift) => sum + shift.underperforming_workspaces, 0) / validShifts.length,
42464
43081
  avgTotalWorkspaces: validShifts.reduce((sum, shift) => sum + shift.total_workspaces, 0) / validShifts.length,
@@ -42472,109 +43089,167 @@ var LineMonthlyPdfGenerator = ({
42472
43089
  doc.setLineWidth(0.2);
42473
43090
  doc.roundedRect(22, y - 7, 165, 12, 2, 2, "S");
42474
43091
  };
43092
+ const overviewStartY = isUptimeMode ? 95 : 90;
43093
+ const overviewHeight = 60;
43094
+ const overviewTitleY = isUptimeMode ? 105 : 100;
42475
43095
  doc.setFillColor(245, 245, 245);
42476
- doc.roundedRect(15, 90, 180, 60, 3, 3, "F");
43096
+ doc.roundedRect(15, overviewStartY, 180, overviewHeight, 3, 3, "F");
42477
43097
  doc.setFontSize(18);
42478
43098
  doc.setFont("helvetica", "bold");
42479
43099
  doc.setTextColor(40, 40, 40);
42480
- doc.text("Monthly Performance Overview", 20, 100);
43100
+ doc.text(isUptimeMode ? "Monthly Utilization Overview" : "Monthly Performance Overview", 20, overviewTitleY);
42481
43101
  doc.setTextColor(0, 0, 0);
42482
43102
  if (monthlyMetrics) {
42483
- const kpiStartY = 112;
43103
+ const kpiStartY = isUptimeMode ? 117 : 112;
42484
43104
  const kpiSpacing = 10;
42485
- createKPIBox(kpiStartY);
42486
- doc.setFontSize(11);
42487
- doc.setFont("helvetica", "normal");
42488
- doc.text("Average Efficiency:", 25, kpiStartY);
42489
- doc.setFont("helvetica", "bold");
42490
- doc.text(`${monthlyMetrics.avgEfficiency.toFixed(1)}% (Target: ${Math.round(effectiveLegend.green_min)}%)`, 120, kpiStartY);
42491
- createKPIBox(kpiStartY + kpiSpacing);
42492
- doc.setFont("helvetica", "normal");
42493
- doc.text("Avg. Underperforming:", 25, kpiStartY + kpiSpacing);
42494
- doc.setFont("helvetica", "bold");
42495
- doc.text(`${monthlyMetrics.avgUnderperforming.toFixed(1)}/${monthlyMetrics.avgTotalWorkspaces.toFixed(1)}`, 120, kpiStartY + kpiSpacing);
42496
- createKPIBox(kpiStartY + kpiSpacing * 2);
42497
- doc.setFont("helvetica", "normal");
42498
- doc.text("Working Days:", 25, kpiStartY + kpiSpacing * 2);
42499
- doc.setFont("helvetica", "bold");
42500
- doc.text(`${monthlyMetrics.totalDays} days`, 120, kpiStartY + kpiSpacing * 2);
42501
- createKPIBox(kpiStartY + kpiSpacing * 3);
42502
- doc.setFont("helvetica", "normal");
42503
- doc.text("Underperforming Days:", 25, kpiStartY + kpiSpacing * 3);
42504
- doc.setFont("helvetica", "bold");
42505
- doc.text(`${monthlyMetrics.underperformingDays} of ${monthlyMetrics.totalDays}`, 120, kpiStartY + kpiSpacing * 3);
43105
+ if (isUptimeMode) {
43106
+ const uptimeMetrics = monthlyMetrics;
43107
+ createKPIBox(kpiStartY);
43108
+ doc.setFontSize(11);
43109
+ doc.setFont("helvetica", "normal");
43110
+ doc.text("Average Utilization:", 25, kpiStartY);
43111
+ doc.setFont("helvetica", "bold");
43112
+ doc.text(`${uptimeMetrics.avgUtilization}%`, 120, kpiStartY);
43113
+ createKPIBox(kpiStartY + kpiSpacing);
43114
+ doc.setFont("helvetica", "normal");
43115
+ doc.text("Average Idle Time:", 25, kpiStartY + kpiSpacing);
43116
+ doc.setFont("helvetica", "bold");
43117
+ doc.text(formatIdleTime(uptimeMetrics.avgIdleTime), 120, kpiStartY + kpiSpacing);
43118
+ createKPIBox(kpiStartY + kpiSpacing * 2);
43119
+ doc.setFont("helvetica", "normal");
43120
+ doc.text("Avg Daily Stoppages:", 25, kpiStartY + kpiSpacing * 2);
43121
+ doc.setFont("helvetica", "bold");
43122
+ doc.text(`${uptimeMetrics.avgDailyStoppages}`, 120, kpiStartY + kpiSpacing * 2);
43123
+ createKPIBox(kpiStartY + kpiSpacing * 3);
43124
+ doc.setFont("helvetica", "normal");
43125
+ doc.text("Low Utilization Days:", 25, kpiStartY + kpiSpacing * 3);
43126
+ doc.setFont("helvetica", "bold");
43127
+ doc.text(`${uptimeMetrics.underperformingDays} of ${uptimeMetrics.totalDays}`, 120, kpiStartY + kpiSpacing * 3);
43128
+ } else {
43129
+ const outputMetrics = monthlyMetrics;
43130
+ createKPIBox(kpiStartY);
43131
+ doc.setFontSize(11);
43132
+ doc.setFont("helvetica", "normal");
43133
+ doc.text("Average Efficiency:", 25, kpiStartY);
43134
+ doc.setFont("helvetica", "bold");
43135
+ doc.text(`${outputMetrics.avgEfficiency.toFixed(1)}% (Target: ${Math.round(effectiveLegend.green_min)}%)`, 120, kpiStartY);
43136
+ createKPIBox(kpiStartY + kpiSpacing);
43137
+ doc.setFont("helvetica", "normal");
43138
+ doc.text("Avg. Underperforming:", 25, kpiStartY + kpiSpacing);
43139
+ doc.setFont("helvetica", "bold");
43140
+ doc.text(`${outputMetrics.avgUnderperforming.toFixed(1)}/${outputMetrics.avgTotalWorkspaces.toFixed(1)}`, 120, kpiStartY + kpiSpacing);
43141
+ createKPIBox(kpiStartY + kpiSpacing * 2);
43142
+ doc.setFont("helvetica", "normal");
43143
+ doc.text("Working Days:", 25, kpiStartY + kpiSpacing * 2);
43144
+ doc.setFont("helvetica", "bold");
43145
+ doc.text(`${outputMetrics.totalDays} days`, 120, kpiStartY + kpiSpacing * 2);
43146
+ createKPIBox(kpiStartY + kpiSpacing * 3);
43147
+ doc.setFont("helvetica", "normal");
43148
+ doc.text("Underperforming Days:", 25, kpiStartY + kpiSpacing * 3);
43149
+ doc.setFont("helvetica", "bold");
43150
+ doc.text(`${outputMetrics.underperformingDays} of ${outputMetrics.totalDays}`, 120, kpiStartY + kpiSpacing * 3);
43151
+ }
42506
43152
  } else {
42507
43153
  doc.setFontSize(12);
42508
43154
  doc.setFont("helvetica", "normal");
42509
43155
  doc.setTextColor(100, 100, 100);
42510
- doc.text("No data available for this month", 25, 115);
43156
+ doc.text("No data available for this month", 25, isUptimeMode ? 125 : 115);
42511
43157
  doc.setTextColor(0, 0, 0);
42512
43158
  }
43159
+ const dailySeparatorY = isUptimeMode ? 180 : 165;
43160
+ const dailySectionStartY = isUptimeMode ? 185 : 170;
43161
+ const dailyTitleY = isUptimeMode ? 195 : 180;
43162
+ const dailyHeaderY = isUptimeMode ? 200 : 185;
43163
+ const dailyHeaderTextY = isUptimeMode ? 205 : 190;
43164
+ const dailyHeaderLineY = isUptimeMode ? 208 : 193;
43165
+ const dailyContentStartY = isUptimeMode ? 215 : 200;
43166
+ const dailyMaxY = isUptimeMode ? 260 : 245;
42513
43167
  doc.setDrawColor(180, 180, 180);
42514
43168
  doc.setLineWidth(0.8);
42515
- doc.line(20, 165, 190, 165);
43169
+ doc.line(20, dailySeparatorY, 190, dailySeparatorY);
42516
43170
  doc.setFillColor(245, 245, 245);
42517
- doc.roundedRect(15, 170, 180, 85, 3, 3, "F");
43171
+ doc.roundedRect(15, dailySectionStartY, 180, 85, 3, 3, "F");
42518
43172
  doc.setFontSize(18);
42519
43173
  doc.setFont("helvetica", "bold");
42520
43174
  doc.setTextColor(40, 40, 40);
42521
- doc.text("Daily Performance Summary", 20, 180);
43175
+ doc.text(isUptimeMode ? "Daily Utilization Summary" : "Daily Performance Summary", 20, dailyTitleY);
42522
43176
  doc.setTextColor(0, 0, 0);
42523
43177
  if (validDays.length > 0) {
42524
43178
  doc.setFontSize(10);
42525
43179
  doc.setFont("helvetica", "bold");
42526
43180
  doc.setFillColor(240, 240, 240);
42527
- doc.roundedRect(20, 185, 170, 7, 1, 1, "F");
42528
- doc.text("Date", 25, 190);
42529
- doc.text("Actual", 60, 190);
42530
- doc.text("Standard", 95, 190);
42531
- doc.text("Efficiency", 135, 190);
42532
- doc.text("Status", 170, 190);
43181
+ doc.roundedRect(20, dailyHeaderY, 170, 7, 1, 1, "F");
43182
+ doc.text("Date", 25, dailyHeaderTextY);
43183
+ if (isUptimeMode) {
43184
+ doc.text("Utilization", 95, dailyHeaderTextY);
43185
+ } else {
43186
+ doc.text("Actual", 60, dailyHeaderTextY);
43187
+ doc.text("Standard", 95, dailyHeaderTextY);
43188
+ doc.text("Efficiency", 135, dailyHeaderTextY);
43189
+ doc.text("Status", 170, dailyHeaderTextY);
43190
+ }
42533
43191
  doc.setLineWidth(0.2);
42534
43192
  doc.setDrawColor(220, 220, 220);
42535
- doc.line(20, 193, 190, 193);
43193
+ doc.line(20, dailyHeaderLineY, 190, dailyHeaderLineY);
42536
43194
  doc.setFont("helvetica", "normal");
42537
- let yPos = 200;
43195
+ let yPos = dailyContentStartY;
42538
43196
  const recentDays = validDays.slice(-10).reverse();
42539
43197
  recentDays.forEach((dayData, index) => {
42540
- if (yPos > 245) return;
43198
+ if (yPos > dailyMaxY) return;
42541
43199
  const shift = getLineShiftData2(dayData, selectedShiftId);
42542
- if (shift.avg_efficiency <= 0 || !shift.hasData) return;
42543
- if (index % 2 === 0) {
42544
- doc.setFillColor(252, 252, 252);
42545
- doc.roundedRect(20, yPos - 4, 170, 7, 1, 1, "F");
42546
- }
42547
- const dateStr = new Date(dayData.date).toLocaleDateString("en-IN", {
42548
- day: "2-digit",
42549
- month: "short",
42550
- timeZone: "Asia/Kolkata"
42551
- });
42552
- doc.text(dateStr, 25, yPos);
42553
- doc.text(`${shift.total_workspaces - shift.underperforming_workspaces}`, 60, yPos);
42554
- doc.text(`${shift.total_workspaces}`, 95, yPos);
42555
- doc.text(`${shift.avg_efficiency.toFixed(1)}%`, 135, yPos);
42556
- const statusColor = getEfficiencyColor(shift.avg_efficiency, effectiveLegend);
42557
- if (statusColor === "green") {
42558
- doc.setTextColor(0, 171, 69);
42559
- doc.text("\u2713", 170, yPos);
42560
- } else if (statusColor === "yellow") {
42561
- doc.setTextColor(255, 176, 32);
42562
- doc.text("!", 170, yPos);
43200
+ if (!hasShiftData(shift)) return;
43201
+ if (isUptimeMode) {
43202
+ doc.setDrawColor(200, 200, 200);
43203
+ doc.setLineWidth(0.1);
43204
+ doc.rect(20, yPos - 5, 75, 8, "S");
43205
+ const dateStr = new Date(dayData.date).toLocaleDateString("en-IN", {
43206
+ day: "2-digit",
43207
+ month: "short",
43208
+ timeZone: "Asia/Kolkata"
43209
+ });
43210
+ doc.text(dateStr, 25, yPos);
43211
+ doc.rect(95, yPos - 5, 95, 8, "S");
43212
+ const utilization = Number.isFinite(shift.avg_efficiency) ? Math.round(Number(shift.avg_efficiency)) : 0;
43213
+ doc.text(`${utilization}%`, 100, yPos);
42563
43214
  } else {
42564
- doc.setTextColor(227, 67, 41);
42565
- doc.text("\xD7", 170, yPos);
43215
+ if (index % 2 === 0) {
43216
+ doc.setFillColor(252, 252, 252);
43217
+ doc.roundedRect(20, yPos - 4, 170, 7, 1, 1, "F");
43218
+ }
43219
+ const dateStr = new Date(dayData.date).toLocaleDateString("en-IN", {
43220
+ day: "2-digit",
43221
+ month: "short",
43222
+ timeZone: "Asia/Kolkata"
43223
+ });
43224
+ doc.text(dateStr, 25, yPos);
43225
+ doc.text(`${shift.total_workspaces - shift.underperforming_workspaces}`, 60, yPos);
43226
+ doc.text(`${shift.total_workspaces}`, 95, yPos);
43227
+ doc.text(`${shift.avg_efficiency.toFixed(1)}%`, 135, yPos);
43228
+ const statusColor = getEfficiencyColor(shift.avg_efficiency, effectiveLegend);
43229
+ if (statusColor === "green") {
43230
+ doc.setTextColor(0, 171, 69);
43231
+ doc.text("\u2713", 170, yPos);
43232
+ } else if (statusColor === "yellow") {
43233
+ doc.setTextColor(255, 176, 32);
43234
+ doc.text("!", 170, yPos);
43235
+ } else {
43236
+ doc.setTextColor(227, 67, 41);
43237
+ doc.text("\xD7", 170, yPos);
43238
+ }
43239
+ doc.setTextColor(0, 0, 0);
42566
43240
  }
42567
- doc.setTextColor(0, 0, 0);
42568
43241
  yPos += 8;
42569
43242
  });
42570
- doc.setLineWidth(0.2);
42571
- doc.setDrawColor(220, 220, 220);
42572
- doc.roundedRect(20, 185, 170, yPos - 185 - 3, 1, 1, "S");
43243
+ if (!isUptimeMode) {
43244
+ doc.setLineWidth(0.2);
43245
+ doc.setDrawColor(220, 220, 220);
43246
+ doc.roundedRect(20, dailyHeaderY, 170, yPos - dailyHeaderY - 3, 1, 1, "S");
43247
+ }
42573
43248
  } else {
42574
43249
  doc.setFontSize(12);
42575
43250
  doc.setFont("helvetica", "normal");
42576
43251
  doc.setTextColor(100, 100, 100);
42577
- doc.text("No daily data available for this month", 25, 200);
43252
+ doc.text("No daily data available for this month", 25, dailyContentStartY);
42578
43253
  doc.setTextColor(0, 0, 0);
42579
43254
  }
42580
43255
  const poorestWorkspaces = underperformingWorkspaces[selectedShiftId] || [];
@@ -42587,7 +43262,7 @@ var LineMonthlyPdfGenerator = ({
42587
43262
  doc.setFontSize(11);
42588
43263
  doc.setFont("helvetica", "normal");
42589
43264
  doc.setTextColor(80, 80, 80);
42590
- const reportText2 = "MONTHLY PERFORMANCE REPORT";
43265
+ const reportText2 = isUptimeMode ? "MONTHLY UTILIZATION REPORT" : "MONTHLY PERFORMANCE REPORT";
42591
43266
  const reportTextWidth2 = doc.getStringUnitWidth(reportText2) * 11 / doc.internal.scaleFactor;
42592
43267
  doc.text(reportText2, doc.internal.pageSize.width - 20 - reportTextWidth2, 15);
42593
43268
  doc.setDrawColor(200, 200, 200);
@@ -42603,7 +43278,7 @@ var LineMonthlyPdfGenerator = ({
42603
43278
  doc.setFillColor(245, 245, 245);
42604
43279
  doc.roundedRect(20, 45, 170, 8, 1, 1, "F");
42605
43280
  doc.text("Workspace", 25, 50);
42606
- doc.text("Avg Efficiency", 120, 50);
43281
+ doc.text(isUptimeMode ? "Avg Utilization" : "Avg Efficiency", 120, 50);
42607
43282
  doc.text("Last 5 Days", 160, 50);
42608
43283
  doc.setLineWidth(0.2);
42609
43284
  doc.setDrawColor(220, 220, 220);
@@ -42681,7 +43356,21 @@ var LineWhatsAppShareButton = ({
42681
43356
  line_name: lineInfo.line_name,
42682
43357
  factory_id: lineInfo.factory_id
42683
43358
  });
42684
- const message = `Line Performance Update for ${lineInfo.line_name}:
43359
+ const isUptimeMode = lineInfo.monitoring_mode === "uptime";
43360
+ const uptimeSeries = isUptimeMode ? buildUptimeSeries({
43361
+ idleTimeHourly: lineInfo.metrics.idle_time_hourly,
43362
+ shiftStart: lineInfo.metrics.shift_start,
43363
+ shiftEnd: lineInfo.metrics.shift_end,
43364
+ shiftDate: lineInfo.date,
43365
+ timezone: "Asia/Kolkata"
43366
+ }) : null;
43367
+ const efficiencyValue = Number.isFinite(lineInfo.metrics.avg_efficiency) ? Number(lineInfo.metrics.avg_efficiency) : null;
43368
+ const utilization = efficiencyValue !== null ? efficiencyValue : uptimeSeries && uptimeSeries.availableMinutes > 0 ? uptimeSeries.activeMinutes / uptimeSeries.availableMinutes * 100 : 0;
43369
+ const idleTimeSeconds = uptimeSeries ? uptimeSeries.idleMinutes * 60 : 0;
43370
+ const message = isUptimeMode ? `Line Utilization Update for ${lineInfo.line_name}:
43371
+ Utilization: ${utilization.toFixed(1)}%
43372
+ Stoppages: ${lineInfo.metrics.current_output}
43373
+ Idle Time: ${formatIdleTime(idleTimeSeconds)}` : `Line Performance Update for ${lineInfo.line_name}:
42685
43374
  Current Output: ${lineInfo.metrics.current_output} / ${lineInfo.metrics.line_threshold}
42686
43375
  Average Efficiency: ${lineInfo.metrics.avg_efficiency.toFixed(1)}%
42687
43376
  Underperforming Workspaces: ${lineInfo.metrics.underperforming_workspaces} / ${lineInfo.metrics.total_workspaces}`;
@@ -42751,13 +43440,15 @@ var LinePdfGenerator = ({
42751
43440
  doc.line(20, 20, 190, 20);
42752
43441
  return 30;
42753
43442
  };
43443
+ const createKPIBox = (y) => {
43444
+ doc.setFillColor(255, 255, 255);
43445
+ doc.roundedRect(22, y - 7, 165, 12, 2, 2, "F");
43446
+ doc.setDrawColor(230, 230, 230);
43447
+ doc.setLineWidth(0.2);
43448
+ doc.roundedRect(22, y - 7, 165, 12, 2, 2, "S");
43449
+ };
42754
43450
  addHeaderPage1();
42755
- doc.setFontSize(26);
42756
- doc.setFont("helvetica", "bold");
42757
- doc.setTextColor(0, 0, 0);
42758
- doc.text(lineInfo.line_name, 20, 30);
42759
- doc.setFontSize(14);
42760
- doc.setFont("helvetica", "normal");
43451
+ const isUptimeMode = lineInfo.monitoring_mode === "uptime";
42761
43452
  const rawShiftType = shiftName || (lineInfo.shift_id === 0 ? "Day" : lineInfo.shift_id === 1 ? "Night" : `Shift ${lineInfo.shift_id}`);
42762
43453
  const shiftType = rawShiftType.toLowerCase().includes("shift") ? rawShiftType : `${rawShiftType} Shift`;
42763
43454
  const date = new Date(lineInfo.date).toLocaleDateString("en-IN", {
@@ -42766,7 +43457,6 @@ var LinePdfGenerator = ({
42766
43457
  month: "long",
42767
43458
  timeZone: "Asia/Kolkata"
42768
43459
  });
42769
- doc.text(`${date} - ${shiftType}`, 20, 40);
42770
43460
  const shiftStartTime = lineInfo.metrics.shift_start ? (/* @__PURE__ */ new Date(`2000-01-01 ${lineInfo.metrics.shift_start}`)).toLocaleTimeString("en-IN", {
42771
43461
  hour: "2-digit",
42772
43462
  minute: "2-digit",
@@ -42789,6 +43479,256 @@ var LinePdfGenerator = ({
42789
43479
  timeZone: "Asia/Kolkata"
42790
43480
  }) : "N/A";
42791
43481
  }
43482
+ if (isUptimeMode) {
43483
+ const configuredTimezone = "Asia/Kolkata";
43484
+ const lineShiftStart = lineInfo.metrics.shift_start || "06:00";
43485
+ const lineShiftEnd = lineInfo.metrics.shift_end || "14:00";
43486
+ const shiftMinutes = getShiftDurationMinutes(lineShiftStart, lineShiftEnd) || 0;
43487
+ const workspaceSummaries = workspaceData.map((workspace) => {
43488
+ const shiftStart = workspace.shift_start || lineShiftStart;
43489
+ const shiftEnd = workspace.shift_end || lineShiftEnd;
43490
+ const shiftDate = workspace.date || lineInfo.date;
43491
+ const shiftDurationMinutes = getShiftDurationMinutes(shiftStart, shiftEnd) || shiftMinutes;
43492
+ const uptimeSeries = buildUptimeSeries({
43493
+ idleTimeHourly: workspace.idle_time_hourly,
43494
+ shiftStart,
43495
+ shiftEnd,
43496
+ shiftDate,
43497
+ timezone: configuredTimezone
43498
+ });
43499
+ let activeMinutes = uptimeSeries.activeMinutes;
43500
+ let idleMinutes = uptimeSeries.idleMinutes;
43501
+ let availableMinutes = uptimeSeries.availableMinutes;
43502
+ let hasData = uptimeSeries.hasData;
43503
+ if (!hasData) {
43504
+ const idleTimeValue = workspace.idle_time;
43505
+ const hasIdleTimeValue = Number.isFinite(idleTimeValue);
43506
+ const fallbackDuration = shiftDurationMinutes;
43507
+ if (hasIdleTimeValue && idleTimeValue > 0 && fallbackDuration > 0) {
43508
+ const idleFromAggregate = Math.min(Math.max(Number(idleTimeValue) / 60, 0), fallbackDuration);
43509
+ idleMinutes = idleFromAggregate;
43510
+ activeMinutes = Math.max(fallbackDuration - idleFromAggregate, 0);
43511
+ availableMinutes = activeMinutes + idleMinutes;
43512
+ hasData = true;
43513
+ }
43514
+ }
43515
+ const utilization = availableMinutes > 0 ? activeMinutes / availableMinutes * 100 : 0;
43516
+ return {
43517
+ workspace,
43518
+ utilization,
43519
+ idleTimeSeconds: idleMinutes * 60,
43520
+ hasData: hasData || availableMinutes > 0,
43521
+ uptimeSeries
43522
+ };
43523
+ });
43524
+ const validSummaries = workspaceSummaries.filter((summary) => summary.hasData);
43525
+ const computedAvgUtilization = validSummaries.length > 0 ? validSummaries.reduce((sum, summary) => sum + summary.utilization, 0) / validSummaries.length : 0;
43526
+ const avgUtilization = Number.isFinite(lineInfo.metrics.avg_efficiency) ? Number(lineInfo.metrics.avg_efficiency) : computedAvgUtilization;
43527
+ const avgIdleSeconds = validSummaries.length > 0 ? validSummaries.reduce((sum, summary) => sum + summary.idleTimeSeconds, 0) / validSummaries.length : 0;
43528
+ const buildHourlyFromSeries = (uptimeSeries) => {
43529
+ if (!uptimeSeries.shiftMinutes || uptimeSeries.shiftMinutes <= 0) return [];
43530
+ const totalHours = Math.ceil(uptimeSeries.shiftMinutes / 60);
43531
+ return Array.from({ length: totalHours }, (_, index) => {
43532
+ const start = index * 60;
43533
+ const end = Math.min(start + 60, uptimeSeries.points.length);
43534
+ const slice = uptimeSeries.points.slice(start, end);
43535
+ const activeMinutes = slice.filter((point) => point.status === "active").length;
43536
+ const idleMinutes = slice.filter((point) => point.status === "idle").length;
43537
+ const total = activeMinutes + idleMinutes;
43538
+ const uptimePercent = total > 0 ? Math.round(activeMinutes / total * 100) : 0;
43539
+ return { uptimePercent };
43540
+ });
43541
+ };
43542
+ let hourlyData = [];
43543
+ const seriesSummaries = workspaceSummaries.filter((summary) => summary.uptimeSeries.hasData && summary.uptimeSeries.points.length > 0);
43544
+ if (seriesSummaries.length > 0) {
43545
+ const hourlyShiftMinutes = seriesSummaries[0].uptimeSeries.shiftMinutes;
43546
+ const shiftDurationHours = Math.ceil(hourlyShiftMinutes / 60);
43547
+ hourlyData = Array.from({ length: shiftDurationHours }, (_, hourIndex) => {
43548
+ const start = hourIndex * 60;
43549
+ const end = Math.min(start + 60, hourlyShiftMinutes);
43550
+ let totalProductivePercent = 0;
43551
+ let workspaceCount = 0;
43552
+ seriesSummaries.forEach((summary) => {
43553
+ const slice = summary.uptimeSeries.points.slice(start, end);
43554
+ if (!slice.length) return;
43555
+ let activeForWorkspace = 0;
43556
+ let idleForWorkspace = 0;
43557
+ slice.forEach((point) => {
43558
+ if (point.status === "active") activeForWorkspace += 1;
43559
+ if (point.status === "idle") idleForWorkspace += 1;
43560
+ });
43561
+ const known = activeForWorkspace + idleForWorkspace;
43562
+ if (known > 0) {
43563
+ workspaceCount += 1;
43564
+ totalProductivePercent += activeForWorkspace / known * 100;
43565
+ }
43566
+ });
43567
+ const avgProductivePercent = workspaceCount > 0 ? totalProductivePercent / workspaceCount : 0;
43568
+ return {
43569
+ uptimePercent: Math.round(avgProductivePercent)
43570
+ };
43571
+ });
43572
+ } else {
43573
+ const lineUptimeSeries = buildUptimeSeries({
43574
+ idleTimeHourly: lineInfo.metrics.idle_time_hourly,
43575
+ shiftStart: lineShiftStart,
43576
+ shiftEnd: lineShiftEnd,
43577
+ shiftDate: lineInfo.date,
43578
+ timezone: configuredTimezone
43579
+ });
43580
+ hourlyData = buildHourlyFromSeries(lineUptimeSeries);
43581
+ }
43582
+ if (hourlyData.length === 0 && shiftMinutes > 0) {
43583
+ const shiftHours = Math.ceil(shiftMinutes / 60);
43584
+ hourlyData = Array.from({ length: shiftHours }, () => ({ uptimePercent: 0 }));
43585
+ }
43586
+ doc.setFillColor(250, 250, 250);
43587
+ doc.roundedRect(15, 25, 180, 53, 3, 3, "F");
43588
+ doc.setFontSize(32);
43589
+ doc.setFont("helvetica", "bold");
43590
+ doc.setTextColor(0, 0, 0);
43591
+ doc.text(lineInfo.line_name, 20, 39);
43592
+ doc.setFontSize(13);
43593
+ doc.setFont("helvetica", "normal");
43594
+ doc.setTextColor(60, 60, 60);
43595
+ doc.text(`${date}`, 20, 51);
43596
+ doc.text(`${shiftType}`, 20, 59);
43597
+ doc.setFontSize(12);
43598
+ doc.setTextColor(80, 80, 80);
43599
+ doc.text(`Report Period: ${shiftStartTime} - ${reportEndTime}`, 20, 67);
43600
+ doc.setTextColor(0, 0, 0);
43601
+ doc.setDrawColor(180, 180, 180);
43602
+ doc.setLineWidth(0.8);
43603
+ doc.line(20, 76, 190, 76);
43604
+ const perfOverviewStartY2 = 81;
43605
+ doc.setFillColor(245, 245, 245);
43606
+ doc.roundedRect(15, perfOverviewStartY2, 180, 60, 3, 3, "F");
43607
+ doc.setFontSize(18);
43608
+ doc.setFont("helvetica", "bold");
43609
+ doc.setTextColor(40, 40, 40);
43610
+ doc.text("Overview", 20, 91);
43611
+ doc.setTextColor(0, 0, 0);
43612
+ const kpiStartY2 = 103;
43613
+ const kpiSpacing2 = 10;
43614
+ createKPIBox(kpiStartY2);
43615
+ doc.setFontSize(11);
43616
+ doc.setFont("helvetica", "normal");
43617
+ doc.text("Utilization:", 25, kpiStartY2);
43618
+ doc.setFont("helvetica", "bold");
43619
+ doc.text(`${Math.round(avgUtilization)}%`, 120, kpiStartY2);
43620
+ createKPIBox(kpiStartY2 + kpiSpacing2);
43621
+ doc.setFont("helvetica", "normal");
43622
+ doc.text("Average Idle Time:", 25, kpiStartY2 + kpiSpacing2);
43623
+ doc.setFont("helvetica", "bold");
43624
+ doc.text(formatIdleTime(avgIdleSeconds), 120, kpiStartY2 + kpiSpacing2);
43625
+ createKPIBox(kpiStartY2 + kpiSpacing2 * 2);
43626
+ doc.setFont("helvetica", "normal");
43627
+ doc.text("Stoppages:", 25, kpiStartY2 + kpiSpacing2 * 2);
43628
+ doc.setFont("helvetica", "bold");
43629
+ doc.text(`${lineInfo.metrics.current_output || 0}`, 120, kpiStartY2 + kpiSpacing2 * 2);
43630
+ const separatorBeforeHourlyY = 151;
43631
+ doc.setDrawColor(180, 180, 180);
43632
+ doc.setLineWidth(0.8);
43633
+ doc.line(20, separatorBeforeHourlyY, 190, separatorBeforeHourlyY);
43634
+ const hourlyPerfStartY = 156;
43635
+ const maxContentY = pageHeight - 15;
43636
+ const baseTableStartY = hourlyPerfStartY + 31;
43637
+ let tableStartY2 = baseTableStartY;
43638
+ let rowHeight = 8;
43639
+ let headerFontSize = 11;
43640
+ let contentFontSize = 11;
43641
+ let titleFontSize = 18;
43642
+ const bottomPadding2 = 8;
43643
+ const estimatedEndY = tableStartY2 + hourlyData.length * rowHeight + bottomPadding2;
43644
+ if (estimatedEndY > maxContentY) {
43645
+ rowHeight = 6.5;
43646
+ headerFontSize = 9;
43647
+ contentFontSize = 9;
43648
+ titleFontSize = 16;
43649
+ tableStartY2 = hourlyPerfStartY + 27;
43650
+ }
43651
+ const backgroundHeight2 = tableStartY2 - hourlyPerfStartY + hourlyData.length * rowHeight + bottomPadding2;
43652
+ doc.setFillColor(245, 245, 245);
43653
+ doc.roundedRect(15, hourlyPerfStartY, 180, backgroundHeight2, 3, 3, "F");
43654
+ doc.setFontSize(titleFontSize);
43655
+ doc.setFont("helvetica", "bold");
43656
+ doc.setTextColor(40, 40, 40);
43657
+ const hourlyTitleY = hourlyPerfStartY + 10;
43658
+ doc.text("Hourly Utilization", 20, hourlyTitleY);
43659
+ doc.setTextColor(0, 0, 0);
43660
+ const headerY = titleFontSize === 16 ? hourlyPerfStartY + 18 : hourlyPerfStartY + 20;
43661
+ const gridTopY2 = headerY - 5;
43662
+ const headerBottomY2 = gridTopY2 + 8;
43663
+ const colBoundaries2 = [20, 105, 190];
43664
+ const totalRows2 = hourlyData.length;
43665
+ const gridBottomY2 = headerBottomY2 + totalRows2 * rowHeight;
43666
+ const tableHeight2 = gridBottomY2 - gridTopY2;
43667
+ doc.setFillColor(255, 255, 255);
43668
+ doc.roundedRect(20, gridTopY2, 170, tableHeight2, 2, 2, "F");
43669
+ doc.setDrawColor(230, 230, 230);
43670
+ doc.setLineWidth(0.2);
43671
+ doc.roundedRect(20, gridTopY2, 170, tableHeight2, 2, 2, "S");
43672
+ doc.setDrawColor(200, 200, 200);
43673
+ doc.setLineWidth(0.3);
43674
+ doc.line(20, headerBottomY2, 190, headerBottomY2);
43675
+ colBoundaries2.slice(1, -1).forEach((x) => {
43676
+ doc.line(x, gridTopY2, x, gridBottomY2);
43677
+ });
43678
+ doc.setFontSize(headerFontSize);
43679
+ doc.setFont("helvetica", "bold");
43680
+ const headerTextY = gridTopY2 + 5.5;
43681
+ doc.text("Time Range", 25, headerTextY);
43682
+ doc.text("Utilization", 147, headerTextY);
43683
+ doc.setFontSize(contentFontSize);
43684
+ doc.setFont("helvetica", "normal");
43685
+ let yPos2 = headerBottomY2 + 5.5;
43686
+ const now5 = /* @__PURE__ */ new Date();
43687
+ const currentTimeIST2 = new Date(now5.toLocaleString("en-US", { timeZone: configuredTimezone }));
43688
+ const [sYear2, sMonth2, sDay2] = lineInfo.date.split("-").map(Number);
43689
+ const [sStartH2, sStartM2] = (lineShiftStart || "06:00").split(":").map(Number);
43690
+ const shiftStartBase2 = new Date(sYear2, sMonth2 - 1, sDay2, sStartH2, sStartM2 || 0);
43691
+ hourlyData.forEach((entry, index) => {
43692
+ const bucketStartTime = new Date(shiftStartBase2);
43693
+ bucketStartTime.setHours(bucketStartTime.getHours() + index);
43694
+ const bucketEndTime = new Date(bucketStartTime);
43695
+ bucketEndTime.setHours(bucketEndTime.getHours() + 1);
43696
+ const timeRange = `${bucketStartTime.toLocaleTimeString("en-IN", {
43697
+ hour: "numeric",
43698
+ hour12: true
43699
+ })} - ${bucketEndTime.toLocaleTimeString("en-IN", {
43700
+ hour: "numeric",
43701
+ hour12: true
43702
+ })}`;
43703
+ const dataCollected = bucketEndTime.getTime() <= currentTimeIST2.getTime();
43704
+ if (index < totalRows2 - 1) {
43705
+ const rowBottomY = headerBottomY2 + (index + 1) * rowHeight;
43706
+ doc.setDrawColor(200, 200, 200);
43707
+ doc.line(20, rowBottomY, 190, rowBottomY);
43708
+ }
43709
+ doc.text(timeRange, 25, yPos2);
43710
+ const utilizationStr = dataCollected ? `${entry.uptimePercent}%` : "TBD";
43711
+ doc.text(utilizationStr, 147, yPos2);
43712
+ yPos2 += rowHeight;
43713
+ });
43714
+ const fileDate2 = new Date(lineInfo.date).toLocaleDateString("en-IN", {
43715
+ day: "2-digit",
43716
+ month: "short",
43717
+ year: "numeric",
43718
+ timeZone: "Asia/Kolkata"
43719
+ }).replace(/ /g, "_");
43720
+ const fileShift2 = shiftType.replace(/ /g, "_");
43721
+ const fileName2 = `${lineInfo.line_name}_${fileDate2}_${fileShift2}.pdf`;
43722
+ doc.save(fileName2);
43723
+ return;
43724
+ }
43725
+ doc.setFontSize(26);
43726
+ doc.setFont("helvetica", "bold");
43727
+ doc.setTextColor(0, 0, 0);
43728
+ doc.text(lineInfo.line_name, 20, 30);
43729
+ doc.setFontSize(14);
43730
+ doc.setFont("helvetica", "normal");
43731
+ doc.text(`${date} - ${shiftType}`, 20, 40);
42792
43732
  doc.setFontSize(12);
42793
43733
  doc.setTextColor(100, 100, 100);
42794
43734
  doc.text(`Report Period: ${shiftStartTime} - ${reportEndTime}`, 20, 47);
@@ -42804,13 +43744,6 @@ var LinePdfGenerator = ({
42804
43744
  doc.setTextColor(40, 40, 40);
42805
43745
  doc.text("Line Performance Overview", 20, 68);
42806
43746
  doc.setTextColor(0, 0, 0);
42807
- const createKPIBox = (y) => {
42808
- doc.setFillColor(255, 255, 255);
42809
- doc.roundedRect(22, y - 7, 165, 12, 2, 2, "F");
42810
- doc.setDrawColor(230, 230, 230);
42811
- doc.setLineWidth(0.2);
42812
- doc.roundedRect(22, y - 7, 165, 12, 2, 2, "S");
42813
- };
42814
43747
  const kpiStartY = 80;
42815
43748
  const kpiSpacing = 10;
42816
43749
  createKPIBox(kpiStartY);
@@ -43084,23 +44017,17 @@ var LinePdfGenerator = ({
43084
44017
  doc.text("Remarks", 160, tableHeaderY);
43085
44018
  doc.setFont("helvetica", "normal");
43086
44019
  let yPos = tableStartY;
43087
- const lineDateForTable = new Date(lineInfo.date);
43088
- const todayForTable = /* @__PURE__ */ new Date();
43089
- todayForTable.setHours(0, 0, 0, 0);
43090
- lineDateForTable.setHours(0, 0, 0, 0);
43091
- const isTodayForTable = lineDateForTable.getTime() === todayForTable.getTime();
43092
- let currentHour = 24;
43093
- if (isTodayForTable) {
43094
- const now4 = /* @__PURE__ */ new Date();
43095
- const currentTimeIST = new Date(now4.toLocaleString("en-US", { timeZone: "Asia/Kolkata" }));
43096
- currentHour = currentTimeIST.getHours();
43097
- }
44020
+ const now4 = /* @__PURE__ */ new Date();
44021
+ const currentTimeIST = new Date(now4.toLocaleString("en-US", { timeZone: "Asia/Kolkata" }));
44022
+ const [sYear, sMonth, sDay] = lineInfo.date.split("-").map(Number);
44023
+ const [sStartH, sStartM] = (lineInfo.metrics.shift_start || "06:00").split(":").map(Number);
44024
+ const shiftStartBase = new Date(sYear, sMonth - 1, sDay, sStartH, sStartM || 0);
43098
44025
  hourlyTimeRanges.forEach((timeRange, index) => {
43099
44026
  const actualOutput = hourlyActualOutput[index] || 0;
43100
- const [startHourStr] = (lineInfo.metrics.shift_start || "6:00").split(":");
43101
- const startHour = parseInt(startHourStr);
43102
- const hourNumber = (startHour + index) % 24;
43103
- const dataCollected = !isTodayForTable || hourNumber < currentHour;
44027
+ const bucketStartTime = new Date(shiftStartBase);
44028
+ bucketStartTime.setHours(bucketStartTime.getHours() + index);
44029
+ const bucketEndTime = new Date(bucketStartTime.getTime() + timeRange.minutes * 60 * 1e3);
44030
+ const dataCollected = bucketEndTime.getTime() <= currentTimeIST.getTime();
43104
44031
  const outputStr = dataCollected ? actualOutput.toString() : "TBD";
43105
44032
  if (index < totalRows - 1) {
43106
44033
  const rowBottomY = headerBottomY + (index + 1) * rowSpacing;
@@ -43516,7 +44443,7 @@ var WorkspaceHistoryCalendar = ({
43516
44443
  const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;
43517
44444
  const hasRealData = (shift) => {
43518
44445
  if (shift.hasData !== void 0) return shift.hasData;
43519
- return shift.efficiency > 0 || shift.output > 0 || shift.cycleTime > 0 || shift.pph > 0 || shift.idealOutput > 0 || shift.idleTime > 0;
44446
+ return shift.efficiency > 0 || shift.output > 0 || shift.cycleTime > 0 || shift.pph > 0 || shift.targetOutput > 0 || shift.idleTime > 0;
43520
44447
  };
43521
44448
  React26.useEffect(() => {
43522
44449
  setAnimationComplete(false);
@@ -43663,7 +44590,7 @@ var WorkspaceHistoryCalendar = ({
43663
44590
  "Output: ",
43664
44591
  Math.round(shift.output),
43665
44592
  " / ",
43666
- Math.round(shift.idealOutput)
44593
+ Math.round(shift.targetOutput)
43667
44594
  ] }),
43668
44595
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
43669
44596
  "Cycle: ",
@@ -43827,7 +44754,8 @@ var CustomTooltip3 = ({ active, payload, label, isUptimeMode }) => {
43827
44754
  /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-gray-600", children: [
43828
44755
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "Utilization:" }),
43829
44756
  " ",
43830
- utilization
44757
+ utilization,
44758
+ "%"
43831
44759
  ] })
43832
44760
  ] })
43833
44761
  ] });
@@ -43917,7 +44845,7 @@ var WorkspaceMonthlyHistory = ({
43917
44845
  });
43918
44846
  const hasRealData = (shift) => {
43919
44847
  if (shift.hasData !== void 0) return shift.hasData;
43920
- return shift.efficiency > 0 || shift.output > 0 || shift.cycleTime > 0 || shift.pph > 0 || shift.idealOutput > 0 || shift.idleTime > 0;
44848
+ return shift.efficiency > 0 || shift.output > 0 || shift.cycleTime > 0 || shift.pph > 0 || shift.idealOutput > 0 || shift.idleTime > 0 || (shift.availableTimeSeconds ?? 0) > 0;
43921
44849
  };
43922
44850
  const shiftWorkSeconds = React26.useMemo(() => {
43923
44851
  if (!shiftConfig) return null;
@@ -43933,7 +44861,6 @@ var WorkspaceMonthlyHistory = ({
43933
44861
  const dailyData = [];
43934
44862
  if (isUptimeMode) {
43935
44863
  let maxHours = 0;
43936
- const shiftSeconds = shiftWorkSeconds ?? 0;
43937
44864
  for (const day of dayNumbers) {
43938
44865
  const dayData = analysisMonthlyData.find((d) => {
43939
44866
  const date = new Date(d.date);
@@ -43941,12 +44868,28 @@ var WorkspaceMonthlyHistory = ({
43941
44868
  });
43942
44869
  const shiftData = dayData ? getShiftData(dayData, selectedShiftId) : null;
43943
44870
  const hasShiftData = Boolean(shiftData && hasRealData(shiftData));
43944
- const idleSeconds = hasShiftData ? shiftData.idleTime : 0;
43945
- const clampedIdleSeconds = hasShiftData && shiftSeconds > 0 ? Math.min(Math.max(idleSeconds, 0), shiftSeconds) : Math.max(idleSeconds, 0);
43946
- const productiveSeconds = hasShiftData && shiftSeconds > 0 ? Math.max(shiftSeconds - clampedIdleSeconds, 0) : 0;
43947
- const idleHours = clampedIdleSeconds / 3600;
43948
- const productiveHours = productiveSeconds / 3600;
43949
- const utilization = hasShiftData && shiftSeconds > 0 ? Math.round(productiveSeconds / shiftSeconds * 100) : 0;
44871
+ const availableSeconds = hasShiftData ? shiftData.availableTimeSeconds ?? shiftWorkSeconds ?? 0 : 0;
44872
+ const efficiencyValue = hasShiftData && Number.isFinite(shiftData?.efficiency) ? Number(shiftData.efficiency) : null;
44873
+ let productiveHours = 0;
44874
+ let idleHours = 0;
44875
+ let utilization = 0;
44876
+ if (efficiencyValue !== null && availableSeconds > 0) {
44877
+ const clampedEfficiency = Math.max(0, Math.min(100, efficiencyValue));
44878
+ utilization = Math.round(clampedEfficiency);
44879
+ const totalHours = availableSeconds / 3600;
44880
+ productiveHours = totalHours * clampedEfficiency / 100;
44881
+ idleHours = Math.max(0, totalHours - productiveHours);
44882
+ } else if (hasShiftData && availableSeconds > 0) {
44883
+ const idleSeconds = shiftData.idleTime || 0;
44884
+ const clampedIdleSeconds = Math.min(Math.max(idleSeconds, 0), availableSeconds);
44885
+ const productiveSeconds = Math.max(
44886
+ shiftData.activeTimeSeconds ?? availableSeconds - clampedIdleSeconds,
44887
+ 0
44888
+ );
44889
+ productiveHours = productiveSeconds / 3600;
44890
+ idleHours = clampedIdleSeconds / 3600;
44891
+ utilization = Math.round(productiveSeconds / availableSeconds * 100);
44892
+ }
43950
44893
  maxHours = Math.max(maxHours, idleHours + productiveHours);
43951
44894
  dailyData.push({
43952
44895
  hour: getOrdinal2(day),
@@ -44043,12 +44986,11 @@ var WorkspaceMonthlyHistory = ({
44043
44986
  }, [chartData.yAxisMax, chartData.lastSetTarget, isUptimeMode]);
44044
44987
  const pieChartData = React26.useMemo(() => {
44045
44988
  const validShifts = analysisMonthlyData.map((d) => getShiftData(d, selectedShiftId)).filter(hasRealData);
44046
- if (validShifts.length === 0 || !shiftWorkSeconds) return [];
44047
- const totalIdleTime = validShifts.reduce((sum, shift) => sum + shift.idleTime, 0);
44048
- const totalShiftTime = validShifts.length * shiftWorkSeconds;
44049
- const clampedIdleTime = Math.min(Math.max(totalIdleTime, 0), totalShiftTime);
44050
- const activeTime = Math.max(0, totalShiftTime - clampedIdleTime);
44051
- const productivePercent = Math.round(activeTime / totalShiftTime * 100);
44989
+ if (validShifts.length === 0) return [];
44990
+ const efficiencyValues = validShifts.map((shift) => Number.isFinite(shift.efficiency) ? Number(shift.efficiency) : null).filter((value) => value !== null);
44991
+ if (!efficiencyValues.length) return [];
44992
+ const avgEfficiency = efficiencyValues.reduce((sum, value) => sum + value, 0) / efficiencyValues.length;
44993
+ const productivePercent = Math.round(Math.max(0, Math.min(100, avgEfficiency)));
44052
44994
  const idlePercent = Math.max(0, Math.min(100, 100 - productivePercent));
44053
44995
  return [
44054
44996
  { name: "Productive", value: productivePercent },
@@ -44057,30 +44999,37 @@ var WorkspaceMonthlyHistory = ({
44057
44999
  }, [analysisMonthlyData, selectedShiftId, shiftWorkSeconds]);
44058
45000
  const metrics2 = React26.useMemo(() => {
44059
45001
  const validShifts = analysisMonthlyData.map((d) => getShiftData(d, selectedShiftId)).filter(hasRealData);
44060
- if (validShifts.length === 0) return null;
44061
- const totalEfficiency = validShifts.reduce((sum, shift) => sum + shift.efficiency, 0);
44062
- const totalCycleTime = validShifts.reduce((sum, shift) => sum + shift.cycleTime, 0);
44063
- const totalOutput = validShifts.reduce((sum, shift) => sum + shift.output, 0);
44064
- validShifts.reduce((sum, shift) => sum + shift.idealOutput, 0);
44065
- const totalIdleTime = validShifts.reduce((sum, shift) => sum + shift.idleTime, 0);
44066
- const ranks = validShifts.map((s) => s.rank).filter((r2) => typeof r2 === "number" && r2 > 0);
45002
+ const filteredShifts = isUptimeMode ? validShifts : validShifts.filter((shift) => (shift.efficiency ?? 0) >= 5);
45003
+ if (filteredShifts.length === 0) return null;
45004
+ const totalEfficiency = filteredShifts.reduce((sum, shift) => sum + (shift.efficiency || 0), 0);
45005
+ const totalUtilization = filteredShifts.reduce(
45006
+ (sum, shift) => sum + getUptimeUtilizationPercent(shift),
45007
+ 0
45008
+ );
45009
+ const totalCycleTime = filteredShifts.reduce((sum, shift) => sum + shift.cycleTime, 0);
45010
+ const totalOutput = filteredShifts.reduce((sum, shift) => sum + shift.output, 0);
45011
+ filteredShifts.reduce((sum, shift) => sum + shift.idealOutput, 0);
45012
+ const totalIdleTime = filteredShifts.reduce((sum, shift) => sum + shift.idleTime, 0);
45013
+ const ranks = filteredShifts.map((s) => s.rank).filter((r2) => typeof r2 === "number" && r2 > 0);
44067
45014
  if (isUptimeMode) {
44068
- const shiftSeconds = shiftWorkSeconds ?? 0;
44069
- const totalShiftSeconds = shiftSeconds * validShifts.length;
44070
- const activeSeconds = Math.max(totalShiftSeconds - totalIdleTime, 0);
44071
- const avgUtilization = totalShiftSeconds > 0 ? Math.round(activeSeconds / totalShiftSeconds * 100) : 0;
44072
- const avgIdleTime = Math.round(totalIdleTime / validShifts.length);
44073
- Math.round(totalCycleTime / validShifts.length);
45015
+ let totalIdleSeconds = 0;
45016
+ filteredShifts.forEach((shift) => {
45017
+ const availableSeconds = shift.availableTimeSeconds ?? shiftWorkSeconds ?? 0;
45018
+ const idleSeconds = Math.min(Math.max(shift.idleTime || 0, 0), availableSeconds);
45019
+ totalIdleSeconds += idleSeconds;
45020
+ });
45021
+ const avgUtilization = filteredShifts.length > 0 ? Math.round(totalUtilization / filteredShifts.length) : 0;
45022
+ const avgIdleTime = filteredShifts.length > 0 ? Math.round(totalIdleSeconds / filteredShifts.length) : 0;
45023
+ Math.round(totalCycleTime / filteredShifts.length);
44074
45024
  return {
44075
45025
  avgUtilization,
44076
45026
  avgIdleTime,
44077
- avgDailyStoppages: 0
44078
- // Mock data
45027
+ avgDailyStoppages: Math.round(totalOutput / filteredShifts.length)
44079
45028
  };
44080
45029
  }
44081
- const avgEfficiency = Math.round(totalEfficiency / validShifts.length);
44082
- const avgDailyOutput = Math.round(totalOutput / validShifts.length);
44083
- const avgCycleTime = Math.round(totalCycleTime / validShifts.length);
45030
+ const avgEfficiency = Math.round(totalEfficiency / filteredShifts.length);
45031
+ const avgDailyOutput = Math.round(totalOutput / filteredShifts.length);
45032
+ const avgCycleTime = Math.round(totalCycleTime / filteredShifts.length);
44084
45033
  const avgRank = ranks.length > 0 ? Math.round(ranks.reduce((a, b) => a + b, 0) / ranks.length) : null;
44085
45034
  return {
44086
45035
  avgEfficiency,
@@ -44088,7 +45037,7 @@ var WorkspaceMonthlyHistory = ({
44088
45037
  avgCycleTime,
44089
45038
  avgRank,
44090
45039
  totalOutput,
44091
- avgIdleTime: Math.round(totalIdleTime / validShifts.length)
45040
+ avgIdleTime: Math.round(totalIdleTime / filteredShifts.length)
44092
45041
  };
44093
45042
  }, [analysisMonthlyData, selectedShiftId, isUptimeMode, shiftWorkSeconds]);
44094
45043
  const efficiencyDelta = trendSummary?.avg_efficiency?.delta_pp ?? 0;
@@ -44101,6 +45050,19 @@ var WorkspaceMonthlyHistory = ({
44101
45050
  const outputImproved = outputDelta >= 0;
44102
45051
  const cycleDelta = cyclePrev ? cycleDeltaRaw / cyclePrev * 100 : 0;
44103
45052
  const cycleWorsened = cycleDelta > 0;
45053
+ const utilizationDelta = efficiencyDelta;
45054
+ const utilizationImproved = utilizationDelta >= 0;
45055
+ const utilizationTrendText = `${Math.abs(utilizationDelta).toFixed(1)}% vs last month`;
45056
+ const idleDeltaRaw = trendSummary?.avg_idle_time?.delta_seconds ?? 0;
45057
+ const idlePrev = trendSummary?.avg_idle_time?.previous ?? 0;
45058
+ const idleDelta = idlePrev ? idleDeltaRaw / idlePrev * 100 : 0;
45059
+ const idleImproved = idleDelta <= 0;
45060
+ const idleTrendText = `${Math.abs(idleDelta).toFixed(1)}% vs last month`;
45061
+ const stoppagesDeltaRaw = trendSummary?.avg_daily_stoppages?.delta_count ?? 0;
45062
+ const stoppagesPrev = trendSummary?.avg_daily_stoppages?.previous ?? 0;
45063
+ const stoppagesDelta = stoppagesPrev ? stoppagesDeltaRaw / stoppagesPrev * 100 : 0;
45064
+ const stoppagesImproved = stoppagesDelta <= 0;
45065
+ const stoppagesTrendText = `${Math.abs(stoppagesDelta).toFixed(1)}% vs last month`;
44104
45066
  const calendarData = React26.useMemo(() => {
44105
45067
  const startOfMonth2 = new Date(year, month, 1);
44106
45068
  const endOfMonth2 = new Date(year, month + 1, 0);
@@ -44185,11 +45147,12 @@ var WorkspaceMonthlyHistory = ({
44185
45147
  if (!hasData || !shiftData) return "bg-gray-300 dark:bg-gray-600";
44186
45148
  if (showRange && !inRange) return "bg-gray-200 dark:bg-gray-700";
44187
45149
  const idleSeconds = Math.max(shiftData.idleTime || 0, 0);
44188
- const totalShiftSeconds = shiftWorkSeconds ?? 0;
45150
+ const totalShiftSeconds = shiftData.availableTimeSeconds ?? shiftWorkSeconds ?? 0;
44189
45151
  const clampedIdleSeconds = totalShiftSeconds > 0 ? Math.min(idleSeconds, totalShiftSeconds) : idleSeconds;
44190
- const utilization = totalShiftSeconds > 0 ? Math.round((totalShiftSeconds - clampedIdleSeconds) / totalShiftSeconds * 100) : shiftData.efficiency;
45152
+ const fallbackUtilization = totalShiftSeconds > 0 ? Math.round((totalShiftSeconds - clampedIdleSeconds) / totalShiftSeconds * 100) : 0;
45153
+ const utilizationValue = Number.isFinite(shiftData.efficiency) ? shiftData.efficiency : fallbackUtilization;
44191
45154
  const efficiencyColor = getEfficiencyColor(
44192
- isUptimeMode ? utilization : shiftData.efficiency,
45155
+ isUptimeMode ? utilizationValue : shiftData.efficiency,
44193
45156
  effectiveLegend
44194
45157
  );
44195
45158
  if (efficiencyColor === "green") return "bg-green-500 dark:bg-green-600";
@@ -44220,7 +45183,16 @@ var WorkspaceMonthlyHistory = ({
44220
45183
  !isFuture && hasData && shiftData && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 bg-black/80 rounded-lg p-2 text-white opacity-0 group-hover:opacity-100 transition-opacity", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs space-y-1", children: isUptimeMode ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
44221
45184
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
44222
45185
  "Utilization: ",
44223
- shiftWorkSeconds ? Math.round((shiftWorkSeconds - Math.min(Math.max(shiftData.idleTime || 0, 0), shiftWorkSeconds)) / shiftWorkSeconds * 100) : 0
45186
+ (() => {
45187
+ const totalShiftSeconds = shiftData.availableTimeSeconds ?? shiftWorkSeconds ?? 0;
45188
+ const idleSeconds = Math.min(
45189
+ Math.max(shiftData.idleTime || 0, 0),
45190
+ totalShiftSeconds
45191
+ );
45192
+ const fallbackUtilization = totalShiftSeconds > 0 ? Math.round((totalShiftSeconds - idleSeconds) / totalShiftSeconds * 100) : 0;
45193
+ return Number.isFinite(shiftData.efficiency) ? Math.round(shiftData.efficiency) : fallbackUtilization;
45194
+ })(),
45195
+ "%"
44224
45196
  ] }),
44225
45197
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
44226
45198
  "Idle Time: ",
@@ -44246,33 +45218,42 @@ var WorkspaceMonthlyHistory = ({
44246
45218
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
44247
45219
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-4", children: [
44248
45220
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-lg shadow-sm border border-gray-100 p-4 flex flex-col justify-between", children: [
44249
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-bold text-gray-700 mb-2", children: isUptimeMode ? "Avg Utilization" : "Avg Efficiency" }),
45221
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-semibold text-gray-600 mb-1", children: isUptimeMode ? "Avg Utilization" : "Avg Efficiency" }),
44250
45222
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 flex-nowrap", children: [
44251
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xl font-bold text-gray-900", children: isUptimeMode ? metrics2?.avgUtilization ?? 0 : `${metrics2?.avgEfficiency ?? 0}%` }),
44252
- !isUptimeMode && /* @__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: [
45223
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-2xl font-bold text-gray-900", children: isUptimeMode ? `${metrics2?.avgUtilization ?? 0}%` : `${metrics2?.avgEfficiency ?? 0}%` }),
45224
+ 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: [
45225
+ utilizationImproved ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "w-3 h-3" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDown, { className: "w-3 h-3" }),
45226
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: utilizationTrendText })
45227
+ ] }) : /* @__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: [
44253
45228
  efficiencyImproved ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "w-3 h-3" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDown, { className: "w-3 h-3" }),
44254
45229
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: `${Math.abs(efficiencyDelta).toFixed(1)}% vs last month` })
44255
45230
  ] })
44256
45231
  ] })
44257
45232
  ] }),
44258
45233
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-lg shadow-sm border border-gray-100 p-4 flex flex-col justify-between", children: [
44259
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-bold text-gray-700 mb-2", children: isUptimeMode ? "Avg Idle Time" : "Avg Daily Output" }),
45234
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-semibold text-gray-600 mb-1", children: isUptimeMode ? "Avg Idle Time" : "Avg Daily Output" }),
44260
45235
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 flex-nowrap", children: [
44261
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xl font-bold text-gray-900", children: isUptimeMode ? formatIdleTime(metrics2?.avgIdleTime ?? 0) : metrics2?.avgDailyOutput?.toLocaleString?.() ?? 0 }),
44262
- !isUptimeMode && /* @__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: [
45236
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-2xl font-bold text-gray-900", children: isUptimeMode ? formatIdleTime(metrics2?.avgIdleTime ?? 0) : metrics2?.avgDailyOutput?.toLocaleString?.() ?? 0 }),
45237
+ isUptimeMode ? /* @__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: [
45238
+ idleImproved ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDown, { className: "w-3 h-3" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "w-3 h-3" }),
45239
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: idleTrendText })
45240
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-1 ${outputImproved ? "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: [
44263
45241
  outputImproved ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "w-3 h-3" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDown, { className: "w-3 h-3" }),
44264
45242
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: `${Math.abs(outputDelta).toFixed(1)}% vs last month` })
44265
45243
  ] })
44266
45244
  ] })
44267
45245
  ] }),
44268
45246
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-lg shadow-sm border border-gray-100 p-4 flex flex-col justify-between", children: [
44269
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-bold text-gray-700 mb-2", children: isUptimeMode ? "Avg Daily Stoppages" : "Avg Cycle Time" }),
45247
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-semibold text-gray-600 mb-1", children: isUptimeMode ? "Avg Daily Stoppages" : "Avg Cycle Time" }),
44270
45248
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 flex-nowrap", children: [
44271
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xl font-bold text-gray-900", children: [
45249
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-2xl font-bold text-gray-900", children: [
44272
45250
  isUptimeMode ? metrics2?.avgDailyStoppages ?? 0 : metrics2?.avgCycleTime ?? 0,
44273
45251
  isUptimeMode ? "" : "s"
44274
45252
  ] }),
44275
- !isUptimeMode && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-1 ${cycleWorsened ? "bg-red-50 text-red-600" : "bg-emerald-50 text-emerald-600"} px-1.5 py-0.5 rounded-full text-[10px] font-medium whitespace-nowrap flex-shrink-0`, children: [
45253
+ 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: [
45254
+ stoppagesImproved ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDown, { className: "w-3 h-3" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "w-3 h-3" }),
45255
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: stoppagesTrendText })
45256
+ ] }) : /* @__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: [
44276
45257
  cycleWorsened ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "w-3 h-3" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDown, { className: "w-3 h-3" }),
44277
45258
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: `${Math.abs(cycleDelta).toFixed(1)}% vs last month` })
44278
45259
  ] })
@@ -44316,8 +45297,8 @@ var WorkspaceMonthlyHistory = ({
44316
45297
  className: "font-bold text-gray-900 leading-none",
44317
45298
  style: { fontSize: "clamp(1rem, 15cqw, 1.5rem)" },
44318
45299
  children: [
44319
- pieChartData[0]?.value,
44320
- isUptimeMode ? "" : "%"
45300
+ pieChartData[0]?.value ?? 0,
45301
+ "%"
44321
45302
  ]
44322
45303
  }
44323
45304
  ),
@@ -44547,7 +45528,8 @@ var WorkspaceWhatsAppShareButton = ({
44547
45528
  const shiftSeconds = shiftMinutes ? shiftMinutes * 60 : 0;
44548
45529
  const idleSeconds = Math.max(workspace.idle_time || 0, 0);
44549
45530
  const clampedIdleSeconds = shiftSeconds > 0 ? Math.min(idleSeconds, shiftSeconds) : idleSeconds;
44550
- const utilization = shiftSeconds > 0 ? Math.max(0, Math.round((shiftSeconds - clampedIdleSeconds) / shiftSeconds * 100)) : 0;
45531
+ const efficiencyValue = Number.isFinite(workspace.avg_efficiency) ? Number(workspace.avg_efficiency) : null;
45532
+ const utilization = efficiencyValue !== null ? Math.max(0, Math.round(efficiencyValue)) : shiftSeconds > 0 ? Math.max(0, Math.round((shiftSeconds - clampedIdleSeconds) / shiftSeconds * 100)) : 0;
44551
45533
  const progressPercent = workspace.target_output > 0 ? workspace.total_actions / workspace.target_output * 100 : 0;
44552
45534
  const message = isUptimeMode ? `*${workspace.workspace_name} Utilization Report*%0A${date} - ${workspace.shift_type}%0A%0A*Utilization:* ${utilization}%0A*Stoppages:* ${workspace.total_actions}%0A*Idle Time:* ${formatIdleTime(clampedIdleSeconds)}%0A*Avg Machine Continuous On Time:* ${workspace.avg_cycle_time.toFixed(1)}s%0A*Rank:* ${workspace.workspace_rank} of ${workspace.total_workspaces}%0A%0AGenerated by Optifye.ai` : `*${workspace.workspace_name} Performance Report*%0A${date} - ${workspace.shift_type}%0A%0A*Progress:* ${progressPercent.toFixed(1)}% of Today's target%0A*Current Output:* ${workspace.total_actions} / ${workspace.target_output}%0A*Efficiency:* ${(workspace.avg_efficiency || 0).toFixed(1)}%25%0A*PPH:* ${workspace.avg_pph.toFixed(1)} (Target: ${workspace.pph_threshold.toFixed(1)})%0A*Cycle Time:* ${workspace.avg_cycle_time.toFixed(1)}s (Standard: ${workspace.ideal_cycle_time.toFixed(1)}s)%0A*Rank:* ${workspace.workspace_rank} of ${workspace.total_workspaces}%0A%0AGenerated by Optifye.ai`;
44553
45535
  window.open(`https://wa.me/?text=${message}`, "_blank");
@@ -44576,7 +45558,8 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
44576
45558
  const shiftSeconds = shiftMinutes ? shiftMinutes * 60 : 0;
44577
45559
  const idleSeconds = Math.max(workspace.idle_time || 0, 0);
44578
45560
  const clampedIdleSeconds = shiftSeconds > 0 ? Math.min(idleSeconds, shiftSeconds) : idleSeconds;
44579
- const utilization = shiftSeconds > 0 ? Math.max(0, Math.round((shiftSeconds - clampedIdleSeconds) / shiftSeconds * 100)) : 0;
45561
+ const computedUtilization = shiftSeconds > 0 ? Math.max(0, Math.round((shiftSeconds - clampedIdleSeconds) / shiftSeconds * 100)) : 0;
45562
+ const utilization = Number.isFinite(workspace.avg_efficiency) ? Math.round(Number(workspace.avg_efficiency)) : computedUtilization;
44580
45563
  const lineName = workspace.line_name || getLineDisplayName(entityConfig, workspace.line_id);
44581
45564
  trackCoreEvent("Workspace PDF Export Clicked", {
44582
45565
  workspace_id: workspace.workspace_id,
@@ -44666,7 +45649,7 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
44666
45649
  doc.setFont("helvetica", "normal");
44667
45650
  doc.text("Utilization:", 25, kpiStartY);
44668
45651
  doc.setFont("helvetica", "bold");
44669
- doc.text(`${utilization}`, 120, kpiStartY);
45652
+ doc.text(`${utilization}%`, 120, kpiStartY);
44670
45653
  createKPIBox(kpiStartY + kpiSpacing);
44671
45654
  doc.setFont("helvetica", "normal");
44672
45655
  doc.text("Total Idle Time:", 25, kpiStartY + kpiSpacing);
@@ -44773,7 +45756,7 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
44773
45756
  const headerY = titleFontSize === 16 ? hourlyPerfStartY + 18 : hourlyPerfStartY + 20;
44774
45757
  const gridTopY = headerY - 5;
44775
45758
  const headerBottomY = gridTopY + 8;
44776
- const colBoundaries = isUptimeMode ? [20, 70, 110, 150, 190] : [20, 70, 100, 130, 155, 190];
45759
+ const colBoundaries = isUptimeMode ? [20, 105, 190] : [20, 70, 100, 130, 155, 190];
44777
45760
  const totalRows = hourlyData.length;
44778
45761
  const gridBottomY = headerBottomY + totalRows * rowHeight;
44779
45762
  const tableHeight = gridBottomY - gridTopY;
@@ -44793,9 +45776,7 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
44793
45776
  const headerTextY = gridTopY + 5.5;
44794
45777
  doc.text("Time Range", 25, headerTextY);
44795
45778
  if (isUptimeMode) {
44796
- doc.text("Active (min)", 80, headerTextY);
44797
- doc.text("Idle (min)", 120, headerTextY);
44798
- doc.text("Utilization", 160, headerTextY);
45779
+ doc.text("Utilization", 147, headerTextY);
44799
45780
  } else {
44800
45781
  doc.text("Output", 75, headerTextY);
44801
45782
  doc.text("Target", 105, headerTextY);
@@ -44842,10 +45823,8 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
44842
45823
  }
44843
45824
  doc.text(timeRange, 25, yPos);
44844
45825
  if (isUptimeMode) {
44845
- doc.text(outputStr, 80, yPos);
44846
- doc.text(targetStr, 120, yPos);
44847
- const utilizationStr = dataCollected ? `${uptimePercent}` : "TBD";
44848
- doc.text(utilizationStr, 160, yPos);
45826
+ const utilizationStr = dataCollected ? `${uptimePercent}%` : "TBD";
45827
+ doc.text(utilizationStr, 147, yPos);
44849
45828
  } else {
44850
45829
  doc.text(outputStr, 75, yPos);
44851
45830
  doc.text(targetStr, 105, yPos);
@@ -44928,7 +45907,7 @@ var WorkspaceMonthlyPdfGenerator = ({
44928
45907
  const shiftWorkSeconds = shiftConfig ? getShiftWorkDurationSeconds(shiftConfig, selectedShiftId) : null;
44929
45908
  const hasShiftData = (shift) => {
44930
45909
  if (shift.hasData !== void 0) return shift.hasData;
44931
- return shift.efficiency > 0 || shift.output > 0 || shift.cycleTime > 0 || shift.pph > 0 || shift.idealOutput > 0 || shift.idleTime > 0;
45910
+ return shift.efficiency > 0 || shift.output > 0 || shift.cycleTime > 0 || shift.pph > 0 || shift.targetOutput > 0 || shift.idleTime > 0;
44932
45911
  };
44933
45912
  const monthBounds = getMonthKeyBounds(selectedYear, selectedMonth);
44934
45913
  const requestedRange = {
@@ -45014,33 +45993,34 @@ var WorkspaceMonthlyPdfGenerator = ({
45014
45993
  return date.getMonth() === selectedMonth && date.getFullYear() === selectedYear;
45015
45994
  });
45016
45995
  const validShifts = validDays.map((day) => getShiftData(day, selectedShiftId)).filter(hasShiftData);
45017
- const monthlyMetrics = validShifts.length > 0 ? isUptimeMode ? (() => {
45018
- const totalIdleTime = validShifts.reduce((sum, shift) => sum + shift.idleTime, 0);
45019
- const totalCycleTime = validShifts.reduce((sum, shift) => sum + shift.cycleTime, 0);
45020
- const totalShiftSeconds = shiftWorkSeconds ? shiftWorkSeconds * validShifts.length : 0;
45021
- const activeSeconds = Math.max(totalShiftSeconds - totalIdleTime, 0);
45022
- const avgUtilization = totalShiftSeconds > 0 ? Math.round(activeSeconds / totalShiftSeconds * 100) : 0;
45023
- const avgIdleTime = Math.round(totalIdleTime / validShifts.length);
45024
- const avgCycleTime = Math.round(totalCycleTime / validShifts.length);
45025
- const underperformingDays = shiftWorkSeconds ? validShifts.filter((shift) => {
45026
- const idleSeconds = Math.min(Math.max(shift.idleTime, 0), shiftWorkSeconds);
45027
- const utilization = Math.round((shiftWorkSeconds - idleSeconds) / shiftWorkSeconds * 100);
45028
- return utilization < effectiveLegend.green_min;
45029
- }).length : 0;
45996
+ const filteredShifts = isUptimeMode ? validShifts : validShifts.filter((shift) => (shift.efficiency ?? 0) >= 5);
45997
+ const monthlyMetrics = filteredShifts.length > 0 ? isUptimeMode ? (() => {
45998
+ const totalIdleTime = filteredShifts.reduce((sum, shift) => sum + shift.idleTime, 0);
45999
+ const totalCycleTime = filteredShifts.reduce((sum, shift) => sum + shift.cycleTime, 0);
46000
+ const totalUtilization = filteredShifts.reduce(
46001
+ (sum, shift) => sum + getUptimeUtilizationPercent(shift),
46002
+ 0
46003
+ );
46004
+ const totalOutput = filteredShifts.reduce((sum, shift) => sum + (shift.output || 0), 0);
46005
+ const avgUtilization = Math.round(totalUtilization / filteredShifts.length);
46006
+ const avgIdleTime = Math.round(totalIdleTime / filteredShifts.length);
46007
+ const avgCycleTime = Math.round(totalCycleTime / filteredShifts.length);
46008
+ const underperformingDays = filteredShifts.filter((shift) => (shift.efficiency || 0) < effectiveLegend.green_min).length;
45030
46009
  return {
45031
46010
  avgUtilization,
45032
46011
  avgIdleTime,
45033
46012
  avgCycleTime,
45034
- totalDays: validShifts.length,
46013
+ avgDailyStoppages: Math.round(totalOutput / filteredShifts.length),
46014
+ totalDays: filteredShifts.length,
45035
46015
  underperformingDays
45036
46016
  };
45037
46017
  })() : {
45038
- avgEfficiency: validShifts.reduce((sum, shift) => sum + shift.efficiency, 0) / validShifts.length,
45039
- avgOutput: validShifts.reduce((sum, shift) => sum + shift.output, 0) / validShifts.length,
45040
- avgCycleTime: validShifts.reduce((sum, shift) => sum + shift.cycleTime, 0) / validShifts.length,
45041
- avgPph: validShifts.reduce((sum, shift) => sum + shift.pph, 0) / validShifts.length,
45042
- totalDays: validShifts.length,
45043
- underperformingDays: validShifts.filter((shift) => shift.efficiency < effectiveLegend.green_min).length
46018
+ avgEfficiency: filteredShifts.reduce((sum, shift) => sum + shift.efficiency, 0) / filteredShifts.length,
46019
+ avgOutput: filteredShifts.reduce((sum, shift) => sum + shift.output, 0) / filteredShifts.length,
46020
+ avgCycleTime: filteredShifts.reduce((sum, shift) => sum + shift.cycleTime, 0) / filteredShifts.length,
46021
+ avgPph: filteredShifts.reduce((sum, shift) => sum + shift.pph, 0) / filteredShifts.length,
46022
+ totalDays: filteredShifts.length,
46023
+ underperformingDays: filteredShifts.filter((shift) => shift.efficiency < effectiveLegend.green_min).length
45044
46024
  } : null;
45045
46025
  const createKPIBox = (y) => {
45046
46026
  doc.setFillColor(255, 255, 255);
@@ -45066,7 +46046,7 @@ var WorkspaceMonthlyPdfGenerator = ({
45066
46046
  doc.setFont("helvetica", "normal");
45067
46047
  doc.text("Average Utilization:", 25, kpiStartY);
45068
46048
  doc.setFont("helvetica", "bold");
45069
- doc.text(`${uptimeMetrics.avgUtilization} (Target: ${Math.round(effectiveLegend.green_min)})`, 120, kpiStartY);
46049
+ doc.text(`${uptimeMetrics.avgUtilization}%`, 120, kpiStartY);
45070
46050
  createKPIBox(kpiStartY + kpiSpacing);
45071
46051
  doc.setFont("helvetica", "normal");
45072
46052
  doc.text("Average Idle Time:", 25, kpiStartY + kpiSpacing);
@@ -45076,7 +46056,7 @@ var WorkspaceMonthlyPdfGenerator = ({
45076
46056
  doc.setFont("helvetica", "normal");
45077
46057
  doc.text("Avg Daily Stoppages:", 25, kpiStartY + kpiSpacing * 2);
45078
46058
  doc.setFont("helvetica", "bold");
45079
- doc.text(`0`, 120, kpiStartY + kpiSpacing * 2);
46059
+ doc.text(`${uptimeMetrics.avgDailyStoppages}`, 120, kpiStartY + kpiSpacing * 2);
45080
46060
  createKPIBox(kpiStartY + kpiSpacing * 3);
45081
46061
  doc.setFont("helvetica", "normal");
45082
46062
  doc.text("Working Days:", 25, kpiStartY + kpiSpacing * 3);
@@ -45168,10 +46148,10 @@ var WorkspaceMonthlyPdfGenerator = ({
45168
46148
  const idleSeconds = Math.max(shift.idleTime || 0, 0);
45169
46149
  const clampedIdleSeconds = totalShiftSeconds > 0 ? Math.min(idleSeconds, totalShiftSeconds) : idleSeconds;
45170
46150
  const productiveSeconds = totalShiftSeconds > 0 ? Math.max(totalShiftSeconds - clampedIdleSeconds, 0) : 0;
45171
- const utilization = totalShiftSeconds > 0 ? Math.round(productiveSeconds / totalShiftSeconds * 100) : 0;
46151
+ const utilization = Number.isFinite(shift.efficiency) ? Math.round(Number(shift.efficiency)) : 0;
45172
46152
  doc.text(formatIdleTime(productiveSeconds), 60, yPos);
45173
46153
  doc.text(formatIdleTime(clampedIdleSeconds), 95, yPos);
45174
- doc.text(`${utilization}`, 135, yPos);
46154
+ doc.text(`${utilization}%`, 135, yPos);
45175
46155
  if (utilization >= effectiveLegend.green_min) {
45176
46156
  doc.setTextColor(0, 171, 69);
45177
46157
  doc.text("\u2713", 170, yPos);
@@ -45182,7 +46162,7 @@ var WorkspaceMonthlyPdfGenerator = ({
45182
46162
  doc.setTextColor(0, 0, 0);
45183
46163
  } else {
45184
46164
  doc.text(`${shift.output}`, 60, yPos);
45185
- doc.text(`${shift.idealOutput}`, 95, yPos);
46165
+ doc.text(`${shift.targetOutput}`, 95, yPos);
45186
46166
  doc.text(`${shift.efficiency.toFixed(1)}%`, 135, yPos);
45187
46167
  if (shift.efficiency >= effectiveLegend.green_min) {
45188
46168
  doc.setTextColor(0, 171, 69);
@@ -45236,14 +46216,18 @@ var UptimeMetricCards = ({
45236
46216
  uptimePieData = [],
45237
46217
  className = ""
45238
46218
  }) => {
46219
+ const efficiencyValue = Number.isFinite(workspace.avg_efficiency) ? Math.max(0, Math.min(100, Number(workspace.avg_efficiency))) : null;
45239
46220
  const total = uptimePieData.reduce((sum, item) => sum + item.value, 0);
45240
46221
  const active = uptimePieData.find((item) => item.name === "Productive")?.value || 0;
45241
- const uptimePercent = total > 0 ? (active / total * 100).toFixed(1) : "0.0";
46222
+ const uptimePercent = efficiencyValue !== null ? efficiencyValue.toFixed(1) : total > 0 ? (active / total * 100).toFixed(1) : "0.0";
45242
46223
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `grid grid-cols-1 gap-4 sm:gap-3 sm:grid-cols-3 w-full h-full ${className}`, children: [
45243
46224
  /* @__PURE__ */ jsxRuntime.jsxs(Card2, { className: "flex flex-col bg-white shadow-sm h-full min-h-[150px] sm:min-h-0", children: [
45244
46225
  /* @__PURE__ */ jsxRuntime.jsx(CardHeader2, { className: "pb-2 flex-none", children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle2, { className: "text-lg text-center", children: "Utilization" }) }),
45245
46226
  /* @__PURE__ */ jsxRuntime.jsx(CardContent2, { className: "flex-1 flex items-center justify-center py-6 sm:py-3", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
45246
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-5xl font-bold text-gray-900", children: uptimePercent }),
46227
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-5xl font-bold text-gray-900", children: [
46228
+ uptimePercent,
46229
+ "%"
46230
+ ] }),
45247
46231
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500 mt-2", children: "Total productive time" })
45248
46232
  ] }) })
45249
46233
  ] }),
@@ -46035,7 +47019,9 @@ var KPISection = React26.memo(({
46035
47019
  gridGap = "md",
46036
47020
  cardVariant = "default",
46037
47021
  compactCards = false,
46038
- useSrcLayout = false
47022
+ useSrcLayout = false,
47023
+ mode = "output",
47024
+ averageIdleTimeSeconds = null
46039
47025
  }) => {
46040
47026
  const showSkeleton = isLoading || !kpis;
46041
47027
  const outputDifference = kpis ? kpis.outputProgress.current - kpis.outputProgress.idealOutput : 0;
@@ -46043,6 +47029,10 @@ var KPISection = React26.memo(({
46043
47029
  if (useSrcLayout) {
46044
47030
  const effChange = showSkeleton ? 0 : kpis.efficiency.change ?? 0;
46045
47031
  const effTrend = effChange > 0 ? "up" : effChange < 0 ? "down" : "neutral";
47032
+ const isUptimeMode = mode === "uptime";
47033
+ const efficiencyTitle = isUptimeMode ? "Utilization %" : "Efficiency";
47034
+ const secondaryTitle = isUptimeMode ? "Average Idle Time" : "Output Progress";
47035
+ const secondaryValue = isUptimeMode ? showSkeleton ? "" : formatIdleTime(averageIdleTimeSeconds ?? 0) : showSkeleton ? "" : `${kpis.outputProgress.current}/${kpis.outputProgress.target}`;
46046
47036
  return /* @__PURE__ */ jsxRuntime.jsxs(
46047
47037
  "div",
46048
47038
  {
@@ -46056,7 +47046,7 @@ var KPISection = React26.memo(({
46056
47046
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(
46057
47047
  KPICard,
46058
47048
  {
46059
- title: "Efficiency",
47049
+ title: efficiencyTitle,
46060
47050
  value: showSkeleton ? "" : kpis.efficiency.value,
46061
47051
  change: effChange,
46062
47052
  trend: effTrend,
@@ -46070,10 +47060,10 @@ var KPISection = React26.memo(({
46070
47060
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(
46071
47061
  KPICard,
46072
47062
  {
46073
- title: "Output Progress",
46074
- value: showSkeleton ? "" : `${kpis.outputProgress.current}/${kpis.outputProgress.target}`,
47063
+ title: secondaryTitle,
47064
+ value: secondaryValue,
46075
47065
  outputDifference,
46076
- showOutputDetails: !showSkeleton,
47066
+ showOutputDetails: !showSkeleton && !isUptimeMode,
46077
47067
  isLoading: showSkeleton
46078
47068
  }
46079
47069
  ) })
@@ -46145,7 +47135,7 @@ var KPISection = React26.memo(({
46145
47135
  if (prevKpis.underperformingWorkers.change !== nextKpis.underperformingWorkers.change) return false;
46146
47136
  if (prevKpis.outputProgress.current !== nextKpis.outputProgress.current || prevKpis.outputProgress.target !== nextKpis.outputProgress.target) return false;
46147
47137
  if (prevKpis.outputProgress.change !== nextKpis.outputProgress.change) return false;
46148
- if (prevProps.layout !== nextProps.layout || prevProps.cardVariant !== nextProps.cardVariant || prevProps.compactCards !== nextProps.compactCards || prevProps.useSrcLayout !== nextProps.useSrcLayout || prevProps.className !== nextProps.className) return false;
47138
+ if (prevProps.layout !== nextProps.layout || prevProps.cardVariant !== nextProps.cardVariant || prevProps.compactCards !== nextProps.compactCards || prevProps.useSrcLayout !== nextProps.useSrcLayout || prevProps.className !== nextProps.className || prevProps.mode !== nextProps.mode || prevProps.averageIdleTimeSeconds !== nextProps.averageIdleTimeSeconds) return false;
46149
47139
  return true;
46150
47140
  });
46151
47141
  KPISection.displayName = "KPISection";
@@ -54010,6 +55000,20 @@ function HomeView({
54010
55000
  }
54011
55001
  };
54012
55002
  }, [kpis, kpiTrend]);
55003
+ const selectedLineMeta = React26.useMemo(
55004
+ () => dbLines.find((line) => line.id === selectedLineId),
55005
+ [dbLines, selectedLineId]
55006
+ );
55007
+ const selectedMonitoringMode = selectedLineId === factoryViewId ? "output" : selectedLineMeta?.monitoring_mode ?? "output";
55008
+ const isUptimeMode = selectedMonitoringMode === "uptime";
55009
+ const averageIdleTimeSeconds = React26.useMemo(() => {
55010
+ if (!isUptimeMode) return null;
55011
+ const targetWorkspaces = selectedLineId === factoryViewId ? workspaceMetrics : workspaceMetrics.filter((ws) => ws.line_id === selectedLineId);
55012
+ const idleValues = targetWorkspaces.map((ws) => ws.idle_time).filter((value) => Number.isFinite(value));
55013
+ if (idleValues.length === 0) return 0;
55014
+ const totalIdle = idleValues.reduce((sum, value) => sum + value, 0);
55015
+ return totalIdle / idleValues.length;
55016
+ }, [isUptimeMode, selectedLineId, factoryViewId, workspaceMetrics]);
54013
55017
  const {
54014
55018
  activeBreaks: allActiveBreaks,
54015
55019
  isLoading: breaksLoading,
@@ -54452,10 +55456,12 @@ function HomeView({
54452
55456
  kpis: displayKpis,
54453
55457
  isLoading: shouldShowKpiLoading,
54454
55458
  className: "w-full sm:w-auto",
54455
- useSrcLayout: true
55459
+ useSrcLayout: true,
55460
+ mode: isUptimeMode ? "uptime" : "output",
55461
+ averageIdleTimeSeconds
54456
55462
  },
54457
55463
  "kpi-section-control"
54458
- ), [displayKpis, shouldShowKpiLoading]);
55464
+ ), [displayKpis, shouldShowKpiLoading, isUptimeMode, averageIdleTimeSeconds]);
54459
55465
  if (isInitialLoading) {
54460
55466
  return /* @__PURE__ */ jsxRuntime.jsx(LoadingPageCmp, { message: "Loading Dashboard..." });
54461
55467
  }
@@ -55375,6 +56381,56 @@ var MetricCards = React26.memo(({
55375
56381
  return prevMetrics.current_output === nextMetrics.current_output && prevMetrics.line_threshold === nextMetrics.line_threshold && prevMetrics.underperforming_workspaces === nextMetrics.underperforming_workspaces && prevMetrics.total_workspaces === nextMetrics.total_workspaces && prevMetrics.avg_efficiency === nextMetrics.avg_efficiency;
55376
56382
  });
55377
56383
  MetricCards.displayName = "MetricCards";
56384
+ var LineUptimeMetricCards = React26.memo(({
56385
+ utilizationPercent,
56386
+ stoppages,
56387
+ idleTimeSeconds,
56388
+ idleTimeData,
56389
+ showIdleTime
56390
+ }) => {
56391
+ const utilizationText = Number.isFinite(utilizationPercent) ? utilizationPercent.toFixed(1) : "0.0";
56392
+ return /* @__PURE__ */ jsxRuntime.jsxs(
56393
+ motion.div,
56394
+ {
56395
+ variants: containerVariants,
56396
+ initial: "initial",
56397
+ animate: "animate",
56398
+ className: `grid grid-cols-1 sm:grid-cols-2 ${showIdleTime ? "lg:grid-cols-4" : "lg:grid-cols-3"} gap-3 sm:gap-4 mb-2 md:h-[35vh] h-auto`,
56399
+ children: [
56400
+ /* @__PURE__ */ jsxRuntime.jsxs(motion.div, { variants: itemVariants, className: "bg-white rounded-xl shadow-sm p-3 sm:p-4 overflow-hidden h-[240px] sm:h-[260px] md:h-auto", children: [
56401
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-sm sm:text-base font-bold text-gray-700 mb-2 text-center", children: "Utilization" }),
56402
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[calc(100%-2.5rem)]", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(
56403
+ OutputProgressChart,
56404
+ {
56405
+ currentOutput: Number(utilizationText),
56406
+ targetOutput: 100
56407
+ }
56408
+ ) }) })
56409
+ ] }),
56410
+ /* @__PURE__ */ jsxRuntime.jsxs(motion.div, { variants: itemVariants, className: "bg-white rounded-xl shadow-sm p-3 sm:p-4 overflow-hidden h-[240px] sm:h-[260px] md:h-auto", children: [
56411
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-sm sm:text-base font-bold text-gray-700 text-center mb-2", children: "Stoppages" }),
56412
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center h-[calc(100%-2.5rem)]", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-4xl sm:text-5xl md:text-6xl lg:text-7xl font-bold text-red-600", children: stoppages }) })
56413
+ ] }),
56414
+ /* @__PURE__ */ jsxRuntime.jsxs(motion.div, { variants: itemVariants, className: "bg-white rounded-xl shadow-sm p-3 sm:p-4 overflow-hidden h-[240px] sm:h-[260px] md:h-auto", children: [
56415
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-sm sm:text-base font-bold text-gray-700 text-center mb-2", children: "Average Idle Time" }),
56416
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center h-[calc(100%-2.5rem)]", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: `text-4xl sm:text-5xl md:text-6xl lg:text-7xl font-bold ${idleTimeSeconds <= 0 ? "text-green-500" : idleTimeSeconds <= 300 ? "text-yellow-500" : "text-red-500"}`, children: formatIdleTime(idleTimeSeconds) }) })
56417
+ ] }),
56418
+ showIdleTime && /* @__PURE__ */ jsxRuntime.jsxs(motion.div, { variants: itemVariants, className: "bg-white rounded-xl shadow-sm p-3 sm:p-4 overflow-hidden h-[240px] sm:h-[260px] md:h-auto", children: [
56419
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-sm sm:text-base font-bold text-gray-700 text-center mb-2", children: "Idle Time Breakdown" }),
56420
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[calc(100%-2.5rem)]", children: /* @__PURE__ */ jsxRuntime.jsx(
56421
+ IdleTimeReasonChart,
56422
+ {
56423
+ data: idleTimeData?.chartData,
56424
+ isLoading: idleTimeData?.isLoading,
56425
+ error: idleTimeData?.error
56426
+ }
56427
+ ) })
56428
+ ] })
56429
+ ]
56430
+ }
56431
+ );
56432
+ });
56433
+ LineUptimeMetricCards.displayName = "LineUptimeMetricCards";
55378
56434
  var BottomSection = React26.memo(({
55379
56435
  lineInfo,
55380
56436
  lineId,
@@ -55536,6 +56592,116 @@ var BottomSection = React26.memo(({
55536
56592
  return true;
55537
56593
  });
55538
56594
  BottomSection.displayName = "BottomSection";
56595
+ var UptimeBottomSection = React26.memo(({
56596
+ lineId,
56597
+ workspaceData,
56598
+ sortedByUtilization,
56599
+ workspaceDisplayNames,
56600
+ idleTimeData,
56601
+ showIdleTime,
56602
+ idleTimeHourly,
56603
+ hourlyAggregates,
56604
+ shiftStart,
56605
+ shiftEnd,
56606
+ shiftDate,
56607
+ timezone,
56608
+ elapsedMinutes,
56609
+ urlDate,
56610
+ urlShift,
56611
+ navigate
56612
+ }) => {
56613
+ const navigation = useNavigation();
56614
+ const handleNavigate = navigate || navigation.navigate;
56615
+ const handleWorkspaceClick = React26.useCallback((ws, index) => {
56616
+ trackCoreEvent("Workspace from KPI Clicked", {
56617
+ workspace_name: ws.workspace_name,
56618
+ workspace_id: ws.workspace_uuid,
56619
+ rank: index + 1,
56620
+ total_workspaces: workspaceData.length,
56621
+ efficiency: ws.utilization,
56622
+ action_count: ws.action_count,
56623
+ action_threshold: ws.action_threshold,
56624
+ section: "Lowest Utilization Workspaces"
56625
+ });
56626
+ }, [workspaceData.length]);
56627
+ return /* @__PURE__ */ jsxRuntime.jsxs(
56628
+ motion.div,
56629
+ {
56630
+ variants: containerVariants,
56631
+ initial: "initial",
56632
+ animate: "animate",
56633
+ className: "grid grid-cols-1 lg:grid-cols-5 gap-3 sm:gap-4 md:h-[calc(57vh-4rem)] h-auto md:mt-0 mt-4",
56634
+ children: [
56635
+ /* @__PURE__ */ jsxRuntime.jsxs(motion.div, { variants: itemVariants, className: "lg:col-span-2 bg-white rounded-xl shadow-sm p-3 sm:p-4 flex flex-col overflow-hidden h-[350px] sm:h-[400px] md:h-auto", children: [
56636
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center mb-2", children: [
56637
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-base sm:text-lg font-bold text-gray-700 text-center flex-1", children: "Lowest Utilization Workspaces" }),
56638
+ /* @__PURE__ */ jsxRuntime.jsx(
56639
+ "div",
56640
+ {
56641
+ className: "p-1.5 hover:bg-gray-100 rounded-lg transition-colors cursor-pointer",
56642
+ onClick: () => handleNavigate && handleNavigate(`/leaderboard`),
56643
+ children: /* @__PURE__ */ jsxRuntime.jsx(outline.ArrowRightIcon, { className: "w-4 h-4 sm:w-5 sm:h-5 text-gray-500" })
56644
+ }
56645
+ )
56646
+ ] }),
56647
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "divide-y overflow-auto flex-1 pr-1 sm:pr-2", children: [
56648
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between pb-2", children: [
56649
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 sm:gap-3 md:gap-6", children: [
56650
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-medium text-gray-500 min-w-[80px] sm:min-w-[100px] md:min-w-[120px] text-xs sm:text-sm md:text-base", children: "Workspace" }),
56651
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs md:text-sm font-medium text-gray-500", children: "Stoppages" })
56652
+ ] }),
56653
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs md:text-sm font-medium text-gray-500 pr-1 sm:pr-2", children: "Utilization" })
56654
+ ] }),
56655
+ sortedByUtilization.map((ws, index) => {
56656
+ const displayName = workspaceDisplayNames && workspaceDisplayNames[ws.workspace_name] || getWorkspaceDisplayName(ws.workspace_name, lineId);
56657
+ const navParams = ws.workspace_uuid ? getWorkspaceNavigationParams(ws.workspace_uuid, displayName, lineId) : "";
56658
+ const dateShiftParams = urlDate ? `&date=${urlDate}&shift=${urlShift || "0"}` : "";
56659
+ const returnToParam = `&returnTo=${encodeURIComponent(`/kpis/${lineId}`)}`;
56660
+ const fullUrl = `/workspace/${ws.workspace_uuid}${navParams}${dateShiftParams}${returnToParam}`;
56661
+ return /* @__PURE__ */ jsxRuntime.jsx(
56662
+ "div",
56663
+ {
56664
+ onClick: () => {
56665
+ handleWorkspaceClick(ws, index);
56666
+ handleNavigate && handleNavigate(fullUrl);
56667
+ },
56668
+ className: "block py-2 sm:py-3 hover:bg-gray-50 transition-colors rounded-lg cursor-pointer",
56669
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
56670
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 sm:gap-3 md:gap-6", children: [
56671
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-[80px] sm:min-w-[100px] md:min-w-[120px]", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-medium text-gray-900 text-xs sm:text-sm md:text-base truncate", children: displayName }) }),
56672
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs md:text-sm font-medium text-gray-900", children: ws.action_count || 0 })
56673
+ ] }),
56674
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs md:text-sm font-medium text-gray-900", children: [
56675
+ ws.utilization.toFixed(1),
56676
+ "%"
56677
+ ] })
56678
+ ] })
56679
+ },
56680
+ ws.workspace_uuid || `${ws.workspace_name}-${index}`
56681
+ );
56682
+ })
56683
+ ] })
56684
+ ] }),
56685
+ /* @__PURE__ */ jsxRuntime.jsxs(motion.div, { variants: itemVariants, className: "lg:col-span-3 bg-white rounded-xl shadow-sm p-3 sm:p-4 flex flex-col overflow-hidden h-[350px] sm:h-[400px] md:h-auto", children: [
56686
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-base sm:text-lg font-bold text-gray-700 mb-2 text-center", children: "Hourly Utilization" }),
56687
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-[280px] sm:min-h-[300px] md:min-h-[400px]", children: /* @__PURE__ */ jsxRuntime.jsx(
56688
+ HourlyUptimeChart,
56689
+ {
56690
+ hourlyAggregates,
56691
+ idleTimeHourly,
56692
+ shiftStart,
56693
+ shiftEnd,
56694
+ shiftDate,
56695
+ timezone,
56696
+ elapsedMinutes
56697
+ }
56698
+ ) })
56699
+ ] })
56700
+ ]
56701
+ }
56702
+ );
56703
+ });
56704
+ UptimeBottomSection.displayName = "UptimeBottomSection";
55539
56705
  var QualityOverview = React26.memo(({ lineInfo }) => {
55540
56706
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center h-full p-6 bg-white rounded-lg shadow-sm", children: [
55541
56707
  /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-xl font-semibold text-gray-700 mb-4", children: "Quality Overview" }),
@@ -55647,7 +56813,11 @@ var KPIDetailView = ({
55647
56813
  React26.useMemo(() => getCurrentTimeInZone(configuredTimezone), [configuredTimezone]);
55648
56814
  const { legend: efficiencyLegend } = useEfficiencyLegend(companyId);
55649
56815
  const supervisorEnabled = dashboardConfig?.supervisorConfig?.enabled || false;
55650
- const { supervisorName, supervisors } = useLineSupervisor(lineId);
56816
+ const { supervisorName, supervisors } = useLineSupervisor(lineId, {
56817
+ enabled: supervisorEnabled,
56818
+ companyId: resolvedCompanyId,
56819
+ useBackend: true
56820
+ });
55651
56821
  const { displayNames: workspaceDisplayNames } = useWorkspaceDisplayNames(lineId, companyId);
55652
56822
  const { isIdleTimeVlmEnabled } = useIdleTimeVlmConfig();
55653
56823
  const idleTimeVlmEnabled = isIdleTimeVlmEnabled(lineId);
@@ -55675,6 +56845,25 @@ var KPIDetailView = ({
55675
56845
  }
55676
56846
  return /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" }) });
55677
56847
  }, []);
56848
+ const resolveShiftTimes = React26.useCallback((shiftId) => {
56849
+ if (!shiftConfig) return { start: void 0, end: void 0 };
56850
+ const targetShiftId = shiftId ?? 0;
56851
+ if (shiftConfig.shifts && shiftConfig.shifts.length > 0) {
56852
+ const shift = shiftConfig.shifts.find((s) => s.shiftId === targetShiftId);
56853
+ if (shift) {
56854
+ return { start: shift.startTime, end: shift.endTime };
56855
+ }
56856
+ }
56857
+ shiftConfig.dayShift?.id ?? 0;
56858
+ const nightShiftId = shiftConfig.nightShift?.id ?? 1;
56859
+ if (targetShiftId === nightShiftId && shiftConfig.nightShift) {
56860
+ return { start: shiftConfig.nightShift.startTime, end: shiftConfig.nightShift.endTime };
56861
+ }
56862
+ if (shiftConfig.dayShift) {
56863
+ return { start: shiftConfig.dayShift.startTime, end: shiftConfig.dayShift.endTime };
56864
+ }
56865
+ return { start: void 0, end: void 0 };
56866
+ }, [shiftConfig]);
55678
56867
  const getDaysDifference2 = React26.useCallback((date) => {
55679
56868
  const compareDate = new Date(date);
55680
56869
  const shiftStartTime = shiftConfig?.dayShift?.startTime || shiftConfig?.shifts?.[0]?.startTime || "06:00";
@@ -55704,6 +56893,9 @@ var KPIDetailView = ({
55704
56893
  shiftId: parsedShiftId,
55705
56894
  enabled: !isShiftConfigLoading && (!historicalRouteRequested || historicalParamsReady)
55706
56895
  });
56896
+ const resolvedMonitoringMode = lineDetails?.monitoring_mode ?? "output";
56897
+ const isUptimeMode = resolvedMonitoringMode === "uptime";
56898
+ const overviewTabLabel = isUptimeMode ? "Utilization" : "Efficiency";
55707
56899
  const {
55708
56900
  workspaces,
55709
56901
  loading: workspacesLoading,
@@ -55743,7 +56935,11 @@ var KPIDetailView = ({
55743
56935
  }
55744
56936
  setMonthlyDataLoading(true);
55745
56937
  Promise.all([
55746
- dashboardService.getLineMonthlyData(lineId, currentMonth, currentYear),
56938
+ dashboardService.getLineMonthlyData(lineId, currentMonth, currentYear, {
56939
+ startDate: isFullRange ? void 0 : range.startKey,
56940
+ endDate: isFullRange ? void 0 : range.endKey,
56941
+ shiftIds: shiftConfig?.shifts?.map((s) => s.shiftId)
56942
+ }),
55747
56943
  dashboardService.getUnderperformingWorkspaces(
55748
56944
  lineId,
55749
56945
  currentMonth,
@@ -55756,6 +56952,7 @@ var KPIDetailView = ({
55756
56952
  ]).then(([monthlyMetrics, underperformingData]) => {
55757
56953
  console.log("Fetched monthly metrics data:", monthlyMetrics);
55758
56954
  const dayDataMap = /* @__PURE__ */ new Map();
56955
+ const resolveMonthlyShiftTimes = (metricShiftId) => resolveShiftTimes(metricShiftId);
55759
56956
  monthlyMetrics.forEach((metric) => {
55760
56957
  const date = new Date(metric.date);
55761
56958
  const dateKey = date.toISOString().split("T")[0];
@@ -55768,15 +56965,40 @@ var KPIDetailView = ({
55768
56965
  };
55769
56966
  dayDataMap.set(dateKey, dayData);
55770
56967
  }
56968
+ const fallbackTimes = resolveMonthlyShiftTimes(metric.shift_id);
56969
+ const resolvedShiftStart = metric.shift_start || fallbackTimes.start;
56970
+ const resolvedShiftEnd = metric.shift_end || fallbackTimes.end;
56971
+ const hasBackendUptimeSeconds = isUptimeMode && (Number.isFinite(metric.active_time_seconds) || Number.isFinite(metric.idle_time_seconds) || Number.isFinite(metric.available_time_seconds));
56972
+ const uptimeSeries2 = isUptimeMode && !hasBackendUptimeSeconds ? buildUptimeSeries({
56973
+ idleTimeHourly: metric.idle_time_hourly || {},
56974
+ shiftStart: resolvedShiftStart || void 0,
56975
+ shiftEnd: resolvedShiftEnd || void 0,
56976
+ shiftDate: metric.date,
56977
+ timezone: configuredTimezone
56978
+ }) : null;
56979
+ const idleTimeSeconds = isUptimeMode ? hasBackendUptimeSeconds ? metric.idle_time_seconds || 0 : (uptimeSeries2?.idleMinutes || 0) * 60 : 0;
56980
+ const activeTimeSeconds = isUptimeMode ? hasBackendUptimeSeconds ? metric.active_time_seconds || 0 : (uptimeSeries2?.activeMinutes || 0) * 60 : 0;
56981
+ const availableTimeSeconds = isUptimeMode ? hasBackendUptimeSeconds ? metric.available_time_seconds || activeTimeSeconds + idleTimeSeconds : (uptimeSeries2?.availableMinutes || 0) * 60 : 0;
56982
+ const computedUptimeEfficiency = (() => {
56983
+ const availableMinutes = availableTimeSeconds / 60;
56984
+ if (!availableMinutes || availableMinutes <= 0) return 0;
56985
+ const idleMinutes = Math.min(Math.max(idleTimeSeconds / 60, 0), availableMinutes);
56986
+ const productiveMinutes = Math.max(activeTimeSeconds / 60 || availableMinutes - idleMinutes, 0);
56987
+ return Math.round(productiveMinutes / availableMinutes * 100);
56988
+ })();
55771
56989
  const shiftData = {
55772
- avg_efficiency: metric.avg_efficiency || 0,
56990
+ avg_efficiency: isUptimeMode ? Number.isFinite(metric.avg_efficiency) ? Number(metric.avg_efficiency) : computedUptimeEfficiency : metric.avg_efficiency || 0,
55773
56991
  underperforming_workspaces: metric.underperforming_workspaces || 0,
55774
56992
  total_workspaces: metric.total_workspaces || 0,
55775
56993
  output: metric.current_output || 0,
55776
- idealOutput: metric.ideal_output || metric.line_threshold || 0,
56994
+ targetOutput: Number(metric.line_threshold || 0),
55777
56995
  compliance_percentage: 95 + Math.random() * 5,
55778
56996
  // Mock data: random value between 95-100%
55779
- hasData: true
56997
+ hasData: isUptimeMode ? availableTimeSeconds > 0 : true,
56998
+ idle_time_seconds: idleTimeSeconds,
56999
+ active_time_seconds: activeTimeSeconds,
57000
+ available_time_seconds: availableTimeSeconds,
57001
+ avg_idle_time_seconds: isUptimeMode ? metric.avg_idle_time_seconds || 0 : 0
55780
57002
  };
55781
57003
  dayData.shifts[metric.shift_id] = shiftData;
55782
57004
  });
@@ -55815,7 +57037,7 @@ var KPIDetailView = ({
55815
57037
  setMonthlyDataLoading(false);
55816
57038
  });
55817
57039
  }
55818
- }, [activeTab, lineId, currentMonth, currentYear, supabase, dashboardConfig, shiftConfig, range.startKey, range.endKey, isFullRange]);
57040
+ }, [activeTab, lineId, currentMonth, currentYear, supabase, dashboardConfig, shiftConfig, range.startKey, range.endKey, isFullRange, configuredTimezone, isUptimeMode, resolveShiftTimes]);
55819
57041
  const analysisMonthlyData = React26.useMemo(() => {
55820
57042
  return filterDataByDateKeyRange(monthlyData, range);
55821
57043
  }, [monthlyData, range]);
@@ -55832,6 +57054,7 @@ var KPIDetailView = ({
55832
57054
  factory_name: lineDetails.factory.factory_name,
55833
57055
  shift_id: metrics2.shift_id ?? 0,
55834
57056
  date: metrics2.date || getOperationalDate(timezone || "UTC"),
57057
+ monitoring_mode: lineDetails.monitoring_mode ?? void 0,
55835
57058
  metrics: {
55836
57059
  avg_efficiency: metrics2.avg_efficiency ?? 0,
55837
57060
  avg_cycle_time: metrics2.avg_cycle_time ?? 0,
@@ -55848,7 +57071,8 @@ var KPIDetailView = ({
55848
57071
  shift_start: metrics2.shift_start || "06:00",
55849
57072
  shift_end: metrics2.shift_end || "14:00",
55850
57073
  last_updated: metrics2.last_updated || (/* @__PURE__ */ new Date()).toISOString(),
55851
- poorest_performing_workspaces: metrics2.poorest_performing_workspaces || []
57074
+ poorest_performing_workspaces: metrics2.poorest_performing_workspaces || [],
57075
+ idle_time_hourly: metrics2.idle_time_hourly || null
55852
57076
  }
55853
57077
  };
55854
57078
  }, [metrics2, lineDetails, companyId]);
@@ -55893,6 +57117,188 @@ var KPIDetailView = ({
55893
57117
  date: resolvedLineInfo.date
55894
57118
  };
55895
57119
  }, [metrics2, metricsMatchRequest, resolvedLineInfo]);
57120
+ const resolvedShiftTimes = React26.useMemo(() => {
57121
+ if (!chartMetrics) return { start: void 0, end: void 0 };
57122
+ const fallback = resolveShiftTimes(chartMetrics.shift_id);
57123
+ return {
57124
+ start: chartMetrics.shift_start || fallback.start,
57125
+ end: chartMetrics.shift_end || fallback.end
57126
+ };
57127
+ }, [chartMetrics?.shift_start, chartMetrics?.shift_end, chartMetrics?.shift_id, resolveShiftTimes]);
57128
+ const currentShiftDetails = React26.useMemo(() => {
57129
+ if (!shiftConfig) return null;
57130
+ return getCurrentShift(configuredTimezone, shiftConfig);
57131
+ }, [configuredTimezone, shiftConfig]);
57132
+ const isCurrentShiftView = React26.useMemo(() => {
57133
+ if (!currentShiftDetails || !chartMetrics?.date || chartMetrics?.shift_id === void 0 || chartMetrics?.shift_id === null) {
57134
+ return false;
57135
+ }
57136
+ return currentShiftDetails.date === chartMetrics.date && currentShiftDetails.shiftId === chartMetrics.shift_id;
57137
+ }, [currentShiftDetails, chartMetrics?.date, chartMetrics?.shift_id]);
57138
+ const elapsedShiftMinutes = React26.useMemo(() => {
57139
+ if (!isCurrentShiftView || !chartMetrics) return null;
57140
+ return getShiftElapsedMinutes({
57141
+ shiftStart: resolvedShiftTimes.start,
57142
+ shiftEnd: resolvedShiftTimes.end,
57143
+ shiftDate: chartMetrics.date,
57144
+ timezone: configuredTimezone
57145
+ });
57146
+ }, [isCurrentShiftView, chartMetrics, resolvedShiftTimes.start, resolvedShiftTimes.end, configuredTimezone]);
57147
+ const uptimeSeries = React26.useMemo(() => {
57148
+ if (!chartMetrics) {
57149
+ return buildUptimeSeries({
57150
+ idleTimeHourly: null,
57151
+ shiftStart: null,
57152
+ shiftEnd: null,
57153
+ shiftDate: null,
57154
+ timezone: configuredTimezone,
57155
+ elapsedMinutes: null
57156
+ });
57157
+ }
57158
+ return buildUptimeSeries({
57159
+ idleTimeHourly: chartMetrics.idle_time_hourly,
57160
+ shiftStart: resolvedShiftTimes.start,
57161
+ shiftEnd: resolvedShiftTimes.end,
57162
+ shiftDate: chartMetrics.date,
57163
+ timezone: configuredTimezone,
57164
+ elapsedMinutes: elapsedShiftMinutes
57165
+ });
57166
+ }, [chartMetrics, resolvedShiftTimes.start, resolvedShiftTimes.end, configuredTimezone, elapsedShiftMinutes]);
57167
+ const lineUtilizationFromLine = React26.useMemo(() => {
57168
+ const efficiencyValue = Number.isFinite(chartMetrics?.avg_efficiency) ? Number(chartMetrics?.avg_efficiency) : null;
57169
+ if (efficiencyValue !== null) return efficiencyValue;
57170
+ if (!uptimeSeries.availableMinutes) return 0;
57171
+ return uptimeSeries.activeMinutes / uptimeSeries.availableMinutes * 100;
57172
+ }, [chartMetrics?.avg_efficiency, uptimeSeries.availableMinutes, uptimeSeries.activeMinutes]);
57173
+ const lineIdleTimeSecondsFromLine = React26.useMemo(() => uptimeSeries.idleMinutes * 60, [uptimeSeries.idleMinutes]);
57174
+ const workspaceUptimeSummaries = React26.useMemo(() => {
57175
+ if (!isUptimeMode || !resolvedWorkspaces || resolvedWorkspaces.length === 0) return [];
57176
+ return resolvedWorkspaces.map((workspace) => {
57177
+ const shiftStart = workspace.shift_start || resolvedShiftTimes.start;
57178
+ const shiftEnd = workspace.shift_end || resolvedShiftTimes.end;
57179
+ const shiftDate = workspace.date || chartMetrics?.date || null;
57180
+ const shiftMinutes = getShiftDurationMinutes(shiftStart, shiftEnd) || 0;
57181
+ const workspaceElapsedMinutes = isCurrentShiftView ? getShiftElapsedMinutes({
57182
+ shiftStart,
57183
+ shiftEnd,
57184
+ shiftDate,
57185
+ timezone: configuredTimezone
57186
+ }) : null;
57187
+ const uptimeSeries2 = buildUptimeSeries({
57188
+ idleTimeHourly: workspace.idle_time_hourly,
57189
+ shiftStart,
57190
+ shiftEnd,
57191
+ shiftDate,
57192
+ timezone: configuredTimezone,
57193
+ elapsedMinutes: workspaceElapsedMinutes
57194
+ });
57195
+ let activeMinutes = uptimeSeries2.activeMinutes;
57196
+ let idleMinutes = uptimeSeries2.idleMinutes;
57197
+ let availableMinutes = uptimeSeries2.availableMinutes;
57198
+ let hasData = uptimeSeries2.hasData;
57199
+ if (!hasData) {
57200
+ const hasIdleHourlyPayload = Boolean(
57201
+ workspace.idle_time_hourly && Object.keys(workspace.idle_time_hourly).length > 0
57202
+ );
57203
+ const idleTimeValue = workspace.idle_time;
57204
+ const hasIdleTimeValue = Number.isFinite(idleTimeValue);
57205
+ const fallbackDuration = workspaceElapsedMinutes ?? shiftMinutes;
57206
+ if (!hasIdleHourlyPayload && hasIdleTimeValue && idleTimeValue > 0 && fallbackDuration > 0) {
57207
+ const idleSeconds = Number(idleTimeValue);
57208
+ const idleFromAggregate = Math.min(Math.max(idleSeconds / 60, 0), fallbackDuration);
57209
+ idleMinutes = idleFromAggregate;
57210
+ activeMinutes = Math.max(fallbackDuration - idleFromAggregate, 0);
57211
+ availableMinutes = activeMinutes + idleMinutes;
57212
+ hasData = true;
57213
+ }
57214
+ }
57215
+ const efficiencyValue = Number.isFinite(workspace.efficiency) ? Number(workspace.efficiency) : null;
57216
+ const utilization = efficiencyValue !== null ? efficiencyValue : availableMinutes > 0 ? activeMinutes / availableMinutes * 100 : 0;
57217
+ return {
57218
+ workspace,
57219
+ utilization,
57220
+ idleTimeSeconds: idleMinutes * 60,
57221
+ activeMinutes,
57222
+ idleMinutes,
57223
+ availableMinutes,
57224
+ hasData: efficiencyValue !== null || hasData || availableMinutes > 0,
57225
+ uptimeSeries: uptimeSeries2
57226
+ };
57227
+ });
57228
+ }, [
57229
+ isUptimeMode,
57230
+ resolvedWorkspaces,
57231
+ resolvedShiftTimes.start,
57232
+ resolvedShiftTimes.end,
57233
+ chartMetrics?.date,
57234
+ configuredTimezone,
57235
+ isCurrentShiftView
57236
+ ]);
57237
+ const lineUptimeStats = React26.useMemo(() => {
57238
+ if (!isUptimeMode) {
57239
+ return {
57240
+ utilizationPercent: lineUtilizationFromLine,
57241
+ idleTimeSeconds: lineIdleTimeSecondsFromLine
57242
+ };
57243
+ }
57244
+ const validSummaries = workspaceUptimeSummaries.filter((summary) => summary.hasData);
57245
+ if (!validSummaries.length) {
57246
+ return {
57247
+ utilizationPercent: lineUtilizationFromLine,
57248
+ idleTimeSeconds: lineIdleTimeSecondsFromLine
57249
+ };
57250
+ }
57251
+ const avgUtilization = validSummaries.reduce((sum, summary) => sum + summary.utilization, 0) / validSummaries.length;
57252
+ const avgIdleSeconds = validSummaries.reduce((sum, summary) => sum + summary.idleTimeSeconds, 0) / validSummaries.length;
57253
+ return {
57254
+ utilizationPercent: avgUtilization,
57255
+ idleTimeSeconds: avgIdleSeconds
57256
+ };
57257
+ }, [isUptimeMode, workspaceUptimeSummaries, lineUtilizationFromLine, lineIdleTimeSecondsFromLine]);
57258
+ const hourlyAggregates = React26.useMemo(() => {
57259
+ if (!isUptimeMode || workspaceUptimeSummaries.length === 0) return null;
57260
+ const seriesSummaries = workspaceUptimeSummaries.filter((summary) => summary.uptimeSeries.hasData && summary.uptimeSeries.points.length > 0);
57261
+ if (!seriesSummaries.length) return null;
57262
+ const shiftMinutes = seriesSummaries[0].uptimeSeries.shiftMinutes;
57263
+ if (!shiftMinutes || shiftMinutes <= 0) return null;
57264
+ const shiftDurationHours = Math.ceil(shiftMinutes / 60);
57265
+ return Array.from({ length: shiftDurationHours }, (_, hourIndex) => {
57266
+ const start = hourIndex * 60;
57267
+ const end = Math.min(start + 60, shiftMinutes);
57268
+ let totalActive = 0;
57269
+ let totalIdle = 0;
57270
+ let totalProductivePercent = 0;
57271
+ let workspaceCount = 0;
57272
+ seriesSummaries.forEach((summary) => {
57273
+ const slice = summary.uptimeSeries.points.slice(start, end);
57274
+ if (!slice.length) return;
57275
+ let activeForWorkspace = 0;
57276
+ let idleForWorkspace = 0;
57277
+ slice.forEach((point) => {
57278
+ if (point.status === "active") activeForWorkspace += 1;
57279
+ if (point.status === "idle") idleForWorkspace += 1;
57280
+ });
57281
+ const known = activeForWorkspace + idleForWorkspace;
57282
+ if (known > 0) {
57283
+ workspaceCount += 1;
57284
+ totalActive += activeForWorkspace;
57285
+ totalIdle += idleForWorkspace;
57286
+ totalProductivePercent += activeForWorkspace / known * 100;
57287
+ }
57288
+ });
57289
+ const avgProductivePercent = workspaceCount > 0 ? totalProductivePercent / workspaceCount : 0;
57290
+ const productivePercent = workspaceCount > 0 ? Number(avgProductivePercent.toFixed(2)) : 0;
57291
+ const idlePercent = workspaceCount > 0 ? Math.max(0, Math.min(100, Number((100 - productivePercent).toFixed(2)))) : 0;
57292
+ const avgIdleMinutes = workspaceCount > 0 ? totalIdle / workspaceCount : 0;
57293
+ const avgProductiveMinutes = workspaceCount > 0 ? totalActive / workspaceCount : 0;
57294
+ return {
57295
+ productivePercent,
57296
+ idlePercent,
57297
+ idleMinutes: avgIdleMinutes,
57298
+ productiveMinutes: avgProductiveMinutes
57299
+ };
57300
+ });
57301
+ }, [isUptimeMode, workspaceUptimeSummaries]);
55896
57302
  const isHistoricalAwaitingParams = historicalRouteRequested && !historicalParamsReady;
55897
57303
  const isHistoricalAwaitingData = historicalRouteRequested && historicalParamsReady && (lineMetricsLoading || workspacesLoading);
55898
57304
  React26.useEffect(() => {
@@ -56058,6 +57464,10 @@ var KPIDetailView = ({
56058
57464
  if (!resolvedWorkspaces) return [];
56059
57465
  return [...resolvedWorkspaces].filter((w) => w.efficiency >= 10).sort((a, b) => (a.efficiency || 0) - (b.efficiency || 0)).slice(0, 5);
56060
57466
  }, [resolvedWorkspaces]);
57467
+ const sortedByUtilization = React26.useMemo(() => {
57468
+ if (!isUptimeMode || workspaceUptimeSummaries.length === 0) return [];
57469
+ return [...workspaceUptimeSummaries].filter((summary) => summary.hasData).sort((a, b) => a.utilization - b.utilization).slice(0, 5).map((summary) => ({ ...summary.workspace, utilization: summary.utilization }));
57470
+ }, [isUptimeMode, workspaceUptimeSummaries]);
56061
57471
  React26.useEffect(() => {
56062
57472
  let timeoutId;
56063
57473
  const suppressNotFound = isHistoricalAwaitingParams || isHistoricalAwaitingData;
@@ -56378,7 +57788,7 @@ var KPIDetailView = ({
56378
57788
  {
56379
57789
  onClick: handleOverviewClick,
56380
57790
  className: `flex-1 px-2 py-1.5 text-xs font-medium rounded-md transition-all duration-200 ${activeTab === "overview" ? "bg-white text-gray-900 shadow-sm" : "text-gray-600"}`,
56381
- children: "Overview"
57791
+ children: overviewTabLabel
56382
57792
  }
56383
57793
  ),
56384
57794
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -56397,7 +57807,7 @@ var KPIDetailView = ({
56397
57807
  {
56398
57808
  onClick: handleOverviewClick,
56399
57809
  className: `px-3 py-1.5 text-sm font-medium rounded-lg transition-colors whitespace-nowrap ${activeTab === "overview" ? "bg-blue-50 text-blue-600" : "text-gray-600 hover:bg-gray-50"}`,
56400
- children: "Efficiency"
57810
+ children: overviewTabLabel
56401
57811
  }
56402
57812
  ),
56403
57813
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -56431,6 +57841,7 @@ var KPIDetailView = ({
56431
57841
  lineName: resolvedLineInfo?.line_name || "Line",
56432
57842
  monthlyData,
56433
57843
  analysisData: analysisMonthlyData,
57844
+ monitoringMode: resolvedMonitoringMode,
56434
57845
  underperformingWorkspaces,
56435
57846
  legend: efficiencyLegend,
56436
57847
  selectedMonth: currentMonth,
@@ -56455,7 +57866,16 @@ var KPIDetailView = ({
56455
57866
  exit: "exit",
56456
57867
  className: "space-y-6 md:space-y-0",
56457
57868
  children: resolvedLineInfo && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
56458
- /* @__PURE__ */ jsxRuntime.jsx(
57869
+ isUptimeMode ? /* @__PURE__ */ jsxRuntime.jsx(
57870
+ LineUptimeMetricCards,
57871
+ {
57872
+ utilizationPercent: lineUptimeStats.utilizationPercent,
57873
+ stoppages: chartMetrics?.current_output || 0,
57874
+ idleTimeSeconds: lineUptimeStats.idleTimeSeconds,
57875
+ idleTimeData,
57876
+ showIdleTime: idleTimeVlmEnabled
57877
+ }
57878
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
56459
57879
  MetricCards,
56460
57880
  {
56461
57881
  lineInfo: resolvedLineInfo,
@@ -56464,7 +57884,25 @@ var KPIDetailView = ({
56464
57884
  efficiencyLegend
56465
57885
  }
56466
57886
  ),
56467
- /* @__PURE__ */ jsxRuntime.jsx(
57887
+ isUptimeMode ? /* @__PURE__ */ jsxRuntime.jsx(
57888
+ UptimeBottomSection,
57889
+ {
57890
+ lineId,
57891
+ workspaceData: resolvedWorkspaces || [],
57892
+ sortedByUtilization,
57893
+ workspaceDisplayNames,
57894
+ idleTimeHourly: chartMetrics?.idle_time_hourly,
57895
+ hourlyAggregates,
57896
+ shiftStart: resolvedShiftTimes.start,
57897
+ shiftEnd: resolvedShiftTimes.end,
57898
+ shiftDate: chartMetrics?.date,
57899
+ timezone: configuredTimezone,
57900
+ elapsedMinutes: elapsedShiftMinutes,
57901
+ urlDate,
57902
+ urlShift,
57903
+ navigate
57904
+ }
57905
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
56468
57906
  BottomSection,
56469
57907
  {
56470
57908
  lineInfo: resolvedLineInfo,
@@ -56503,6 +57941,8 @@ var KPIDetailView = ({
56503
57941
  rangeEnd,
56504
57942
  timezone: configuredTimezone,
56505
57943
  legend: efficiencyLegend,
57944
+ monitoringMode: resolvedMonitoringMode,
57945
+ shiftConfig,
56506
57946
  selectedShiftId,
56507
57947
  onShiftChange: setSelectedShiftId,
56508
57948
  onRangeChange: handleRangeChange,
@@ -56523,7 +57963,16 @@ var KPIDetailView = ({
56523
57963
  exit: "exit",
56524
57964
  className: "space-y-6 md:space-y-0",
56525
57965
  children: resolvedLineInfo && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
56526
- /* @__PURE__ */ jsxRuntime.jsx(
57966
+ isUptimeMode ? /* @__PURE__ */ jsxRuntime.jsx(
57967
+ LineUptimeMetricCards,
57968
+ {
57969
+ utilizationPercent: lineUptimeStats.utilizationPercent,
57970
+ stoppages: chartMetrics?.current_output || 0,
57971
+ idleTimeSeconds: lineUptimeStats.idleTimeSeconds,
57972
+ idleTimeData,
57973
+ showIdleTime: idleTimeVlmEnabled
57974
+ }
57975
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
56527
57976
  MetricCards,
56528
57977
  {
56529
57978
  lineInfo: resolvedLineInfo,
@@ -56532,7 +57981,25 @@ var KPIDetailView = ({
56532
57981
  efficiencyLegend
56533
57982
  }
56534
57983
  ),
56535
- /* @__PURE__ */ jsxRuntime.jsx(
57984
+ isUptimeMode ? /* @__PURE__ */ jsxRuntime.jsx(
57985
+ UptimeBottomSection,
57986
+ {
57987
+ lineId,
57988
+ workspaceData: resolvedWorkspaces || [],
57989
+ sortedByUtilization,
57990
+ workspaceDisplayNames,
57991
+ idleTimeHourly: chartMetrics?.idle_time_hourly,
57992
+ hourlyAggregates,
57993
+ shiftStart: resolvedShiftTimes.start,
57994
+ shiftEnd: resolvedShiftTimes.end,
57995
+ shiftDate: chartMetrics?.date,
57996
+ timezone: configuredTimezone,
57997
+ elapsedMinutes: elapsedShiftMinutes,
57998
+ urlDate,
57999
+ urlShift,
58000
+ navigate
58001
+ }
58002
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
56536
58003
  BottomSection,
56537
58004
  {
56538
58005
  lineInfo: resolvedLineInfo,
@@ -56881,7 +58348,7 @@ var LinesLeaderboard = ({
56881
58348
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: `font-bold text-gray-900 text-center line-clamp-1 mb-1 ${isFirst ? "text-xs md:text-sm lg:text-base xl:text-lg" : "text-[10px] md:text-xs lg:text-sm xl:text-base"}`, children: item.supervisorName }),
56882
58349
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: `text-gray-600 text-center line-clamp-1 font-medium opacity-80 bg-white/50 px-2 md:px-3 py-0.5 rounded-full ${isFirst ? "text-[9px] md:text-[10px] lg:text-xs xl:text-sm mb-2 md:mb-3" : "text-[8px] md:text-[9px] lg:text-[10px] xl:text-xs mb-1 md:mb-2"}`, children: item.line.line_name }),
56883
58350
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center mt-auto", children: [
56884
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: `font-bold uppercase tracking-widest mb-0.5 ${isFirst ? "text-yellow-700/70 text-[8px] md:text-[9px] lg:text-[10px] xl:text-xs" : isSecond ? "text-gray-600/70 text-[7px] md:text-[8px] lg:text-[9px] xl:text-[10px]" : "text-orange-700/70 text-[7px] md:text-[8px] lg:text-[9px] xl:text-[10px]"}`, children: "Efficiency" }),
58351
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `font-bold uppercase tracking-widest mb-0.5 ${isFirst ? "text-yellow-700/70 text-[8px] md:text-[9px] lg:text-[10px] xl:text-xs" : isSecond ? "text-gray-600/70 text-[7px] md:text-[8px] lg:text-[9px] xl:text-[10px]" : "text-orange-700/70 text-[7px] md:text-[8px] lg:text-[9px] xl:text-[10px]"}`, children: viewType === "machine" ? "Utilization" : "Efficiency" }),
56885
58352
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: `font-black tracking-tight leading-none ${isFirst ? "text-lg md:text-xl lg:text-2xl xl:text-3xl text-transparent bg-clip-text bg-gradient-to-br from-yellow-600 to-yellow-800 drop-shadow-sm" : isSecond ? "text-base md:text-lg lg:text-xl xl:text-2xl text-transparent bg-clip-text bg-gradient-to-br from-gray-600 to-gray-800 drop-shadow-sm" : "text-sm md:text-base lg:text-lg xl:text-xl text-transparent bg-clip-text bg-gradient-to-br from-orange-600 to-orange-800 drop-shadow-sm"}`, children: formatEfficiency(item.efficiency) })
56886
58353
  ] })
56887
58354
  ] })
@@ -56898,7 +58365,7 @@ var LinesLeaderboard = ({
56898
58365
  /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider w-16", children: "Rank" }),
56899
58366
  /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider", children: "Assigned To" }),
56900
58367
  /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider", children: "Line" }),
56901
- /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-4 py-3 text-right text-xs font-semibold text-gray-500 uppercase tracking-wider", children: "Efficiency" })
58368
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-4 py-3 text-right text-xs font-semibold text-gray-500 uppercase tracking-wider", children: viewType === "machine" ? "Utilization" : "Efficiency" })
56902
58369
  ] }) }),
56903
58370
  /* @__PURE__ */ jsxRuntime.jsx("tbody", { className: "divide-y divide-gray-100", children: leaderboardData.map((item) => {
56904
58371
  const isTopThree = item.rank <= 3;
@@ -56954,7 +58421,8 @@ var LineCard = ({
56954
58421
  supervisorName,
56955
58422
  supervisors
56956
58423
  }) => {
56957
- const isOnTrack = React26__namespace.default.useMemo(() => {
58424
+ const isUptimeLine = (line.monitoring_mode ?? "output") === "uptime";
58425
+ React26__namespace.default.useMemo(() => {
56958
58426
  if (!kpis) return null;
56959
58427
  return kpis.efficiency.value > 90;
56960
58428
  }, [kpis]);
@@ -56967,73 +58435,67 @@ var LineCard = ({
56967
58435
  onClick: () => onClick(kpis),
56968
58436
  className: "relative bg-white border border-gray-200/80 shadow-sm hover:shadow-lg \n rounded-xl p-4 sm:p-5 md:p-6 transition-all duration-200 cursor-pointer \n hover:scale-[1.01] active:scale-[0.99] group",
56969
58437
  children: [
56970
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-4 sm:mb-5 md:mb-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-start gap-2 sm:gap-3", children: [
56971
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
56972
- /* @__PURE__ */ jsxRuntime.jsx(
56973
- FittingTitle,
56974
- {
56975
- title: line.line_name,
56976
- className: "text-[10px] sm:text-xs md:text-sm"
56977
- }
56978
- ),
56979
- supervisorEnabled && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4", children: [
56980
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[10px] font-semibold text-gray-400 uppercase tracking-wider mb-1.5", children: "Assigned To" }),
56981
- supervisors && supervisors.length > 0 ? supervisors.length === 1 ? (
56982
- // Single supervisor - just avatar with tooltip
56983
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-12 h-12 rounded-full bg-white border border-gray-100 shadow-sm flex-shrink-0 group/avatar hover:scale-110 transition-transform", children: [
56984
- supervisors[0].profilePhotoUrl ? /* @__PURE__ */ jsxRuntime.jsx(
56985
- "img",
56986
- {
56987
- src: supervisors[0].profilePhotoUrl,
56988
- alt: supervisors[0].displayName,
56989
- className: "w-full h-full object-cover rounded-full"
56990
- }
56991
- ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full flex items-center justify-center bg-gray-50 text-base font-bold text-gray-500 uppercase rounded-full", children: getInitials(supervisors[0].displayName) }),
56992
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute bottom-full left-1/2 -translate-x-1/2 mb-2 px-2 py-1 bg-gray-900 text-white text-[10px] font-medium rounded shadow-lg opacity-0 group-hover/avatar:opacity-100 transition-opacity whitespace-nowrap pointer-events-none z-20", children: [
56993
- supervisors[0].displayName,
56994
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-full left-1/2 -translate-x-1/2 -mt-[1px] border-4 border-transparent border-t-gray-900" })
56995
- ] })
58438
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-4 sm:mb-5 md:mb-6", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap items-start gap-2 sm:gap-3", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
58439
+ /* @__PURE__ */ jsxRuntime.jsx(
58440
+ FittingTitle,
58441
+ {
58442
+ title: line.line_name,
58443
+ className: "text-[10px] sm:text-xs md:text-sm"
58444
+ }
58445
+ ),
58446
+ supervisorEnabled && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4", children: [
58447
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[10px] font-semibold text-gray-400 uppercase tracking-wider mb-1.5", children: "Assigned To" }),
58448
+ supervisors && supervisors.length > 0 ? supervisors.length === 1 ? (
58449
+ // Single supervisor - just avatar with tooltip
58450
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-12 h-12 rounded-full bg-white border border-gray-100 shadow-sm flex-shrink-0 group/avatar hover:scale-110 transition-transform", children: [
58451
+ supervisors[0].profilePhotoUrl ? /* @__PURE__ */ jsxRuntime.jsx(
58452
+ "img",
58453
+ {
58454
+ src: supervisors[0].profilePhotoUrl,
58455
+ alt: supervisors[0].displayName,
58456
+ className: "w-full h-full object-cover rounded-full"
58457
+ }
58458
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full flex items-center justify-center bg-gray-50 text-base font-bold text-gray-500 uppercase rounded-full", children: getInitials(supervisors[0].displayName) }),
58459
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute bottom-full left-1/2 -translate-x-1/2 mb-2 px-2 py-1 bg-gray-900 text-white text-[10px] font-medium rounded shadow-lg opacity-0 group-hover/avatar:opacity-100 transition-opacity whitespace-nowrap pointer-events-none z-20", children: [
58460
+ supervisors[0].displayName,
58461
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-full left-1/2 -translate-x-1/2 -mt-[1px] border-4 border-transparent border-t-gray-900" })
56996
58462
  ] })
56997
- ) : (
56998
- // Multiple supervisors - overlapping avatars
56999
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex -space-x-4 py-1", children: [
57000
- supervisors.slice(0, 3).map((supervisor) => /* @__PURE__ */ jsxRuntime.jsxs(
57001
- "div",
57002
- {
57003
- className: "relative inline-block w-12 h-12 rounded-full ring-2 ring-white bg-white shadow-sm z-0 hover:z-10 transition-all hover:scale-110 group/avatar",
57004
- children: [
57005
- supervisor.profilePhotoUrl ? /* @__PURE__ */ jsxRuntime.jsx(
57006
- "img",
57007
- {
57008
- src: supervisor.profilePhotoUrl,
57009
- alt: supervisor.displayName,
57010
- className: "w-full h-full object-cover rounded-full"
57011
- }
57012
- ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full flex items-center justify-center bg-gray-100 text-sm font-bold text-gray-600 uppercase rounded-full", children: getInitials(supervisor.displayName) }),
57013
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute bottom-full left-1/2 -translate-x-1/2 mb-2 px-2 py-1 bg-gray-900 text-white text-[10px] font-medium rounded shadow-lg opacity-0 group-hover/avatar:opacity-100 transition-opacity whitespace-nowrap pointer-events-none z-20", children: [
57014
- supervisor.displayName,
57015
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-full left-1/2 -translate-x-1/2 -mt-[1px] border-4 border-transparent border-t-gray-900" })
57016
- ] })
57017
- ]
57018
- },
57019
- supervisor.userId
57020
- )),
57021
- supervisors.length > 3 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex w-12 h-12 rounded-full ring-2 ring-white bg-gray-100 items-center justify-center text-sm font-medium text-gray-600 z-0", children: [
57022
- "+",
57023
- supervisors.length - 3
57024
- ] })
58463
+ ] })
58464
+ ) : (
58465
+ // Multiple supervisors - overlapping avatars
58466
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex -space-x-4 py-1", children: [
58467
+ supervisors.slice(0, 3).map((supervisor) => /* @__PURE__ */ jsxRuntime.jsxs(
58468
+ "div",
58469
+ {
58470
+ className: "relative inline-block w-12 h-12 rounded-full ring-2 ring-white bg-white shadow-sm z-0 hover:z-10 transition-all hover:scale-110 group/avatar",
58471
+ children: [
58472
+ supervisor.profilePhotoUrl ? /* @__PURE__ */ jsxRuntime.jsx(
58473
+ "img",
58474
+ {
58475
+ src: supervisor.profilePhotoUrl,
58476
+ alt: supervisor.displayName,
58477
+ className: "w-full h-full object-cover rounded-full"
58478
+ }
58479
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full flex items-center justify-center bg-gray-100 text-sm font-bold text-gray-600 uppercase rounded-full", children: getInitials(supervisor.displayName) }),
58480
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute bottom-full left-1/2 -translate-x-1/2 mb-2 px-2 py-1 bg-gray-900 text-white text-[10px] font-medium rounded shadow-lg opacity-0 group-hover/avatar:opacity-100 transition-opacity whitespace-nowrap pointer-events-none z-20", children: [
58481
+ supervisor.displayName,
58482
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-full left-1/2 -translate-x-1/2 -mt-[1px] border-4 border-transparent border-t-gray-900" })
58483
+ ] })
58484
+ ]
58485
+ },
58486
+ supervisor.userId
58487
+ )),
58488
+ supervisors.length > 3 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex w-12 h-12 rounded-full ring-2 ring-white bg-gray-100 items-center justify-center text-sm font-medium text-gray-600 z-0", children: [
58489
+ "+",
58490
+ supervisors.length - 3
57025
58491
  ] })
57026
- ) : /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-gray-600", children: [
57027
- "Supervisor: ",
57028
- supervisorName || "Unassigned"
57029
58492
  ] })
58493
+ ) : /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-gray-600", children: [
58494
+ "Supervisor: ",
58495
+ supervisorName || "Unassigned"
57030
58496
  ] })
57031
- ] }),
57032
- kpis && isOnTrack !== null && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-1.5 px-2.5 sm:px-3 py-1 sm:py-1.5 rounded-full text-xs font-medium flex-shrink-0 ${isOnTrack ? "bg-emerald-100 text-emerald-700 border border-emerald-200" : "bg-red-100 text-red-700 border border-red-200"}`, style: { minWidth: "fit-content" }, children: [
57033
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-2 h-2 rounded-full ${isOnTrack ? "bg-emerald-500" : "bg-red-500"} animate-pulse` }),
57034
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: isOnTrack ? "On Track" : "Behind" })
57035
58497
  ] })
57036
- ] }) }),
58498
+ ] }) }) }),
57037
58499
  isLoading && !kpis && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
57038
58500
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "animate-pulse", children: [
57039
58501
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-gray-200 rounded w-1/3 mb-2" }),
@@ -57051,7 +58513,7 @@ var LineCard = ({
57051
58513
  error && !kpis && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center py-8", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500", children: "Unable to load metrics" }) }),
57052
58514
  kpis && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4 sm:space-y-5 pb-8 sm:pb-10", children: [
57053
58515
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
57054
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs font-medium text-gray-500 uppercase tracking-wider mb-1.5 sm:mb-2", children: "Efficiency" }),
58516
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs font-medium text-gray-500 uppercase tracking-wider mb-1.5 sm:mb-2", children: isUptimeLine ? "Utilization" : "Efficiency" }),
57055
58517
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline justify-between", children: [
57056
58518
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-2xl sm:text-3xl font-semibold text-gray-900", children: [
57057
58519
  kpis.efficiency.value.toFixed(1),
@@ -57065,16 +58527,16 @@ var LineCard = ({
57065
58527
  ] })
57066
58528
  ] }),
57067
58529
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
57068
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs font-medium text-gray-500 uppercase tracking-wider mb-1.5 sm:mb-2", children: "Output Progress" }),
58530
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs font-medium text-gray-500 uppercase tracking-wider mb-1.5 sm:mb-2", children: isUptimeLine ? "Stoppages" : "Output Progress" }),
57069
58531
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline justify-between mb-2 sm:mb-3", children: [
57070
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xl sm:text-2xl font-semibold text-gray-900", children: kpis.outputProgress.current }),
57071
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs sm:text-sm text-gray-500 font-medium", children: [
58532
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${isUptimeLine ? "text-2xl sm:text-3xl font-bold text-red-600" : "text-xl sm:text-2xl font-semibold text-gray-900"}`, children: kpis.outputProgress.current }),
58533
+ !isUptimeLine && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs sm:text-sm text-gray-500 font-medium", children: [
57072
58534
  "/ ",
57073
58535
  kpis.outputProgress.target,
57074
58536
  " units"
57075
58537
  ] })
57076
58538
  ] }),
57077
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full bg-gray-200 rounded-full h-2 sm:h-2.5", children: /* @__PURE__ */ jsxRuntime.jsx(
58539
+ !isUptimeLine && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full bg-gray-200 rounded-full h-2 sm:h-2.5", children: /* @__PURE__ */ jsxRuntime.jsx(
57078
58540
  "div",
57079
58541
  {
57080
58542
  className: "bg-blue-600 h-2 sm:h-2.5 rounded-full transition-all duration-500 ease-out",
@@ -57150,6 +58612,38 @@ var KPIsOverviewView = ({
57150
58612
  const targetMode = viewType === "machine" ? "uptime" : "output";
57151
58613
  return leaderboardLines.filter((line) => (line.monitoring_mode ?? "output") === targetMode);
57152
58614
  }, [leaderboardLines, viewType]);
58615
+ const linesForView = React26__namespace.default.useMemo(() => {
58616
+ const targetMode = viewType === "machine" ? "uptime" : "output";
58617
+ return lines.filter((line) => (line.monitoring_mode ?? "output") === targetMode);
58618
+ }, [lines, viewType]);
58619
+ const relevantLinesForMode = React26__namespace.default.useMemo(() => {
58620
+ if (activeTab === "leaderboard") {
58621
+ return leaderboardLines.length > 0 ? leaderboardLines : lines;
58622
+ }
58623
+ return lines;
58624
+ }, [activeTab, leaderboardLines, lines]);
58625
+ const { hasUptime, hasOutput } = React26__namespace.default.useMemo(() => {
58626
+ let uptime = false;
58627
+ let output = false;
58628
+ for (const line of relevantLinesForMode) {
58629
+ const mode = line.monitoring_mode ?? "output";
58630
+ if (mode === "uptime") {
58631
+ uptime = true;
58632
+ } else {
58633
+ output = true;
58634
+ }
58635
+ if (uptime && output) break;
58636
+ }
58637
+ return { hasUptime: uptime, hasOutput: output };
58638
+ }, [relevantLinesForMode]);
58639
+ const showViewTypeDropdown = hasUptime && hasOutput;
58640
+ React26.useEffect(() => {
58641
+ if (hasUptime && !hasOutput && viewType !== "machine") {
58642
+ setViewType("machine");
58643
+ } else if (hasOutput && !hasUptime && viewType !== "operator") {
58644
+ setViewType("operator");
58645
+ }
58646
+ }, [hasUptime, hasOutput, viewType]);
57153
58647
  const currentShiftDetails = getCurrentShift(configuredTimezone, shiftConfig);
57154
58648
  const currentShiftDate = currentShiftDetails.date;
57155
58649
  const currentShiftId = currentShiftDetails.shiftId;
@@ -57822,7 +59316,7 @@ var KPIsOverviewView = ({
57822
59316
  }
57823
59317
  )
57824
59318
  ] }),
57825
- activeTab === "leaderboard" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative", children: /* @__PURE__ */ jsxRuntime.jsxs(
59319
+ (activeTab === "leaderboard" || activeTab === "today") && showViewTypeDropdown && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative", children: /* @__PURE__ */ jsxRuntime.jsxs(
57826
59320
  "select",
57827
59321
  {
57828
59322
  value: viewType,
@@ -57830,7 +59324,7 @@ var KPIsOverviewView = ({
57830
59324
  className: "appearance-none pl-3 pr-8 py-1.5 text-sm font-medium bg-white border border-gray-200 hover:border-gray-300 rounded-lg text-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500 transition-all cursor-pointer shadow-sm",
57831
59325
  style: { backgroundImage: `url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e")`, backgroundPosition: `right 0.5rem center`, backgroundRepeat: `no-repeat`, backgroundSize: `1.2em 1.2em` },
57832
59326
  children: [
57833
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "operator", children: "Operator" }),
59327
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "operator", children: "Workforce" }),
57834
59328
  /* @__PURE__ */ jsxRuntime.jsx("option", { value: "machine", children: "Machine" })
57835
59329
  ]
57836
59330
  }
@@ -57840,7 +59334,7 @@ var KPIsOverviewView = ({
57840
59334
  ] }) }),
57841
59335
  /* @__PURE__ */ jsxRuntime.jsx("main", { className: `flex-1 p-3 sm:p-4 md:p-6 bg-slate-50 ${activeTab === "leaderboard" ? "overflow-hidden flex flex-col" : "overflow-y-auto"}`, children: activeTab === "today" ? (
57842
59336
  /* Line Cards Grid */
57843
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-3 sm:gap-4 md:gap-6", children: lines.map((line) => /* @__PURE__ */ jsxRuntime.jsx(
59337
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-3 sm:gap-4 md:gap-6", children: linesForView.map((line) => /* @__PURE__ */ jsxRuntime.jsx(
57844
59338
  LineCard,
57845
59339
  {
57846
59340
  line,
@@ -57947,7 +59441,8 @@ var MobileWorkspaceCard = React26.memo(({
57947
59441
  rank,
57948
59442
  cardClass,
57949
59443
  onWorkspaceClick,
57950
- getMedalIcon
59444
+ getMedalIcon,
59445
+ efficiencyLabel
57951
59446
  }) => /* @__PURE__ */ jsxRuntime.jsx(
57952
59447
  motion.div,
57953
59448
  {
@@ -57975,12 +59470,12 @@ var MobileWorkspaceCard = React26.memo(({
57975
59470
  ] }),
57976
59471
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-right", children: [
57977
59472
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-lg font-bold text-gray-900", children: /* @__PURE__ */ jsxRuntime.jsx(AnimatedEfficiency, { value: workspace.efficiency || 0 }) }),
57978
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-gray-500", children: "Efficiency" })
59473
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-gray-500", children: efficiencyLabel })
57979
59474
  ] })
57980
59475
  ] })
57981
59476
  }
57982
59477
  ), (prevProps, nextProps) => {
57983
- return prevProps.rank === nextProps.rank && prevProps.cardClass === nextProps.cardClass && prevProps.workspace.workspace_uuid === nextProps.workspace.workspace_uuid && prevProps.workspace.efficiency === nextProps.workspace.efficiency && prevProps.workspace.action_count === nextProps.workspace.action_count && prevProps.workspace.action_threshold === nextProps.workspace.action_threshold && prevProps.workspace.avg_cycle_time === nextProps.workspace.avg_cycle_time && prevProps.workspace.displayName === nextProps.workspace.displayName && prevProps.workspace.lineName === nextProps.workspace.lineName;
59478
+ return prevProps.efficiencyLabel === nextProps.efficiencyLabel && prevProps.rank === nextProps.rank && prevProps.cardClass === nextProps.cardClass && prevProps.workspace.workspace_uuid === nextProps.workspace.workspace_uuid && prevProps.workspace.efficiency === nextProps.workspace.efficiency && prevProps.workspace.action_count === nextProps.workspace.action_count && prevProps.workspace.action_threshold === nextProps.workspace.action_threshold && prevProps.workspace.avg_cycle_time === nextProps.workspace.avg_cycle_time && prevProps.workspace.displayName === nextProps.workspace.displayName && prevProps.workspace.lineName === nextProps.workspace.lineName;
57984
59479
  });
57985
59480
  MobileWorkspaceCard.displayName = "MobileWorkspaceCard";
57986
59481
  var DesktopWorkspaceRow = React26.memo(({
@@ -58169,6 +59664,39 @@ var LeaderboardDetailView = React26.memo(({
58169
59664
  }
58170
59665
  return allLineIds;
58171
59666
  }, [entityConfig, userAccessibleLineIds]);
59667
+ const { hasUptime: lineModeHasUptime, hasOutput: lineModeHasOutput } = React26.useMemo(() => {
59668
+ if (!lines || lines.length === 0) {
59669
+ return { hasUptime: false, hasOutput: false };
59670
+ }
59671
+ if (selectedLineFilter !== "all") {
59672
+ const selectedLine = lines.find((line) => line.id === selectedLineFilter);
59673
+ if (!selectedLine) {
59674
+ return { hasUptime: false, hasOutput: false };
59675
+ }
59676
+ const mode = selectedLine.monitoring_mode ?? "output";
59677
+ return { hasUptime: mode === "uptime", hasOutput: mode !== "uptime" };
59678
+ }
59679
+ let uptime = false;
59680
+ let output = false;
59681
+ for (const line of lines) {
59682
+ const mode = line.monitoring_mode ?? "output";
59683
+ if (mode === "uptime") {
59684
+ uptime = true;
59685
+ } else {
59686
+ output = true;
59687
+ }
59688
+ if (uptime && output) break;
59689
+ }
59690
+ return { hasUptime: uptime, hasOutput: output };
59691
+ }, [lines, selectedLineFilter]);
59692
+ const showViewTypeDropdown = lineModeHasUptime && lineModeHasOutput;
59693
+ React26.useEffect(() => {
59694
+ if (lineModeHasUptime && !lineModeHasOutput && viewType !== "machine") {
59695
+ setViewType("machine");
59696
+ } else if (lineModeHasOutput && !lineModeHasUptime && viewType !== "operator") {
59697
+ setViewType("operator");
59698
+ }
59699
+ }, [lineModeHasUptime, lineModeHasOutput, viewType]);
58172
59700
  const lineKey = React26.useMemo(() => {
58173
59701
  if (configuredLineIds.length === 0) {
58174
59702
  return "all";
@@ -58628,6 +60156,7 @@ var LeaderboardDetailView = React26.memo(({
58628
60156
  error.message
58629
60157
  ] }) });
58630
60158
  }
60159
+ const efficiencyLabel = viewType === "machine" ? "Utilization" : "Efficiency";
58631
60160
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `min-h-screen bg-slate-50 flex flex-col ${className}`, style: { willChange: "contents" }, children: [
58632
60161
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sticky top-0 z-20 bg-white shadow-sm border-b border-gray-200/80", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-3 sm:px-6 md:px-8 py-2 sm:py-2.5", children: [
58633
60162
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sm:hidden", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", children: [
@@ -58677,7 +60206,7 @@ var LeaderboardDetailView = React26.memo(({
58677
60206
  ] }),
58678
60207
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
58679
60208
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
58680
- /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-xs font-medium text-gray-500 uppercase tracking-wide ml-1", children: "Efficiency" }),
60209
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-xs font-medium text-gray-500 uppercase tracking-wide ml-1", children: efficiencyLabel }),
58681
60210
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative", children: /* @__PURE__ */ jsxRuntime.jsxs(
58682
60211
  "select",
58683
60212
  {
@@ -58814,7 +60343,7 @@ var LeaderboardDetailView = React26.memo(({
58814
60343
  showLabel: false
58815
60344
  }
58816
60345
  ),
58817
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative", children: /* @__PURE__ */ jsxRuntime.jsxs(
60346
+ showViewTypeDropdown && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative", children: /* @__PURE__ */ jsxRuntime.jsxs(
58818
60347
  "select",
58819
60348
  {
58820
60349
  value: viewType,
@@ -58822,7 +60351,7 @@ var LeaderboardDetailView = React26.memo(({
58822
60351
  className: "appearance-none pl-3 pr-8 py-1.5 text-sm font-medium bg-white border border-gray-200 hover:border-gray-300 rounded-lg text-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500 transition-all cursor-pointer shadow-sm",
58823
60352
  style: { backgroundImage: `url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e")`, backgroundPosition: `right 0.5rem center`, backgroundRepeat: `no-repeat`, backgroundSize: `1.2em 1.2em` },
58824
60353
  children: [
58825
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "operator", children: "Operator" }),
60354
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "operator", children: "Workforce" }),
58826
60355
  /* @__PURE__ */ jsxRuntime.jsx("option", { value: "machine", children: "Machine" })
58827
60356
  ]
58828
60357
  }
@@ -58856,7 +60385,8 @@ var LeaderboardDetailView = React26.memo(({
58856
60385
  rank,
58857
60386
  cardClass,
58858
60387
  onWorkspaceClick: stableHandleWorkspaceClick,
58859
- getMedalIcon: stableGetMedalIcon
60388
+ getMedalIcon: stableGetMedalIcon,
60389
+ efficiencyLabel
58860
60390
  },
58861
60391
  ws.workspace_uuid
58862
60392
  );
@@ -58867,7 +60397,7 @@ var LeaderboardDetailView = React26.memo(({
58867
60397
  /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-3 py-2.5 sm:p-4 text-left text-xs sm:text-sm font-semibold text-gray-600 whitespace-nowrap", children: "Rank" }),
58868
60398
  /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-3 py-2.5 sm:p-4 text-left text-xs sm:text-sm font-semibold text-gray-600 whitespace-nowrap", children: "Workspace" }),
58869
60399
  /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-3 py-2.5 sm:p-4 text-left text-xs sm:text-sm font-semibold text-gray-600 whitespace-nowrap", children: "Line" }),
58870
- /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-3 py-2.5 sm:p-4 text-left text-xs sm:text-sm font-semibold text-gray-600 whitespace-nowrap", children: "Efficiency" })
60400
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-3 py-2.5 sm:p-4 text-left text-xs sm:text-sm font-semibold text-gray-600 whitespace-nowrap", children: efficiencyLabel })
58871
60401
  ] }) }),
58872
60402
  /* @__PURE__ */ jsxRuntime.jsx("tbody", { className: "divide-y divide-gray-100", children: sortedWorkspaces.map((ws, index) => {
58873
60403
  const isTopThree = index < 3;
@@ -62018,7 +63548,7 @@ var WorkspaceDetailView = ({
62018
63548
  date: cachedOverviewMetrics.date,
62019
63549
  shift_id: cachedOverviewMetrics.shift_id,
62020
63550
  action_name: "",
62021
- monitoring_mode: "output",
63551
+ monitoring_mode: cachedOverviewMetrics.monitoring_mode ?? "output",
62022
63552
  shift_start: shiftDefinition?.startTime || "",
62023
63553
  shift_end: shiftDefinition?.endTime || "",
62024
63554
  shift_type: shiftType,
@@ -62034,7 +63564,7 @@ var WorkspaceDetailView = ({
62034
63564
  total_workspaces: 0,
62035
63565
  ideal_output_until_now: idealOutput,
62036
63566
  output_difference: totalActions - idealOutput,
62037
- idle_time: 0,
63567
+ idle_time: Number(cachedOverviewMetrics.idle_time || 0),
62038
63568
  idle_time_hourly: void 0
62039
63569
  };
62040
63570
  }, [cachedOverviewMetrics, shiftConfig?.shifts]);
@@ -62213,16 +63743,39 @@ var WorkspaceDetailView = ({
62213
63743
  };
62214
63744
  dayDataMap.set(dateKey, dayEntry);
62215
63745
  }
63746
+ const idleTimeSecondsRaw = Number.isFinite(metric.idle_time_seconds) ? Number(metric.idle_time_seconds) : Number.isFinite(metric.idle_time) ? Number(metric.idle_time) : 0;
63747
+ const activeTimeSeconds = Number.isFinite(metric.active_time_seconds) ? Number(metric.active_time_seconds) : void 0;
63748
+ let availableTimeSeconds = Number.isFinite(metric.available_time_seconds) ? Number(metric.available_time_seconds) : void 0;
63749
+ if (availableTimeSeconds === void 0) {
63750
+ const activeFallback = activeTimeSeconds ?? 0;
63751
+ if (activeFallback > 0 || idleTimeSecondsRaw > 0) {
63752
+ availableTimeSeconds = activeFallback + idleTimeSecondsRaw;
63753
+ }
63754
+ }
63755
+ const idleTimeSeconds = idleTimeSecondsRaw;
63756
+ const computedUptimeEfficiency = (() => {
63757
+ const availableMinutes = (availableTimeSeconds ?? 0) / 60;
63758
+ if (!availableMinutes || availableMinutes <= 0) return 0;
63759
+ const idleMinutes = Math.min(Math.max(idleTimeSeconds / 60, 0), availableMinutes);
63760
+ const productiveMinutes = Math.max(
63761
+ (activeTimeSeconds ?? 0) / 60 || availableMinutes - idleMinutes,
63762
+ 0
63763
+ );
63764
+ return Math.round(productiveMinutes / availableMinutes * 100);
63765
+ })();
62216
63766
  const shiftData = {
62217
- efficiency: metric.avg_efficiency || 0,
63767
+ efficiency: monitoringMode === "uptime" ? Number.isFinite(metric.avg_efficiency) ? Number(metric.avg_efficiency) : computedUptimeEfficiency : Number.isFinite(metric.avg_efficiency) ? Number(metric.avg_efficiency) : 0,
62218
63768
  output: metric.total_output || 0,
62219
63769
  cycleTime: metric.avg_cycle_time || 0,
62220
63770
  pph: metric.avg_pph || 0,
62221
63771
  pphThreshold: metric.pph_threshold || 0,
62222
- idealOutput: metric.ideal_output || 0,
63772
+ idealOutput: Number(metric.ideal_output || 0),
63773
+ targetOutput: Number(metric.total_day_output || 0),
62223
63774
  rank: metric.workspace_rank || 0,
62224
- idleTime: metric.idle_time || 0,
62225
- hasData: true
63775
+ idleTime: idleTimeSeconds || 0,
63776
+ activeTimeSeconds,
63777
+ availableTimeSeconds,
63778
+ hasData: monitoringMode === "uptime" ? Boolean(availableTimeSeconds && availableTimeSeconds > 0) : true
62226
63779
  };
62227
63780
  dayEntry.shifts[metric.shift_id] = shiftData;
62228
63781
  });
@@ -62235,7 +63788,7 @@ var WorkspaceDetailView = ({
62235
63788
  sample: processedData.slice(0, 1)
62236
63789
  });
62237
63790
  setMonthlyData(processedData);
62238
- }, []);
63791
+ }, [monitoringMode]);
62239
63792
  React26.useEffect(() => {
62240
63793
  if (activeTab === "monthly_history" && workspaceId) {
62241
63794
  if (!supabase || !dashboardConfig || !dashboardConfig.supabaseUrl || !dashboardConfig.supabaseKey) {
@@ -69473,6 +71026,8 @@ exports.awardsService = awardsService;
69473
71026
  exports.buildDateKey = buildDateKey;
69474
71027
  exports.buildKPIsFromLineMetricsRow = buildKPIsFromLineMetricsRow;
69475
71028
  exports.buildShiftGroupsKey = buildShiftGroupsKey;
71029
+ exports.captureSentryException = captureSentryException;
71030
+ exports.captureSentryMessage = captureSentryMessage;
69476
71031
  exports.checkRateLimit = checkRateLimit2;
69477
71032
  exports.clearAllRateLimits = clearAllRateLimits2;
69478
71033
  exports.clearRateLimit = clearRateLimit2;