@optifye/dashboard-core 6.5.1 → 6.5.3

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
@@ -1905,7 +1905,29 @@ var WorkspaceHealthService = class _WorkspaceHealthService {
1905
1905
  throw error;
1906
1906
  }
1907
1907
  const processedData = (data || []).map((item) => this.processHealthStatus(item));
1908
- let filteredData = processedData;
1908
+ let uptimeMap = /* @__PURE__ */ new Map();
1909
+ if (options.companyId || data && data.length > 0) {
1910
+ const companyId = options.companyId || data[0]?.company_id;
1911
+ if (companyId) {
1912
+ try {
1913
+ uptimeMap = await this.calculateWorkspaceUptime(companyId);
1914
+ } catch (error2) {
1915
+ console.error("Error calculating uptime:", error2);
1916
+ }
1917
+ }
1918
+ }
1919
+ const dataWithUptime = processedData.map((workspace) => {
1920
+ const uptimeDetails = uptimeMap.get(workspace.workspace_id);
1921
+ if (uptimeDetails) {
1922
+ return {
1923
+ ...workspace,
1924
+ uptimePercentage: uptimeDetails.percentage,
1925
+ uptimeDetails
1926
+ };
1927
+ }
1928
+ return workspace;
1929
+ });
1930
+ let filteredData = dataWithUptime;
1909
1931
  try {
1910
1932
  const { data: enabledWorkspaces, error: workspaceError } = await supabase.from("workspaces").select("workspace_id, display_name").eq("enable", true);
1911
1933
  if (!workspaceError && enabledWorkspaces && enabledWorkspaces.length > 0) {
@@ -1981,13 +2003,31 @@ var WorkspaceHealthService = class _WorkspaceHealthService {
1981
2003
  const healthyWorkspaces = workspaces.filter((w) => w.status === "healthy").length;
1982
2004
  const unhealthyWorkspaces = workspaces.filter((w) => w.status === "unhealthy").length;
1983
2005
  const warningWorkspaces = workspaces.filter((w) => w.status === "warning").length;
1984
- const uptimePercentage = totalWorkspaces > 0 ? healthyWorkspaces / totalWorkspaces * 100 : 0;
2006
+ let uptimePercentage = 0;
2007
+ let totalDowntimeMinutes = 0;
2008
+ if (totalWorkspaces > 0) {
2009
+ const workspacesWithUptime = workspaces.filter((w) => w.uptimePercentage !== void 0 && w.uptimeDetails !== void 0);
2010
+ if (workspacesWithUptime.length > 0) {
2011
+ const totalUptime = workspacesWithUptime.reduce((sum, w) => sum + (w.uptimePercentage || 0), 0);
2012
+ uptimePercentage = totalUptime / workspacesWithUptime.length;
2013
+ totalDowntimeMinutes = workspacesWithUptime.reduce((sum, w) => {
2014
+ if (w.uptimeDetails) {
2015
+ const downtime = Math.max(0, w.uptimeDetails.expectedMinutes - w.uptimeDetails.actualMinutes);
2016
+ return sum + downtime;
2017
+ }
2018
+ return sum;
2019
+ }, 0);
2020
+ } else {
2021
+ uptimePercentage = healthyWorkspaces / totalWorkspaces * 100;
2022
+ }
2023
+ }
1985
2024
  return {
1986
2025
  totalWorkspaces,
1987
2026
  healthyWorkspaces,
1988
2027
  unhealthyWorkspaces,
1989
2028
  warningWorkspaces,
1990
2029
  uptimePercentage,
2030
+ totalDowntimeMinutes,
1991
2031
  lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
1992
2032
  };
1993
2033
  }
@@ -2067,6 +2107,155 @@ var WorkspaceHealthService = class _WorkspaceHealthService {
2067
2107
  clearCache() {
2068
2108
  this.cache.clear();
2069
2109
  }
2110
+ async calculateWorkspaceUptime(companyId) {
2111
+ const supabase = _getSupabaseInstance();
2112
+ if (!supabase) throw new Error("Supabase client not initialized");
2113
+ const dashboardConfig = _getDashboardConfigInstance();
2114
+ const timezone = dashboardConfig?.dateTimeConfig?.defaultTimezone || "UTC";
2115
+ const shiftConfig = dashboardConfig?.shiftConfig;
2116
+ const currentShiftInfo = getCurrentShift(timezone, shiftConfig);
2117
+ const currentDate = currentShiftInfo.date;
2118
+ const currentShiftId = currentShiftInfo.shiftId;
2119
+ const now2 = /* @__PURE__ */ new Date();
2120
+ const currentHour = now2.getHours();
2121
+ const currentMinute = now2.getMinutes();
2122
+ let shiftStartHour;
2123
+ let shiftEndHour;
2124
+ if (currentShiftId === 0) {
2125
+ shiftStartHour = 9;
2126
+ shiftEndHour = 18;
2127
+ } else {
2128
+ shiftStartHour = 20;
2129
+ shiftEndHour = 3;
2130
+ }
2131
+ let elapsedMinutes = 0;
2132
+ if (currentShiftId === 0) {
2133
+ if (currentHour >= shiftStartHour && currentHour < shiftEndHour) {
2134
+ elapsedMinutes = (currentHour - shiftStartHour) * 60 + currentMinute;
2135
+ } else if (currentHour >= shiftEndHour) {
2136
+ elapsedMinutes = (shiftEndHour - shiftStartHour) * 60;
2137
+ }
2138
+ } else {
2139
+ if (currentHour >= shiftStartHour) {
2140
+ elapsedMinutes = (currentHour - shiftStartHour) * 60 + currentMinute;
2141
+ } else if (currentHour < shiftEndHour) {
2142
+ elapsedMinutes = (24 - shiftStartHour + currentHour) * 60 + currentMinute;
2143
+ }
2144
+ }
2145
+ const tableName = `performance_metrics_${companyId.replace(/-/g, "_")}`;
2146
+ const query = `
2147
+ WITH workspace_uptime AS (
2148
+ SELECT
2149
+ workspace_id,
2150
+ workspace_display_name,
2151
+ idle_time_hourly,
2152
+ output_hourly,
2153
+ shift_start,
2154
+ shift_end
2155
+ FROM ${tableName}
2156
+ WHERE date = $1::date
2157
+ AND shift_id = $2
2158
+ ),
2159
+ calculated_uptime AS (
2160
+ SELECT
2161
+ workspace_id,
2162
+ workspace_display_name,
2163
+ -- Calculate actual minutes from hourly data
2164
+ (
2165
+ SELECT COALESCE(SUM(
2166
+ CASE
2167
+ WHEN jsonb_array_length(idle_time_hourly->key::text) >= 58 THEN 60
2168
+ WHEN jsonb_array_length(idle_time_hourly->key::text) > 0 THEN jsonb_array_length(idle_time_hourly->key::text)
2169
+ ELSE 0
2170
+ END
2171
+ ), 0)
2172
+ FROM jsonb_object_keys(idle_time_hourly) AS key
2173
+ WHERE key::int >= $3 AND key::int < $4
2174
+ ) +
2175
+ -- Add current hour's data if applicable
2176
+ CASE
2177
+ WHEN $4::int >= $3 AND $4::int < $5 THEN
2178
+ LEAST($6::int,
2179
+ COALESCE(jsonb_array_length(idle_time_hourly->$4::text), 0))
2180
+ ELSE 0
2181
+ END as actual_minutes
2182
+ FROM workspace_uptime
2183
+ )
2184
+ SELECT
2185
+ workspace_id,
2186
+ workspace_display_name,
2187
+ actual_minutes,
2188
+ $7::int as expected_minutes,
2189
+ ROUND(
2190
+ CASE
2191
+ WHEN $7::int > 0 THEN (actual_minutes::numeric / $7::numeric) * 100
2192
+ ELSE 100
2193
+ END, 1
2194
+ ) as uptime_percentage
2195
+ FROM calculated_uptime
2196
+ `;
2197
+ try {
2198
+ const { data, error } = await supabase.rpc("sql_query", {
2199
+ query_text: query,
2200
+ params: [
2201
+ currentDate,
2202
+ currentShiftId,
2203
+ shiftStartHour,
2204
+ currentHour,
2205
+ shiftEndHour,
2206
+ currentMinute,
2207
+ elapsedMinutes
2208
+ ]
2209
+ }).single();
2210
+ if (error) {
2211
+ const { data: queryData, error: queryError } = await supabase.from(tableName).select("workspace_id, workspace_display_name, idle_time_hourly, output_hourly").eq("date", currentDate).eq("shift_id", currentShiftId);
2212
+ if (queryError) {
2213
+ console.error("Error fetching performance metrics:", queryError);
2214
+ return /* @__PURE__ */ new Map();
2215
+ }
2216
+ const uptimeMap2 = /* @__PURE__ */ new Map();
2217
+ for (const record of queryData || []) {
2218
+ let actualMinutes = 0;
2219
+ const idleTimeHourly = record.idle_time_hourly || {};
2220
+ if (idleTimeHourly && typeof idleTimeHourly === "object") {
2221
+ for (const [hour, dataArray] of Object.entries(idleTimeHourly)) {
2222
+ const hourNum = parseInt(hour);
2223
+ if (hourNum >= shiftStartHour && hourNum < currentHour) {
2224
+ const arrayLength = Array.isArray(dataArray) ? dataArray.length : 0;
2225
+ actualMinutes += arrayLength >= 58 ? 60 : arrayLength;
2226
+ } else if (hourNum === currentHour && currentHour >= shiftStartHour) {
2227
+ const arrayLength = Array.isArray(dataArray) ? dataArray.length : 0;
2228
+ actualMinutes += Math.min(currentMinute, arrayLength);
2229
+ }
2230
+ }
2231
+ }
2232
+ const percentage = elapsedMinutes > 0 ? Math.round(actualMinutes / elapsedMinutes * 1e3) / 10 : 100;
2233
+ uptimeMap2.set(record.workspace_id, {
2234
+ expectedMinutes: elapsedMinutes,
2235
+ actualMinutes,
2236
+ percentage,
2237
+ lastCalculated: (/* @__PURE__ */ new Date()).toISOString()
2238
+ });
2239
+ }
2240
+ return uptimeMap2;
2241
+ }
2242
+ const uptimeMap = /* @__PURE__ */ new Map();
2243
+ if (Array.isArray(data)) {
2244
+ for (const record of data) {
2245
+ uptimeMap.set(record.workspace_id, {
2246
+ expectedMinutes: record.expected_minutes,
2247
+ actualMinutes: record.actual_minutes,
2248
+ percentage: record.uptime_percentage,
2249
+ lastCalculated: (/* @__PURE__ */ new Date()).toISOString()
2250
+ });
2251
+ }
2252
+ }
2253
+ return uptimeMap;
2254
+ } catch (error) {
2255
+ console.error("Error calculating workspace uptime:", error);
2256
+ return /* @__PURE__ */ new Map();
2257
+ }
2258
+ }
2070
2259
  };
2071
2260
  var workspaceHealthService = WorkspaceHealthService.getInstance();
2072
2261
 
@@ -4152,6 +4341,23 @@ var S3ClipsAPIClient = class {
4152
4341
  };
4153
4342
  });
4154
4343
  }
4344
+ /**
4345
+ * Batch fetch multiple videos in parallel
4346
+ */
4347
+ async batchFetchVideos(workspaceId, date, shiftId, requests) {
4348
+ const batchKey = `batch:${workspaceId}:${date}:${shiftId}:${requests.length}`;
4349
+ return this.deduplicate(batchKey, async () => {
4350
+ const response = await this.fetchWithAuth("/api/clips/batch", {
4351
+ workspaceId,
4352
+ date,
4353
+ shift: shiftId.toString(),
4354
+ requests,
4355
+ sopCategories: this.sopCategories
4356
+ });
4357
+ console.log(`[S3ClipsAPIClient] Batch fetched ${response.videos.length} videos in ${response.performance.duration}ms`);
4358
+ return response.videos;
4359
+ });
4360
+ }
4155
4361
  /**
4156
4362
  * Convert S3 URI to CloudFront URL
4157
4363
  * In the API client, URLs are already signed from the server
@@ -4435,6 +4641,23 @@ var S3ClipsService = class {
4435
4641
  );
4436
4642
  return result.videos;
4437
4643
  }
4644
+ /**
4645
+ * Batch fetch multiple videos in parallel
4646
+ */
4647
+ async batchFetchVideos(workspaceId, date, shiftId, requests) {
4648
+ try {
4649
+ const results = await this.apiClient.batchFetchVideos(
4650
+ workspaceId,
4651
+ date,
4652
+ shiftId,
4653
+ requests
4654
+ );
4655
+ return results.map((r2) => r2.video).filter((v) => v !== null);
4656
+ } catch (error) {
4657
+ console.error("[S3ClipsService] Error batch fetching videos:", error);
4658
+ return [];
4659
+ }
4660
+ }
4438
4661
  /**
4439
4662
  * Get videos page using pagination API
4440
4663
  */
@@ -19691,7 +19914,7 @@ var OptifyeLogoLoader = ({
19691
19914
  className: `${sizeClasses[size]} h-auto animate-pulse select-none pointer-events-none`
19692
19915
  }
19693
19916
  ),
19694
- message && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-3 text-gray-600 text-sm font-medium text-center", children: message })
19917
+ message && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-3 text-gray-600 text-base sm:text-sm font-medium text-center", children: message })
19695
19918
  ]
19696
19919
  }
19697
19920
  );
@@ -21602,8 +21825,8 @@ var VideoCard = React19__namespace.default.memo(({
21602
21825
  ] }) }),
21603
21826
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full h-full overflow-hidden bg-black", children: [
21604
21827
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-black z-0", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "animate-pulse flex flex-col items-center", children: [
21605
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Camera, { className: `${compact ? "w-4 h-4" : "w-6 h-6"} text-gray-500` }),
21606
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${compact ? "text-[10px]" : "text-xs"} text-gray-500 mt-1`, children: "Loading..." })
21828
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Camera, { className: `w-5 h-5 sm:${compact ? "w-4 h-4" : "w-6 h-6"} text-gray-500` }),
21829
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `text-[11px] sm:${compact ? "text-[10px]" : "text-xs"} text-gray-500 mt-1`, children: "Loading..." })
21607
21830
  ] }) }),
21608
21831
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute inset-0 z-10", children: [
21609
21832
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -21638,10 +21861,10 @@ var VideoCard = React19__namespace.default.memo(({
21638
21861
  }
21639
21862
  ) })
21640
21863
  ] }),
21641
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `absolute bottom-0 left-0 right-0 bg-black bg-opacity-60 ${compact ? "p-1" : "p-1.5"} flex justify-between items-center z-10`, children: [
21864
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `absolute bottom-0 left-0 right-0 bg-black bg-opacity-60 p-1.5 sm:${compact ? "p-1" : "p-1.5"} flex justify-between items-center z-10`, children: [
21642
21865
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
21643
21866
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Camera, { size: compact ? 10 : 12, className: "text-white" }),
21644
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: `text-white ${compact ? "text-[10px]" : "text-xs"} font-medium tracking-wide`, children: displayName })
21867
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: `text-white text-[11px] sm:${compact ? "text-[10px]" : "text-xs"} font-medium tracking-wide`, children: displayName })
21645
21868
  ] }),
21646
21869
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center ${compact ? "gap-1" : "gap-1.5"}`, children: [
21647
21870
  trendInfo && /* @__PURE__ */ jsxRuntime.jsx(
@@ -21653,7 +21876,7 @@ var VideoCard = React19__namespace.default.memo(({
21653
21876
  }
21654
21877
  ),
21655
21878
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: `${compact ? "w-1 h-1" : "w-1.5 h-1.5"} rounded-full bg-green-500` }),
21656
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: `text-white ${compact ? "text-[10px]" : "text-xs"}`, children: "Live" })
21879
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `text-white text-[11px] sm:${compact ? "text-[10px]" : "text-xs"}`, children: "Live" })
21657
21880
  ] })
21658
21881
  ] })
21659
21882
  ]
@@ -21863,10 +22086,10 @@ var VideoGridView = React19__namespace.default.memo(({
21863
22086
  view_type: "video_grid"
21864
22087
  });
21865
22088
  }, []);
21866
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `relative overflow-hidden h-full w-full ${className}`, children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: "h-full w-full p-2", children: /* @__PURE__ */ jsxRuntime.jsx(
22089
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `relative overflow-hidden h-full w-full ${className}`, children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: "h-full w-full p-3 sm:p-2", children: /* @__PURE__ */ jsxRuntime.jsx(
21867
22090
  "div",
21868
22091
  {
21869
- className: "grid h-full w-full gap-2",
22092
+ className: "grid h-full w-full gap-3 sm:gap-2",
21870
22093
  style: {
21871
22094
  gridTemplateColumns: `repeat(${gridCols}, 1fr)`,
21872
22095
  gridTemplateRows: `repeat(${gridRows}, 1fr)`,
@@ -22651,15 +22874,15 @@ var BreakNotificationPopup = ({
22651
22874
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-2 h-2 bg-amber-500 rounded-full animate-pulse flex-shrink-0 mt-2" }),
22652
22875
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
22653
22876
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-1", children: [
22654
- /* @__PURE__ */ jsxRuntime.jsx("h4", { className: "font-semibold text-sm text-gray-900", children: breakItem.remarks || "Break" }),
22655
- (activeBreaks.length > 1 || lineNames[breakItem.lineId]) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-gray-500 mt-0.5", children: lineNames[breakItem.lineId] || `Line ${breakItem.lineId.substring(0, 8)}` })
22877
+ /* @__PURE__ */ jsxRuntime.jsx("h4", { className: "font-semibold text-base sm:text-sm text-gray-900", children: breakItem.remarks || "Break" }),
22878
+ (activeBreaks.length > 1 || lineNames[breakItem.lineId]) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm sm:text-xs text-gray-500 mt-0.5", children: lineNames[breakItem.lineId] || `Line ${breakItem.lineId.substring(0, 8)}` })
22656
22879
  ] }),
22657
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-2", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-gray-600 font-medium", children: [
22880
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-2", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-sm sm:text-xs text-gray-600 font-medium", children: [
22658
22881
  breakItem.startTime,
22659
22882
  " - ",
22660
22883
  breakItem.endTime
22661
22884
  ] }) }),
22662
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-2", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-gray-500", children: [
22885
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-2", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-sm sm:text-xs text-gray-500", children: [
22663
22886
  formatTime3(breakItem.elapsedMinutes),
22664
22887
  " / ",
22665
22888
  formatTime3(breakItem.duration)
@@ -23377,7 +23600,7 @@ var TimeDisplay = ({ className, variant = "default" }) => {
23377
23600
  className: `flex items-center space-x-1.5 bg-white/60 backdrop-blur-sm px-2 py-0.5 rounded-md shadow-xs ${className || ""}`,
23378
23601
  children: [
23379
23602
  /* @__PURE__ */ jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-3 w-3 text-[var(--primary-DEFAULT)]", viewBox: "0 0 20 20", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { fillRule: "evenodd", d: "M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z", clipRule: "evenodd" }) }),
23380
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[11px] font-medium text-gray-800 tabular-nums tracking-tight", children: [
23603
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs sm:text-[11px] font-medium text-gray-800 tabular-nums tracking-tight", children: [
23381
23604
  time2,
23382
23605
  " ",
23383
23606
  timeSuffix
@@ -27024,44 +27247,73 @@ var BottlenecksContent = ({
27024
27247
  if (indicesToLoad.length === 0) return;
27025
27248
  console.log(`[ensureVideosLoaded] Preloading ${indicesToLoad.length} videos around index ${centerIndex}: [${indicesToLoad.join(", ")}]`);
27026
27249
  indicesToLoad.forEach((idx) => loadingVideosRef.current.add(idx));
27027
- const loadPromises = indicesToLoad.map(async (index) => {
27028
- try {
27029
- let video = null;
27030
- if (!video) {
27031
- const operationalDate = date || getOperationalDate();
27032
- const shiftStr = effectiveShift;
27033
- video = await s3ClipsService.getClipByIndex(
27250
+ const operationalDate = date || getOperationalDate();
27251
+ const shiftStr = effectiveShift;
27252
+ console.log(`[ensureVideosLoaded] Batch fetching ${indicesToLoad.length} videos in parallel`);
27253
+ try {
27254
+ const batchRequests = indicesToLoad.map((index) => ({
27255
+ category: effectiveFilter,
27256
+ index,
27257
+ includeMetadata: false
27258
+ // No metadata during bulk preloading
27259
+ }));
27260
+ const videos = await s3ClipsService.batchFetchVideos(
27261
+ workspaceId,
27262
+ operationalDate,
27263
+ shiftStr,
27264
+ batchRequests
27265
+ );
27266
+ if (videos.length > 0 && isMountedRef.current) {
27267
+ videos.forEach((video, idx) => {
27268
+ if (video) {
27269
+ setAllVideos((prev) => {
27270
+ const exists = prev.some((v) => v.id === video.id);
27271
+ if (!exists) {
27272
+ return [...prev, video];
27273
+ }
27274
+ return prev;
27275
+ });
27276
+ const originalIndex = indicesToLoad[idx];
27277
+ loadedIndices.add(originalIndex);
27278
+ preloadVideoUrl(video.src);
27279
+ }
27280
+ });
27281
+ console.log(`[ensureVideosLoaded] Successfully loaded ${videos.length} videos in batch`);
27282
+ }
27283
+ } catch (error2) {
27284
+ console.error("[ensureVideosLoaded] Batch fetch failed:", error2);
27285
+ const loadPromises = indicesToLoad.map(async (index) => {
27286
+ try {
27287
+ const video = await s3ClipsService.getClipByIndex(
27034
27288
  workspaceId,
27035
27289
  operationalDate,
27036
27290
  shiftStr,
27037
27291
  effectiveFilter,
27038
27292
  index,
27039
27293
  true,
27040
- // includeCycleTime - OK for preloading
27294
+ // includeCycleTime
27041
27295
  false
27042
- // includeMetadata - NO metadata during bulk preloading to prevent flooding
27296
+ // includeMetadata
27043
27297
  );
27298
+ if (video && isMountedRef.current) {
27299
+ setAllVideos((prev) => {
27300
+ const exists = prev.some((v) => v.id === video.id);
27301
+ if (!exists) {
27302
+ return [...prev, video];
27303
+ }
27304
+ return prev;
27305
+ });
27306
+ loadedIndices.add(index);
27307
+ preloadVideoUrl(video.src);
27308
+ }
27309
+ } catch (err) {
27310
+ console.warn(`[ensureVideosLoaded] Failed to load video at index ${index}:`, err);
27044
27311
  }
27045
- if (video && isMountedRef.current) {
27046
- setAllVideos((prev) => {
27047
- const exists = prev.some((v) => v.id === video.id);
27048
- if (!exists) {
27049
- return [...prev, video];
27050
- }
27051
- return prev;
27052
- });
27053
- loadedIndices.add(index);
27054
- preloadVideoUrl(video.src);
27055
- }
27056
- } catch (error2) {
27057
- console.warn(`[ensureVideosLoaded] Failed to load video at index ${index}:`, error2);
27058
- } finally {
27059
- loadingVideosRef.current.delete(index);
27060
- }
27061
- });
27062
- Promise.all(loadPromises).catch((err) => {
27063
- console.warn("[ensureVideosLoaded] Some videos failed to preload:", err);
27064
- });
27312
+ });
27313
+ await Promise.all(loadPromises);
27314
+ } finally {
27315
+ indicesToLoad.forEach((idx) => loadingVideosRef.current.delete(idx));
27316
+ }
27065
27317
  }, [s3ClipsService, workspaceId, clipCounts, sopCategories, date, effectiveShift]);
27066
27318
  const loadFirstVideoForCategory = React19.useCallback(async (category) => {
27067
27319
  if (!workspaceId || !s3ClipsService || !isMountedRef.current) return;
@@ -28024,7 +28276,7 @@ var WorkspaceGridItem = React19__namespace.default.memo(({
28024
28276
  isVeryLowEfficiency && !isInactive && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute -top-10 left-1/2 -translate-x-1/2 z-30", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
28025
28277
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute -inset-1 bg-red-400/50 rounded-full blur-sm animate-pulse" }),
28026
28278
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute -inset-0.5 bg-red-500/30 rounded-full blur-md animate-ping [animation-duration:1.5s]" }),
28027
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-[#E34329] w-9 h-9 rounded-full flex items-center justify-center text-white font-bold text-lg shadow-lg ring-2 ring-red-400/40 border border-red-400/80 animate-pulse", children: "!" })
28279
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-[#E34329] w-8 h-8 sm:w-9 sm:h-9 rounded-full flex items-center justify-center text-white font-bold text-base sm:text-lg shadow-lg ring-2 ring-red-400/40 border border-red-400/80 animate-pulse", children: "!" })
28028
28280
  ] }) }),
28029
28281
  /* @__PURE__ */ jsxRuntime.jsx(
28030
28282
  "button",
@@ -28033,14 +28285,14 @@ var WorkspaceGridItem = React19__namespace.default.memo(({
28033
28285
  className: `${styles2} ${colorClass} ${isBottleneck ? "ring-2 ring-red-500/70" : ""} ${isVeryLowEfficiency ? "ring-2 ring-red-500/50" : ""} ${isInactive ? "bg-gray-200" : ""} shadow-lg`,
28034
28286
  "aria-label": isInactive ? `Inactive workspace ${workspaceNumber}` : `View details for workspace ${workspaceNumber}`,
28035
28287
  title: isInactive ? `Inactive: ${getWorkspaceDisplayName(data.workspace_name, data.line_id)}` : getWorkspaceDisplayName(data.workspace_name, data.line_id),
28036
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: `font-semibold tracking-wide text-[min(4vw,2rem)] uppercase ${isInactive ? "text-gray-400" : "text-white"} drop-shadow-sm`, children: workspaceNumber })
28288
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: `font-semibold tracking-wide text-lg sm:text-xl md:text-[min(4vw,2rem)] uppercase ${isInactive ? "text-gray-400" : "text-white"} drop-shadow-sm`, children: workspaceNumber })
28037
28289
  }
28038
28290
  ),
28039
28291
  arrow && !isInactive && /* @__PURE__ */ jsxRuntime.jsx(
28040
28292
  "div",
28041
28293
  {
28042
28294
  className: `absolute left-1/2 -translate-x-1/2 ${arrowPosition}
28043
- text-[min(3.5vw,2.25rem)] font-bold tracking-tight ${arrowColor} drop-shadow-sm`,
28295
+ text-base sm:text-lg md:text-[min(3.5vw,2.25rem)] font-bold tracking-tight ${arrowColor} drop-shadow-sm`,
28044
28296
  style: { bottom: "-2.5rem", lineHeight: 1, display: "flex", alignItems: "center", justifyContent: "center" },
28045
28297
  children: arrow
28046
28298
  }
@@ -28070,11 +28322,11 @@ var WorkspaceGrid = React19__namespace.default.memo(({
28070
28322
  }, [workspaces.length]);
28071
28323
  const { VideoGridView: VideoGridViewComponent } = useRegistry();
28072
28324
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `relative w-full h-full overflow-hidden ${className}`, children: [
28073
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute top-0 left-2 sm:left-4 right-2 sm:right-8 z-20", children: [
28074
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-row items-center justify-between py-1 sm:py-1.5 gap-2", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden sm:block", children: /* @__PURE__ */ jsxRuntime.jsx(Legend6, {}) }) }),
28075
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sm:hidden mt-1", children: /* @__PURE__ */ jsxRuntime.jsx(Legend6, {}) })
28325
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute top-0 left-4 sm:left-4 right-4 sm:right-8 z-20", children: [
28326
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-row items-center justify-between py-2 sm:py-1.5 gap-2", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden sm:block", children: /* @__PURE__ */ jsxRuntime.jsx(Legend6, {}) }) }),
28327
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sm:hidden mt-2", children: /* @__PURE__ */ jsxRuntime.jsx(Legend6, {}) })
28076
28328
  ] }),
28077
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-10 sm:top-16 left-0 right-0 bottom-0", children: /* @__PURE__ */ jsxRuntime.jsx(
28329
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-12 sm:top-16 left-0 right-0 bottom-0", children: /* @__PURE__ */ jsxRuntime.jsx(
28078
28330
  VideoGridViewComponent,
28079
28331
  {
28080
28332
  workspaces,
@@ -28289,8 +28541,8 @@ var KPICard = ({
28289
28541
  const cardClasses = clsx(
28290
28542
  // Base classes
28291
28543
  "rounded-lg transition-all duration-200",
28292
- // Sizing based on compact mode - extra compact on mobile
28293
- "p-1.5 sm:p-3 md:p-4",
28544
+ // Sizing based on compact mode - better padding on mobile
28545
+ "p-2.5 sm:p-3 md:p-4",
28294
28546
  // Variant-specific styling
28295
28547
  {
28296
28548
  "bg-white/50 backdrop-blur-sm border border-gray-200/60 shadow-sm hover:shadow-md dark:bg-gray-800/50 dark:border-gray-700/60": style.variant === "default",
@@ -28298,8 +28550,8 @@ var KPICard = ({
28298
28550
  "bg-blue-50 border border-blue-200 dark:bg-blue-900/20 dark:border-blue-800/30": style.variant === "filled",
28299
28551
  "bg-gray-50/50 dark:bg-gray-800/30": style.variant === "subtle"
28300
28552
  },
28301
- // Width for src matching - compact on mobile, flexible on small screens
28302
- !className?.includes("w-") && "w-[120px] sm:w-[180px] md:w-[220px]",
28553
+ // Width for src matching - better mobile width, flexible on small screens
28554
+ !className?.includes("w-") && "w-[110px] sm:w-[180px] md:w-[220px]",
28303
28555
  // Interactive styling if onClick is provided
28304
28556
  onClick && "cursor-pointer hover:scale-[1.01] active:scale-[0.99]",
28305
28557
  // Loading state
@@ -28318,14 +28570,14 @@ var KPICard = ({
28318
28570
  children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
28319
28571
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: clsx(
28320
28572
  "font-medium text-gray-500 dark:text-gray-400",
28321
- "text-[9px] sm:text-xs md:text-sm",
28322
- "mb-0.5 sm:mb-1 md:mb-2",
28323
- "uppercase tracking-wider"
28573
+ "text-[10px] sm:text-xs md:text-sm",
28574
+ "mb-1 sm:mb-1 md:mb-2",
28575
+ "uppercase tracking-wide sm:tracking-wider"
28324
28576
  ), children: title }),
28325
28577
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline gap-0.5 sm:gap-2 flex-wrap", children: [
28326
28578
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: clsx(
28327
28579
  "font-bold text-gray-900 dark:text-gray-50",
28328
- "text-sm sm:text-xl md:text-2xl"
28580
+ "text-base sm:text-xl md:text-2xl"
28329
28581
  ), children: isLoading ? "\u2014" : formattedValue }),
28330
28582
  suffix && !isLoading && /* @__PURE__ */ jsxRuntime.jsx("span", { className: clsx(
28331
28583
  "font-medium text-gray-600 dark:text-gray-300",
@@ -28500,35 +28752,46 @@ var KPISection = React19.memo(({
28500
28752
  const outputDifference = kpis.outputProgress.current - kpis.outputProgress.idealOutput;
28501
28753
  const outputIsOnTarget = outputDifference >= 0;
28502
28754
  if (useSrcLayout) {
28503
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex gap-1 sm:gap-3 overflow-x-auto sm:overflow-visible pb-1 sm:pb-0 ${className || ""}`, children: [
28504
- /* @__PURE__ */ jsxRuntime.jsx(
28505
- KPICard,
28506
- {
28507
- title: "Underperforming",
28508
- value: "2/3",
28509
- change: 0
28510
- }
28511
- ),
28512
- /* @__PURE__ */ jsxRuntime.jsx(
28513
- KPICard,
28514
- {
28515
- title: "Efficiency",
28516
- value: kpis.efficiency.value,
28517
- change: kpis.efficiency.change,
28518
- suffix: "%"
28519
- }
28520
- ),
28521
- /* @__PURE__ */ jsxRuntime.jsx(
28522
- KPICard,
28523
- {
28524
- title: "Output Progress",
28525
- value: `${kpis.outputProgress.current}/${kpis.outputProgress.target}`,
28526
- change: kpis.outputProgress.change,
28527
- outputDifference,
28528
- showOutputDetails: true
28529
- }
28530
- )
28531
- ] });
28755
+ return /* @__PURE__ */ jsxRuntime.jsxs(
28756
+ "div",
28757
+ {
28758
+ className: `flex gap-2 sm:gap-3 overflow-x-auto sm:overflow-visible pb-2 sm:pb-0 ${className || ""}`,
28759
+ style: {
28760
+ scrollbarWidth: "none",
28761
+ msOverflowStyle: "none",
28762
+ WebkitScrollbar: { display: "none" }
28763
+ },
28764
+ children: [
28765
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(
28766
+ KPICard,
28767
+ {
28768
+ title: "Underperforming",
28769
+ value: "2/3",
28770
+ change: 0
28771
+ }
28772
+ ) }),
28773
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(
28774
+ KPICard,
28775
+ {
28776
+ title: "Efficiency",
28777
+ value: kpis.efficiency.value,
28778
+ change: kpis.efficiency.change,
28779
+ suffix: "%"
28780
+ }
28781
+ ) }),
28782
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(
28783
+ KPICard,
28784
+ {
28785
+ title: "Output Progress",
28786
+ value: `${kpis.outputProgress.current}/${kpis.outputProgress.target}`,
28787
+ change: kpis.outputProgress.change,
28788
+ outputDifference,
28789
+ showOutputDetails: true
28790
+ }
28791
+ ) })
28792
+ ]
28793
+ }
28794
+ );
28532
28795
  }
28533
28796
  const kpiCardData = [
28534
28797
  {
@@ -28661,6 +28924,19 @@ var WorkspaceHealthCard = ({
28661
28924
  const formatTimeAgo = (timeString) => {
28662
28925
  return timeString.replace("about ", "").replace(" ago", "");
28663
28926
  };
28927
+ const formatDowntime = (uptimeDetails) => {
28928
+ if (!uptimeDetails) return "";
28929
+ const downtimeMinutes = Math.max(0, uptimeDetails.expectedMinutes - uptimeDetails.actualMinutes);
28930
+ if (downtimeMinutes === 0) return "No downtime";
28931
+ if (downtimeMinutes < 1) return "< 1 min downtime";
28932
+ if (downtimeMinutes < 60) return `${downtimeMinutes} min downtime`;
28933
+ const hours = Math.floor(downtimeMinutes / 60);
28934
+ const minutes = downtimeMinutes % 60;
28935
+ if (minutes === 0) {
28936
+ return `${hours} hr downtime`;
28937
+ }
28938
+ return `${hours} hr ${minutes} min downtime`;
28939
+ };
28664
28940
  return /* @__PURE__ */ jsxRuntime.jsx(
28665
28941
  Card2,
28666
28942
  {
@@ -28694,13 +28970,32 @@ var WorkspaceHealthCard = ({
28694
28970
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: config.statusText })
28695
28971
  ] })
28696
28972
  ] }),
28697
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-1", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
28698
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "h-3.5 w-3.5 text-gray-400" }),
28699
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm text-gray-600 dark:text-gray-400 whitespace-nowrap", children: [
28700
- "Last seen: ",
28701
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: formatTimeAgo(workspace.timeSinceLastUpdate) })
28702
- ] })
28703
- ] }) }) })
28973
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
28974
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
28975
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "h-3.5 w-3.5 text-gray-400" }),
28976
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm text-gray-600 dark:text-gray-400 whitespace-nowrap", children: [
28977
+ "Last seen: ",
28978
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: formatTimeAgo(workspace.timeSinceLastUpdate) })
28979
+ ] })
28980
+ ] }),
28981
+ workspace.uptimePercentage !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
28982
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Activity, { className: "h-3.5 w-3.5 text-gray-400" }),
28983
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm text-gray-600 dark:text-gray-400 whitespace-nowrap", children: [
28984
+ "Uptime today: ",
28985
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: clsx(
28986
+ "font-medium",
28987
+ workspace.uptimePercentage >= 97 ? "text-green-600 dark:text-green-400" : workspace.uptimePercentage >= 90 ? "text-yellow-600 dark:text-yellow-400" : "text-red-600 dark:text-red-400"
28988
+ ), children: [
28989
+ workspace.uptimePercentage.toFixed(1),
28990
+ "%"
28991
+ ] })
28992
+ ] })
28993
+ ] }),
28994
+ workspace.uptimeDetails && workspace.uptimeDetails.expectedMinutes > workspace.uptimeDetails.actualMinutes && workspace.uptimePercentage !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: clsx(
28995
+ "inline-flex items-center px-2 py-0.5 rounded text-xs font-medium",
28996
+ workspace.uptimePercentage >= 97 ? "bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400" : workspace.uptimePercentage >= 90 ? "bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-400" : "bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-400"
28997
+ ), children: formatDowntime(workspace.uptimeDetails) }) })
28998
+ ] })
28704
28999
  ] })
28705
29000
  }
28706
29001
  );
@@ -28771,6 +29066,20 @@ var CompactWorkspaceHealthCard = ({
28771
29066
  ] })
28772
29067
  ] }),
28773
29068
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
29069
+ workspace.uptimePercentage !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
29070
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: clsx(
29071
+ "text-xs font-medium",
29072
+ workspace.uptimePercentage >= 97 ? "text-green-600 dark:text-green-400" : workspace.uptimePercentage >= 90 ? "text-yellow-600 dark:text-yellow-400" : "text-red-600 dark:text-red-400"
29073
+ ), children: [
29074
+ workspace.uptimePercentage.toFixed(1),
29075
+ "%"
29076
+ ] }),
29077
+ workspace.uptimeDetails && workspace.uptimeDetails.expectedMinutes > workspace.uptimeDetails.actualMinutes && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-400 dark:text-gray-400", children: "\u2022" }),
29078
+ workspace.uptimeDetails && workspace.uptimeDetails.expectedMinutes > workspace.uptimeDetails.actualMinutes && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: [
29079
+ Math.max(0, workspace.uptimeDetails.expectedMinutes - workspace.uptimeDetails.actualMinutes),
29080
+ "m down"
29081
+ ] })
29082
+ ] }),
28774
29083
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: workspace.timeSinceLastUpdate }),
28775
29084
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: clsx("h-2 w-2 rounded-full", config.dot) })
28776
29085
  ] })
@@ -28994,18 +29303,18 @@ var DashboardHeader = React19.memo(({ lineTitle, className = "", headerControls
28994
29303
  };
28995
29304
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex flex-row items-center justify-between w-full ${className}`, children: [
28996
29305
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
28997
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 sm:gap-2 md:gap-3", children: [
28998
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-base sm:text-xl md:text-2xl lg:text-3xl font-bold text-gray-800 tracking-tight leading-none", children: lineTitle }),
28999
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-1 w-1 sm:h-1.5 sm:w-1.5 md:h-2 md:w-2 rounded-full bg-green-500 animate-pulse ring-1 sm:ring-2 ring-green-500/30 ring-offset-1" })
29306
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 sm:gap-2 md:gap-3", children: [
29307
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-lg sm:text-xl md:text-2xl lg:text-3xl font-bold text-gray-800 tracking-tight leading-none", children: lineTitle }),
29308
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-1.5 w-1.5 sm:h-1.5 sm:w-1.5 md:h-2 md:w-2 rounded-full bg-green-500 animate-pulse ring-2 sm:ring-2 ring-green-500/30 ring-offset-1" })
29000
29309
  ] }),
29001
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 inline-flex items-center gap-3", children: [
29002
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-sm font-medium text-gray-600", children: [
29310
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 inline-flex items-center gap-2 sm:gap-3", children: [
29311
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs sm:text-sm font-medium text-gray-600", children: [
29003
29312
  /* @__PURE__ */ jsxRuntime.jsx(ISTTimer2, {}),
29004
29313
  " IST"
29005
29314
  ] }),
29006
29315
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center gap-1", children: [
29007
29316
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-600", children: getShiftIcon() }),
29008
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm font-medium text-gray-600", children: [
29317
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs sm:text-sm font-medium text-gray-600", children: [
29009
29318
  getShiftName(),
29010
29319
  " Shift"
29011
29320
  ] })
@@ -29016,9 +29325,9 @@ var DashboardHeader = React19.memo(({ lineTitle, className = "", headerControls
29016
29325
  ] });
29017
29326
  });
29018
29327
  DashboardHeader.displayName = "DashboardHeader";
29019
- var NoWorkspaceData = React19.memo(({ message = "No workspace data available", className = "" }) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: `flex h-full items-center justify-center ${className}`, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-lg bg-white p-4 shadow-md", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center space-x-2 text-gray-500", children: [
29020
- /* @__PURE__ */ jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", viewBox: "0 0 20 20", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { fillRule: "evenodd", d: "M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z", clipRule: "evenodd" }) }),
29021
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: message })
29328
+ var NoWorkspaceData = React19.memo(({ message = "No workspace data available", className = "" }) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: `flex h-full items-center justify-center ${className}`, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-lg bg-white p-5 sm:p-4 shadow-md", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center space-x-3 sm:space-x-2 text-gray-500", children: [
29329
+ /* @__PURE__ */ jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-6 w-6 sm:h-5 sm:w-5", viewBox: "0 0 20 20", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { fillRule: "evenodd", d: "M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z", clipRule: "evenodd" }) }),
29330
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-base sm:text-sm", children: message })
29022
29331
  ] }) }) }));
29023
29332
  NoWorkspaceData.displayName = "NoWorkspaceData";
29024
29333
  var WorkspaceMonthlyDataFetcher = ({
@@ -29143,10 +29452,10 @@ var HamburgerButton = ({
29143
29452
  "button",
29144
29453
  {
29145
29454
  type: "button",
29146
- className: `md:hidden p-2 rounded-md text-gray-500 hover:text-gray-600 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 ${className}`,
29455
+ className: `md:hidden p-2.5 rounded-lg text-gray-600 hover:text-gray-900 hover:bg-gray-100 active:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition-colors ${className}`,
29147
29456
  onClick,
29148
29457
  "aria-label": ariaLabel,
29149
- children: /* @__PURE__ */ jsxRuntime.jsx(outline.Bars3Icon, { className: "w-6 h-6" })
29458
+ children: /* @__PURE__ */ jsxRuntime.jsx(outline.Bars3Icon, { className: "w-7 h-7" })
29150
29459
  }
29151
29460
  );
29152
29461
  };
@@ -29677,28 +29986,189 @@ var SideNavBar = React19.memo(({
29677
29986
  }
29678
29987
  ) })
29679
29988
  ] });
29989
+ const MobileNavigationContent = () => {
29990
+ const isActive = (path) => {
29991
+ if (path === "/" && pathname === "/") return true;
29992
+ if (path !== "/" && pathname.startsWith(path)) return true;
29993
+ return false;
29994
+ };
29995
+ const getMobileButtonClass = (path) => {
29996
+ const active = isActive(path);
29997
+ return `w-full flex items-center gap-3 px-5 py-3.5 rounded-lg transition-colors active:scale-[0.98] ${active ? "bg-blue-50 text-blue-700" : "text-gray-700 hover:bg-gray-100 active:bg-gray-200"}`;
29998
+ };
29999
+ const getIconClass = (path) => {
30000
+ const active = isActive(path);
30001
+ return `w-7 h-7 ${active ? "text-blue-600" : "text-gray-600"}`;
30002
+ };
30003
+ const handleMobileNavClick = (handler) => {
30004
+ return () => {
30005
+ handler();
30006
+ onMobileMenuClose?.();
30007
+ };
30008
+ };
30009
+ return /* @__PURE__ */ jsxRuntime.jsxs("nav", { className: "px-5 py-6", children: [
30010
+ /* @__PURE__ */ jsxRuntime.jsxs(
30011
+ "button",
30012
+ {
30013
+ onClick: handleMobileNavClick(handleHomeClick),
30014
+ className: getMobileButtonClass("/"),
30015
+ "aria-label": "Home",
30016
+ children: [
30017
+ /* @__PURE__ */ jsxRuntime.jsx(outline.HomeIcon, { className: getIconClass("/") }),
30018
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-base font-medium", children: "Home" })
30019
+ ]
30020
+ }
30021
+ ),
30022
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-6 space-y-2", children: [
30023
+ /* @__PURE__ */ jsxRuntime.jsxs(
30024
+ "button",
30025
+ {
30026
+ onClick: handleMobileNavClick(handleLeaderboardClick),
30027
+ className: getMobileButtonClass("/leaderboard"),
30028
+ "aria-label": "Leaderboard",
30029
+ children: [
30030
+ /* @__PURE__ */ jsxRuntime.jsx(outline.TrophyIcon, { className: getIconClass("/leaderboard") }),
30031
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-base font-medium", children: "Leaderboard" })
30032
+ ]
30033
+ }
30034
+ ),
30035
+ /* @__PURE__ */ jsxRuntime.jsxs(
30036
+ "button",
30037
+ {
30038
+ onClick: handleMobileNavClick(handleKPIsClick),
30039
+ className: getMobileButtonClass("/kpis"),
30040
+ "aria-label": "Lines",
30041
+ children: [
30042
+ /* @__PURE__ */ jsxRuntime.jsx(outline.ChartBarIcon, { className: getIconClass("/kpis") }),
30043
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-base font-medium", children: "Lines" })
30044
+ ]
30045
+ }
30046
+ ),
30047
+ /* @__PURE__ */ jsxRuntime.jsxs(
30048
+ "button",
30049
+ {
30050
+ onClick: handleMobileNavClick(handleTargetsClick),
30051
+ className: getMobileButtonClass("/targets"),
30052
+ "aria-label": "Targets",
30053
+ children: [
30054
+ /* @__PURE__ */ jsxRuntime.jsx(outline.AdjustmentsHorizontalIcon, { className: getIconClass("/targets") }),
30055
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-base font-medium", children: "Targets" })
30056
+ ]
30057
+ }
30058
+ ),
30059
+ /* @__PURE__ */ jsxRuntime.jsxs(
30060
+ "button",
30061
+ {
30062
+ onClick: handleMobileNavClick(handleShiftsClick),
30063
+ className: getMobileButtonClass("/shifts"),
30064
+ "aria-label": "Shift Management",
30065
+ children: [
30066
+ /* @__PURE__ */ jsxRuntime.jsx(outline.ClockIcon, { className: getIconClass("/shifts") }),
30067
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-base font-medium", children: "Shifts" })
30068
+ ]
30069
+ }
30070
+ ),
30071
+ skuEnabled && /* @__PURE__ */ jsxRuntime.jsxs(
30072
+ "button",
30073
+ {
30074
+ onClick: handleMobileNavClick(handleSKUsClick),
30075
+ className: getMobileButtonClass("/skus"),
30076
+ "aria-label": "SKU Management",
30077
+ children: [
30078
+ /* @__PURE__ */ jsxRuntime.jsx(outline.CubeIcon, { className: getIconClass("/skus") }),
30079
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-base font-medium", children: "SKUs" })
30080
+ ]
30081
+ }
30082
+ ),
30083
+ /* @__PURE__ */ jsxRuntime.jsxs(
30084
+ "button",
30085
+ {
30086
+ onClick: handleMobileNavClick(handleAIAgentClick),
30087
+ className: getMobileButtonClass("/ai-agent"),
30088
+ "aria-label": "AI Manufacturing Expert",
30089
+ children: [
30090
+ /* @__PURE__ */ jsxRuntime.jsx(outline.SparklesIcon, { className: getIconClass("/ai-agent") }),
30091
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-base font-medium", children: "Axel AI" })
30092
+ ]
30093
+ }
30094
+ ),
30095
+ /* @__PURE__ */ jsxRuntime.jsxs(
30096
+ "button",
30097
+ {
30098
+ onClick: handleMobileNavClick(handleHelpClick),
30099
+ className: getMobileButtonClass("/help"),
30100
+ "aria-label": "Help & Support",
30101
+ children: [
30102
+ /* @__PURE__ */ jsxRuntime.jsx(outline.QuestionMarkCircleIcon, { className: getIconClass("/help") }),
30103
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-base font-medium", children: "Help" })
30104
+ ]
30105
+ }
30106
+ ),
30107
+ /* @__PURE__ */ jsxRuntime.jsxs(
30108
+ "button",
30109
+ {
30110
+ onClick: handleMobileNavClick(handleHealthClick),
30111
+ className: getMobileButtonClass("/health"),
30112
+ "aria-label": "System Health",
30113
+ children: [
30114
+ /* @__PURE__ */ jsxRuntime.jsx(outline.HeartIcon, { className: getIconClass("/health") }),
30115
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-base font-medium", children: "System Health" })
30116
+ ]
30117
+ }
30118
+ )
30119
+ ] }),
30120
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-8 pt-6 border-t border-gray-200", children: /* @__PURE__ */ jsxRuntime.jsxs(
30121
+ "button",
30122
+ {
30123
+ onClick: handleMobileNavClick(handleProfileClick),
30124
+ className: getMobileButtonClass("/profile"),
30125
+ "aria-label": "Profile & Settings",
30126
+ children: [
30127
+ /* @__PURE__ */ jsxRuntime.jsx(outline.UserCircleIcon, { className: getIconClass("/profile") }),
30128
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-base font-medium", children: "Profile" })
30129
+ ]
30130
+ }
30131
+ ) })
30132
+ ] });
30133
+ };
29680
30134
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
29681
30135
  /* @__PURE__ */ jsxRuntime.jsx("aside", { className: `hidden md:flex w-20 h-screen bg-white shadow-lg border-r border-gray-100 flex-col items-center fixed ${className}`, children: /* @__PURE__ */ jsxRuntime.jsx(NavigationContent, {}) }),
29682
- isMobileMenuOpen && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
30136
+ /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
29683
30137
  /* @__PURE__ */ jsxRuntime.jsx(
29684
30138
  "div",
29685
30139
  {
29686
- className: "md:hidden fixed inset-0 bg-black bg-opacity-50 z-40",
30140
+ className: `md:hidden fixed inset-0 bg-black/60 backdrop-blur-sm z-40 transition-opacity duration-300 ease-in-out ${isMobileMenuOpen ? "opacity-100 pointer-events-auto" : "opacity-0 pointer-events-none"}`,
29687
30141
  onClick: onMobileMenuClose,
29688
30142
  "aria-hidden": "true"
29689
30143
  }
29690
30144
  ),
29691
- /* @__PURE__ */ jsxRuntime.jsxs("aside", { className: "md:hidden fixed inset-y-0 left-0 w-20 bg-white shadow-lg border-r border-gray-100 flex flex-col items-center z-50", children: [
29692
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-4 right-4", children: /* @__PURE__ */ jsxRuntime.jsx(
29693
- "button",
29694
- {
29695
- onClick: onMobileMenuClose,
29696
- className: "p-2 rounded-md text-gray-400 hover:text-gray-600 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500",
29697
- "aria-label": "Close menu",
29698
- children: /* @__PURE__ */ jsxRuntime.jsx(outline.XMarkIcon, { className: "w-5 h-5" })
29699
- }
29700
- ) }),
29701
- /* @__PURE__ */ jsxRuntime.jsx(NavigationContent, {})
30145
+ /* @__PURE__ */ jsxRuntime.jsxs("aside", { className: `md:hidden fixed inset-y-0 left-0 w-72 xs:w-80 bg-white shadow-2xl flex flex-col z-50 transform transition-transform duration-300 ease-in-out ${isMobileMenuOpen ? "translate-x-0" : "-translate-x-full"}`, children: [
30146
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-5 py-4 border-b border-gray-200 bg-gradient-to-r from-blue-50 to-white", children: [
30147
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsxRuntime.jsx(
30148
+ "img",
30149
+ {
30150
+ src: "/optifye-logo.png",
30151
+ alt: "Optifye",
30152
+ className: "w-11 h-11 object-contain",
30153
+ onError: (e) => {
30154
+ e.currentTarget.style.display = "none";
30155
+ if (e.currentTarget.parentElement) {
30156
+ e.currentTarget.parentElement.innerHTML = '<span class="text-blue-600 font-bold text-xl">Optifye</span>';
30157
+ }
30158
+ }
30159
+ }
30160
+ ) }),
30161
+ /* @__PURE__ */ jsxRuntime.jsx(
30162
+ "button",
30163
+ {
30164
+ onClick: onMobileMenuClose,
30165
+ className: "p-2.5 rounded-lg text-gray-600 hover:text-gray-900 hover:bg-gray-100 active:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-blue-500 transition-all active:scale-95",
30166
+ "aria-label": "Close menu",
30167
+ children: /* @__PURE__ */ jsxRuntime.jsx(outline.XMarkIcon, { className: "w-7 h-7" })
30168
+ }
30169
+ )
30170
+ ] }),
30171
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 overflow-y-auto", children: /* @__PURE__ */ jsxRuntime.jsx(MobileNavigationContent, {}) })
29702
30172
  ] })
29703
30173
  ] })
29704
30174
  ] });
@@ -29790,17 +30260,39 @@ var MainLayout = ({
29790
30260
  logo
29791
30261
  }) => {
29792
30262
  const router$1 = router.useRouter();
30263
+ const [isMobileMenuOpen, setIsMobileMenuOpen] = React19.useState(false);
30264
+ const handleMobileMenuOpen = () => setIsMobileMenuOpen(true);
30265
+ const handleMobileMenuClose = () => setIsMobileMenuOpen(false);
29793
30266
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `min-h-screen ${className}`, children: [
30267
+ /* @__PURE__ */ jsxRuntime.jsx("header", { className: "md:hidden bg-white border-b border-gray-200 shadow-sm px-5 py-3.5 flex items-center justify-between sticky top-0 z-40", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
30268
+ /* @__PURE__ */ jsxRuntime.jsx(HamburgerButton, { onClick: handleMobileMenuOpen }),
30269
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center", children: /* @__PURE__ */ jsxRuntime.jsx(
30270
+ "img",
30271
+ {
30272
+ src: "/optifye-logo.png",
30273
+ alt: "Optifye",
30274
+ className: "h-9 w-9 object-contain",
30275
+ onError: (e) => {
30276
+ e.currentTarget.style.display = "none";
30277
+ if (e.currentTarget.parentElement) {
30278
+ e.currentTarget.parentElement.innerHTML = '<span class="text-blue-600 font-bold text-lg">Optifye</span>';
30279
+ }
30280
+ }
30281
+ }
30282
+ ) })
30283
+ ] }) }),
29794
30284
  /* @__PURE__ */ jsxRuntime.jsx(
29795
30285
  SideNavBar,
29796
30286
  {
29797
30287
  navItems,
29798
30288
  currentPathname: router$1.pathname,
29799
30289
  currentQuery: router$1.query,
29800
- logo
30290
+ logo,
30291
+ isMobileMenuOpen,
30292
+ onMobileMenuClose: handleMobileMenuClose
29801
30293
  }
29802
30294
  ),
29803
- /* @__PURE__ */ jsxRuntime.jsx("main", { className: "ml-20 bg-gray-50 min-h-screen", children })
30295
+ /* @__PURE__ */ jsxRuntime.jsx("main", { className: "md:ml-20 bg-gray-50 min-h-screen transition-all duration-300", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full", children }) })
29804
30296
  ] });
29805
30297
  };
29806
30298
  var Header = ({
@@ -32810,7 +33302,7 @@ function HomeView({
32810
33302
  return null;
32811
33303
  }
32812
33304
  return /* @__PURE__ */ jsxRuntime.jsxs(Select, { onValueChange: handleLineChange, defaultValue: selectedLineId, children: [
32813
- /* @__PURE__ */ jsxRuntime.jsx(SelectTrigger, { className: "w-full sm:w-[200px] bg-white border border-gray-200 shadow-sm rounded-md h-9 text-sm", children: /* @__PURE__ */ jsxRuntime.jsx(SelectValue, { placeholder: "Select a line" }) }),
33305
+ /* @__PURE__ */ jsxRuntime.jsx(SelectTrigger, { className: "w-full sm:w-[200px] bg-white border border-gray-200 shadow-sm rounded-md h-11 sm:h-9 text-sm", children: /* @__PURE__ */ jsxRuntime.jsx(SelectValue, { placeholder: "Select a line" }) }),
32814
33306
  /* @__PURE__ */ jsxRuntime.jsx(SelectContent, { className: "z-50 bg-white shadow-lg border border-gray-200 rounded-md", children: availableLineIds.map((id3) => /* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: id3, children: lineNames[id3] || (id3 === factoryViewId ? "All Lines" : `Line ${id3.substring(0, 4)}`) }, id3)) })
32815
33307
  ] });
32816
33308
  }, [availableLineIds, handleLineChange, selectedLineId, lineNames, factoryViewId, allLineIds.length]);
@@ -32822,7 +33314,7 @@ function HomeView({
32822
33314
  if (errorMessage || displayNamesError) {
32823
33315
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-screen items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-lg bg-white p-6 shadow-lg", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center space-x-3 text-red-500", children: [
32824
33316
  /* @__PURE__ */ jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-6 w-6", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" }) }),
32825
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-lg font-medium", children: [
33317
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-base sm:text-lg font-medium", children: [
32826
33318
  "Error: ",
32827
33319
  errorMessage || displayNamesError?.message
32828
33320
  ] })
@@ -32836,7 +33328,7 @@ function HomeView({
32836
33328
  animate: { opacity: 1 },
32837
33329
  children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex flex-1", children: [
32838
33330
  /* @__PURE__ */ jsxRuntime.jsxs("main", { className: "flex flex-1 flex-col", children: [
32839
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sticky top-0 z-30 sm:static bg-white shadow-sm border-b border-gray-200/80", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col sm:flex-row sm:items-center sm:justify-between px-3 sm:px-6 lg:px-8 py-1.5 sm:py-2.5", children: /* @__PURE__ */ jsxRuntime.jsx(
33331
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative sm:sticky sm:top-0 z-30 bg-white shadow-sm border-b border-gray-200/80", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col sm:flex-row sm:items-center sm:justify-between px-4 sm:px-6 lg:px-8 py-3 sm:py-2.5", children: /* @__PURE__ */ jsxRuntime.jsx(
32840
33332
  DashboardHeader,
32841
33333
  {
32842
33334
  lineTitle,
@@ -32845,8 +33337,8 @@ function HomeView({
32845
33337
  }
32846
33338
  ) }) }),
32847
33339
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 overflow-y-auto sm:overflow-hidden relative", children: [
32848
- lineSelectorComponent && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-3 top-2 sm:right-6 sm:top-3 z-30", children: lineSelectorComponent }),
32849
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full sm:h-full min-h-[calc(100vh-80px)] sm:min-h-0", children: memoizedWorkspaceMetrics.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
33340
+ lineSelectorComponent && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-4 top-3 sm:right-6 sm:top-3 z-30", children: lineSelectorComponent }),
33341
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full sm:h-full min-h-[calc(100vh-120px)] sm:min-h-0", children: memoizedWorkspaceMetrics.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
32850
33342
  motion.div,
32851
33343
  {
32852
33344
  initial: { opacity: 0, scale: 0.98 },
@@ -38356,8 +38848,8 @@ var WorkspaceHealthView = ({
38356
38848
  }
38357
38849
  };
38358
38850
  const getUptimeColor = (percentage) => {
38359
- if (percentage >= 99) return "text-green-600 dark:text-green-400";
38360
- if (percentage >= 95) return "text-yellow-600 dark:text-yellow-400";
38851
+ if (percentage >= 97) return "text-green-600 dark:text-green-400";
38852
+ if (percentage >= 90) return "text-yellow-600 dark:text-yellow-400";
38361
38853
  return "text-red-600 dark:text-red-400";
38362
38854
  };
38363
38855
  if (loading && !summary) {
@@ -38445,20 +38937,21 @@ var WorkspaceHealthView = ({
38445
38937
  className: "grid grid-cols-2 sm:grid-cols-2 md:grid-cols-5 gap-2 sm:gap-3 lg:gap-4",
38446
38938
  children: [
38447
38939
  /* @__PURE__ */ jsxRuntime.jsxs(Card2, { className: "col-span-2 sm:col-span-2 md:col-span-2 bg-white", children: [
38448
- /* @__PURE__ */ jsxRuntime.jsx(CardHeader2, { className: "pb-3", children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle2, { className: "text-sm font-medium text-gray-500 dark:text-gray-400", children: "Overall System Status" }) }),
38940
+ /* @__PURE__ */ jsxRuntime.jsx(CardHeader2, { className: "pb-3", children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle2, { className: "text-sm font-medium text-gray-500 dark:text-gray-400", children: "Overall System Uptime Today" }) }),
38449
38941
  /* @__PURE__ */ jsxRuntime.jsxs(CardContent2, { children: [
38450
38942
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline gap-2", children: [
38451
38943
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: clsx("text-3xl font-bold", getUptimeColor(summary.uptimePercentage)), children: [
38452
38944
  summary.uptimePercentage.toFixed(1),
38453
38945
  "%"
38454
38946
  ] }),
38455
- summary.uptimePercentage >= 99 ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.TrendingUp, { className: "h-5 w-5 text-green-500" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.TrendingDown, { className: "h-5 w-5 text-red-500" })
38947
+ summary.uptimePercentage >= 97 ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.TrendingUp, { className: "h-5 w-5 text-green-500" }) : summary.uptimePercentage >= 90 ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Activity, { className: "h-5 w-5 text-yellow-500" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.TrendingDown, { className: "h-5 w-5 text-red-500" })
38456
38948
  ] }),
38457
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: [
38949
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: "Average uptime across all workspaces" }),
38950
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: [
38458
38951
  summary.healthyWorkspaces,
38459
38952
  " of ",
38460
38953
  summary.totalWorkspaces,
38461
- " workspaces healthy"
38954
+ " workspaces online"
38462
38955
  ] })
38463
38956
  ] })
38464
38957
  ] }),