@optifye/dashboard-core 6.9.12 → 6.9.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -2,8 +2,8 @@ import * as React23 from 'react';
2
2
  import React23__default, { createContext, useRef, useCallback, useState, useMemo, useEffect, forwardRef, useImperativeHandle, useLayoutEffect, memo, useContext, useId, Children, isValidElement, useInsertionEffect, Fragment as Fragment$1, createElement, Component } from 'react';
3
3
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
4
  import { useRouter } from 'next/router';
5
- import { toZonedTime, formatInTimeZone } from 'date-fns-tz';
6
- import { subDays, format, parseISO, isValid, formatDistanceToNow, isFuture, isToday } from 'date-fns';
5
+ import { toZonedTime, formatInTimeZone, fromZonedTime } from 'date-fns-tz';
6
+ import { subDays, format, parseISO, isValid, addMinutes, differenceInMinutes, formatDistanceToNow, isFuture, isToday } from 'date-fns';
7
7
  import mixpanel from 'mixpanel-browser';
8
8
  import { EventEmitter } from 'events';
9
9
  import { createClient, REALTIME_SUBSCRIBE_STATES } from '@supabase/supabase-js';
@@ -2134,6 +2134,88 @@ var WorkspaceHealthService = class _WorkspaceHealthService {
2134
2134
  setCache(key, data) {
2135
2135
  this.cache.set(key, { data, timestamp: Date.now() });
2136
2136
  }
2137
+ getShiftTiming(timezone, shiftConfig) {
2138
+ const { shiftId, date } = getCurrentShift(timezone, shiftConfig);
2139
+ const dayShiftId = shiftConfig?.dayShift?.id ?? 0;
2140
+ const isDayShift = shiftId === dayShiftId;
2141
+ const defaultDayStart = "06:00";
2142
+ const defaultDayEnd = "18:00";
2143
+ const defaultNightStart = "18:00";
2144
+ const defaultNightEnd = "06:00";
2145
+ const shiftStartStr = isDayShift ? shiftConfig?.dayShift?.startTime || defaultDayStart : shiftConfig?.nightShift?.startTime || defaultNightStart;
2146
+ const shiftEndStr = isDayShift ? shiftConfig?.dayShift?.endTime || defaultDayEnd : shiftConfig?.nightShift?.endTime || defaultNightEnd;
2147
+ const shiftLabel = isDayShift ? "Day Shift" : "Night Shift";
2148
+ const parseTime = (value) => {
2149
+ const [hourPart = "0", minutePart = "0"] = value.split(":");
2150
+ const hour = Number.parseInt(hourPart, 10);
2151
+ const minute = Number.parseInt(minutePart, 10);
2152
+ return {
2153
+ hour: Number.isFinite(hour) ? hour : 0,
2154
+ minute: Number.isFinite(minute) ? minute : 0
2155
+ };
2156
+ };
2157
+ const getShiftDurationMinutes = (start, end) => {
2158
+ const startParsed = parseTime(start);
2159
+ const endParsed = parseTime(end);
2160
+ const startTotal = startParsed.hour * 60 + startParsed.minute;
2161
+ const endTotal = endParsed.hour * 60 + endParsed.minute;
2162
+ let duration = endTotal - startTotal;
2163
+ if (duration <= 0) {
2164
+ duration += 24 * 60;
2165
+ }
2166
+ return duration;
2167
+ };
2168
+ const shiftStartDate = fromZonedTime(`${date}T${shiftStartStr}:00`, timezone);
2169
+ const totalMinutes = getShiftDurationMinutes(shiftStartStr, shiftEndStr);
2170
+ const shiftEndDate = addMinutes(shiftStartDate, totalMinutes);
2171
+ const now2 = /* @__PURE__ */ new Date();
2172
+ let completedMinutes = differenceInMinutes(now2 < shiftEndDate ? now2 : shiftEndDate, shiftStartDate);
2173
+ if (completedMinutes < 0) completedMinutes = 0;
2174
+ if (completedMinutes > totalMinutes) completedMinutes = totalMinutes;
2175
+ const pendingMinutes = Math.max(0, totalMinutes - completedMinutes);
2176
+ return {
2177
+ shiftId,
2178
+ date,
2179
+ shiftLabel,
2180
+ shiftStartDate,
2181
+ shiftEndDate,
2182
+ totalMinutes,
2183
+ completedMinutes,
2184
+ pendingMinutes
2185
+ };
2186
+ }
2187
+ normalizeHourBucket(bucket) {
2188
+ if (Array.isArray(bucket)) return bucket;
2189
+ if (bucket && Array.isArray(bucket.values)) return bucket.values;
2190
+ return void 0;
2191
+ }
2192
+ normalizeOutputHourly(outputHourlyRaw) {
2193
+ return outputHourlyRaw && typeof outputHourlyRaw === "object" ? Object.fromEntries(
2194
+ Object.entries(outputHourlyRaw).map(([key, value]) => [key, this.normalizeHourBucket(value) || []])
2195
+ ) : {};
2196
+ }
2197
+ interpretUptimeValue(value) {
2198
+ if (value === null || value === void 0) return "down";
2199
+ if (typeof value === "string") {
2200
+ return value.trim().toLowerCase() === "x" ? "down" : "up";
2201
+ }
2202
+ return "up";
2203
+ }
2204
+ deriveStatusForMinute(minuteOffset, minuteDate, outputHourly, outputArray, timezone) {
2205
+ const hourKey = formatInTimeZone(minuteDate, timezone, "H");
2206
+ const minuteKey = Number.parseInt(formatInTimeZone(minuteDate, timezone, "m"), 10);
2207
+ const hourBucket = outputHourly[hourKey];
2208
+ if (Array.isArray(hourBucket)) {
2209
+ const value = hourBucket[minuteKey];
2210
+ if (value !== void 0) {
2211
+ return this.interpretUptimeValue(value);
2212
+ }
2213
+ }
2214
+ if (minuteOffset < outputArray.length) {
2215
+ return this.interpretUptimeValue(outputArray[minuteOffset]);
2216
+ }
2217
+ return "down";
2218
+ }
2137
2219
  async getWorkspaceHealthStatus(options = {}) {
2138
2220
  const supabase = _getSupabaseInstance();
2139
2221
  if (!supabase) throw new Error("Supabase client not initialized");
@@ -2150,15 +2232,13 @@ var WorkspaceHealthService = class _WorkspaceHealthService {
2150
2232
  throw error;
2151
2233
  }
2152
2234
  const processedData = (data || []).map((item) => this.processHealthStatus(item));
2235
+ const companyId = options.companyId || data?.[0]?.company_id;
2153
2236
  let uptimeMap = /* @__PURE__ */ new Map();
2154
- if (options.companyId || data && data.length > 0) {
2155
- const companyId = options.companyId || data[0]?.company_id;
2156
- if (companyId) {
2157
- try {
2158
- uptimeMap = await this.calculateWorkspaceUptime(companyId);
2159
- } catch (error2) {
2160
- console.error("Error calculating uptime:", error2);
2161
- }
2237
+ if (companyId) {
2238
+ try {
2239
+ uptimeMap = await this.calculateWorkspaceUptime(companyId);
2240
+ } catch (error2) {
2241
+ console.error("Error calculating uptime:", error2);
2162
2242
  }
2163
2243
  }
2164
2244
  const dataWithUptime = processedData.map((workspace) => {
@@ -2174,16 +2254,29 @@ var WorkspaceHealthService = class _WorkspaceHealthService {
2174
2254
  });
2175
2255
  let filteredData = dataWithUptime;
2176
2256
  try {
2177
- const { data: enabledWorkspaces, error: workspaceError } = await supabase.from("workspaces").select("workspace_id, display_name").eq("enable", true);
2257
+ const enabledQuery = supabase.from("workspaces").select("workspace_id, display_name, line_id").eq("enable", true);
2258
+ if (options.lineId) {
2259
+ enabledQuery.eq("line_id", options.lineId);
2260
+ }
2261
+ const { data: enabledWorkspaces, error: workspaceError } = await enabledQuery;
2178
2262
  if (!workspaceError && enabledWorkspaces && enabledWorkspaces.length > 0) {
2179
- const enabledWorkspaceNames = /* @__PURE__ */ new Set();
2263
+ const enabledByLineAndId = /* @__PURE__ */ new Set();
2264
+ const enabledByLineAndName = /* @__PURE__ */ new Set();
2180
2265
  enabledWorkspaces.forEach((w) => {
2181
- if (w.workspace_id) enabledWorkspaceNames.add(w.workspace_id);
2182
- if (w.display_name) enabledWorkspaceNames.add(w.display_name);
2266
+ const lineKey = w.line_id ? String(w.line_id) : "";
2267
+ const idKey = w.workspace_id ? String(w.workspace_id) : "";
2268
+ const nameKey = w.display_name ? String(w.display_name) : "";
2269
+ if (lineKey && idKey) enabledByLineAndId.add(`${lineKey}::${idKey}`);
2270
+ if (lineKey && nameKey) enabledByLineAndName.add(`${lineKey}::${nameKey}`);
2183
2271
  });
2184
2272
  filteredData = filteredData.filter((item) => {
2185
- const displayName = item.workspace_display_name || "";
2186
- return enabledWorkspaceNames.has(displayName);
2273
+ const lineKey = item.line_id ? String(item.line_id) : "";
2274
+ if (!lineKey) return false;
2275
+ const idKey = item.workspace_id ? `${lineKey}::${item.workspace_id}` : "";
2276
+ const nameKey = item.workspace_display_name ? `${lineKey}::${item.workspace_display_name}` : "";
2277
+ if (idKey && enabledByLineAndId.has(idKey)) return true;
2278
+ if (nameKey && enabledByLineAndName.has(nameKey)) return true;
2279
+ return false;
2187
2280
  });
2188
2281
  } else if (!workspaceError && enabledWorkspaces && enabledWorkspaces.length === 0) {
2189
2282
  return [];
@@ -2221,6 +2314,127 @@ var WorkspaceHealthService = class _WorkspaceHealthService {
2221
2314
  }
2222
2315
  return filteredData;
2223
2316
  }
2317
+ async getWorkspaceUptimeTimeline(workspaceId, companyId) {
2318
+ if (!workspaceId) {
2319
+ throw new Error("workspaceId is required to fetch uptime timeline");
2320
+ }
2321
+ if (!companyId) {
2322
+ throw new Error("companyId is required to fetch uptime timeline");
2323
+ }
2324
+ const supabase = _getSupabaseInstance();
2325
+ if (!supabase) throw new Error("Supabase client not initialized");
2326
+ const dashboardConfig = _getDashboardConfigInstance();
2327
+ const timezone = dashboardConfig?.dateTimeConfig?.defaultTimezone || "UTC";
2328
+ const shiftConfig = dashboardConfig?.shiftConfig;
2329
+ const {
2330
+ shiftId,
2331
+ shiftLabel,
2332
+ shiftStartDate,
2333
+ shiftEndDate,
2334
+ totalMinutes,
2335
+ completedMinutes,
2336
+ pendingMinutes,
2337
+ date
2338
+ } = this.getShiftTiming(timezone, shiftConfig);
2339
+ const tableName = `performance_metrics_${companyId.replace(/-/g, "_")}`;
2340
+ const { data, error } = await supabase.from(tableName).select("workspace_id, output_hourly, output_array").eq("workspace_id", workspaceId).eq("date", date).eq("shift_id", shiftId).limit(1);
2341
+ if (error) {
2342
+ console.error("Error fetching uptime timeline metrics:", error);
2343
+ throw error;
2344
+ }
2345
+ const record = Array.isArray(data) && data.length > 0 ? data[0] : null;
2346
+ const outputHourly = this.normalizeOutputHourly(record?.output_hourly || {});
2347
+ const outputArray = Array.isArray(record?.output_array) ? record.output_array : [];
2348
+ const points = [];
2349
+ let uptimeMinutes = 0;
2350
+ let downtimeMinutes = 0;
2351
+ const MIN_DOWNTIME_MINUTES = 2;
2352
+ for (let minuteIndex = 0; minuteIndex < totalMinutes; minuteIndex++) {
2353
+ const minuteDate = addMinutes(shiftStartDate, minuteIndex);
2354
+ const timestamp = formatInTimeZone(minuteDate, timezone, "yyyy-MM-dd'T'HH:mm:ssXXX");
2355
+ let status;
2356
+ if (minuteIndex < completedMinutes) {
2357
+ status = this.deriveStatusForMinute(
2358
+ minuteIndex,
2359
+ minuteDate,
2360
+ outputHourly,
2361
+ outputArray,
2362
+ timezone
2363
+ );
2364
+ if (status === "up") {
2365
+ uptimeMinutes += 1;
2366
+ } else {
2367
+ downtimeMinutes += 1;
2368
+ }
2369
+ } else {
2370
+ status = "pending";
2371
+ }
2372
+ points.push({
2373
+ minuteIndex,
2374
+ timestamp,
2375
+ status
2376
+ });
2377
+ }
2378
+ const downtimeSegments = [];
2379
+ let currentSegmentStart = null;
2380
+ const pushSegment = (startIndex, endIndex) => {
2381
+ if (endIndex <= startIndex) return;
2382
+ const segmentStartDate = addMinutes(shiftStartDate, startIndex);
2383
+ const segmentEndDate = addMinutes(shiftStartDate, endIndex);
2384
+ downtimeSegments.push({
2385
+ startMinuteIndex: startIndex,
2386
+ endMinuteIndex: endIndex,
2387
+ startTime: formatInTimeZone(segmentStartDate, timezone, "yyyy-MM-dd'T'HH:mm:ssXXX"),
2388
+ endTime: formatInTimeZone(segmentEndDate, timezone, "yyyy-MM-dd'T'HH:mm:ssXXX"),
2389
+ durationMinutes: endIndex - startIndex
2390
+ });
2391
+ };
2392
+ for (let i = 0; i < completedMinutes; i++) {
2393
+ const point = points[i];
2394
+ if (point.status === "down") {
2395
+ if (currentSegmentStart === null) {
2396
+ currentSegmentStart = i;
2397
+ }
2398
+ } else if (currentSegmentStart !== null) {
2399
+ pushSegment(currentSegmentStart, i);
2400
+ currentSegmentStart = null;
2401
+ }
2402
+ }
2403
+ if (currentSegmentStart !== null) {
2404
+ pushSegment(currentSegmentStart, completedMinutes);
2405
+ }
2406
+ const filteredSegments = [];
2407
+ downtimeSegments.forEach((segment) => {
2408
+ if (segment.durationMinutes >= MIN_DOWNTIME_MINUTES) {
2409
+ filteredSegments.push(segment);
2410
+ } else {
2411
+ for (let i = segment.startMinuteIndex; i < segment.endMinuteIndex; i++) {
2412
+ if (points[i] && points[i].status === "down") {
2413
+ points[i].status = "up";
2414
+ downtimeMinutes = Math.max(0, downtimeMinutes - 1);
2415
+ uptimeMinutes += 1;
2416
+ }
2417
+ }
2418
+ }
2419
+ });
2420
+ const completedWindow = Math.max(1, uptimeMinutes + downtimeMinutes);
2421
+ const uptimePercentage = completedMinutes > 0 ? Number((uptimeMinutes / completedWindow * 100).toFixed(1)) : 100;
2422
+ return {
2423
+ shiftId,
2424
+ shiftLabel,
2425
+ shiftStart: formatInTimeZone(shiftStartDate, timezone, "yyyy-MM-dd'T'HH:mm:ssXXX"),
2426
+ shiftEnd: formatInTimeZone(shiftEndDate, timezone, "yyyy-MM-dd'T'HH:mm:ssXXX"),
2427
+ totalMinutes,
2428
+ completedMinutes,
2429
+ uptimeMinutes,
2430
+ downtimeMinutes,
2431
+ pendingMinutes,
2432
+ uptimePercentage,
2433
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
2434
+ points,
2435
+ downtimeSegments: filteredSegments
2436
+ };
2437
+ }
2224
2438
  async getWorkspaceHealthById(workspaceId) {
2225
2439
  const cacheKey = `health-${workspaceId}`;
2226
2440
  const cached = this.getFromCache(cacheKey);
@@ -2358,145 +2572,63 @@ var WorkspaceHealthService = class _WorkspaceHealthService {
2358
2572
  const dashboardConfig = _getDashboardConfigInstance();
2359
2573
  const timezone = dashboardConfig?.dateTimeConfig?.defaultTimezone || "UTC";
2360
2574
  const shiftConfig = dashboardConfig?.shiftConfig;
2361
- const currentShiftInfo = getCurrentShift(timezone, shiftConfig);
2362
- const currentDate = currentShiftInfo.date;
2363
- const currentShiftId = currentShiftInfo.shiftId;
2364
- const now2 = /* @__PURE__ */ new Date();
2365
- const currentHour = now2.getHours();
2366
- const currentMinute = now2.getMinutes();
2367
- const dayShiftStart = shiftConfig?.dayShift?.startTime || "08:00";
2368
- const dayShiftEnd = shiftConfig?.dayShift?.endTime || "19:30";
2369
- const nightShiftStart = shiftConfig?.nightShift?.startTime || "19:30";
2370
- const nightShiftEnd = shiftConfig?.nightShift?.endTime || "06:00";
2371
- const parseShiftTime = (timeStr) => {
2372
- const [hour, minute] = timeStr.split(":").map(Number);
2373
- return { hour, minute };
2374
- };
2375
- let shiftStart, shiftEnd;
2376
- if (currentShiftId === 0) {
2377
- shiftStart = parseShiftTime(dayShiftStart);
2378
- shiftEnd = parseShiftTime(dayShiftEnd);
2379
- } else {
2380
- shiftStart = parseShiftTime(nightShiftStart);
2381
- shiftEnd = parseShiftTime(nightShiftEnd);
2382
- }
2383
- let elapsedMinutes = 0;
2384
- if (currentShiftId === 0) {
2385
- const shiftStartTotalMinutes = shiftStart.hour * 60 + shiftStart.minute;
2386
- const currentTotalMinutes = currentHour * 60 + currentMinute;
2387
- const shiftEndTotalMinutes = shiftEnd.hour * 60 + shiftEnd.minute;
2388
- if (currentTotalMinutes >= shiftStartTotalMinutes && currentTotalMinutes < shiftEndTotalMinutes) {
2389
- elapsedMinutes = currentTotalMinutes - shiftStartTotalMinutes;
2390
- } else if (currentTotalMinutes >= shiftEndTotalMinutes) {
2391
- elapsedMinutes = shiftEndTotalMinutes - shiftStartTotalMinutes;
2392
- }
2393
- } else {
2394
- const shiftStartTotalMinutes = shiftStart.hour * 60 + shiftStart.minute;
2395
- const currentTotalMinutes = currentHour * 60 + currentMinute;
2396
- const shiftEndTotalMinutes = shiftEnd.hour * 60 + shiftEnd.minute;
2397
- if (currentHour >= shiftStart.hour || currentHour < shiftEnd.hour) {
2398
- if (currentHour >= shiftStart.hour) {
2399
- elapsedMinutes = currentTotalMinutes - shiftStartTotalMinutes;
2400
- } else {
2401
- elapsedMinutes = 24 * 60 - shiftStartTotalMinutes + currentTotalMinutes;
2402
- }
2403
- if (currentHour >= shiftEnd.hour && currentTotalMinutes >= shiftEndTotalMinutes) {
2404
- const totalShiftMinutes = 24 * 60 - shiftStartTotalMinutes + shiftEndTotalMinutes;
2405
- elapsedMinutes = Math.min(elapsedMinutes, totalShiftMinutes);
2406
- }
2407
- }
2408
- }
2575
+ const {
2576
+ shiftId,
2577
+ date,
2578
+ shiftStartDate,
2579
+ completedMinutes
2580
+ } = this.getShiftTiming(timezone, shiftConfig);
2409
2581
  const tableName = `performance_metrics_${companyId.replace(/-/g, "_")}`;
2410
2582
  try {
2411
- const { data: queryData, error } = await supabase.from(tableName).select("workspace_id, workspace_display_name, output_hourly").eq("date", currentDate).eq("shift_id", currentShiftId);
2583
+ const { data: queryData, error } = await supabase.from(tableName).select("workspace_id, workspace_display_name, output_hourly, output_array").eq("date", date).eq("shift_id", shiftId);
2412
2584
  if (error) {
2413
2585
  console.error("Error fetching performance metrics:", error);
2414
2586
  return /* @__PURE__ */ new Map();
2415
2587
  }
2416
2588
  const uptimeMap = /* @__PURE__ */ new Map();
2417
2589
  for (const record of queryData || []) {
2418
- let actualMinutes = 0;
2419
- let totalPossibleMinutes = 0;
2420
- const outputHourly = record.output_hourly || {};
2421
- const hoursElapsed = Math.ceil(elapsedMinutes / 60);
2422
- for (let hourIndex = 0; hourIndex < hoursElapsed; hourIndex++) {
2423
- const actualHour = (shiftStart.hour + hourIndex) % 24;
2424
- let hourData = [];
2425
- let minutesInThisHour = 60;
2426
- if (shiftStart.minute > 0) {
2427
- if (hourIndex === 0) {
2428
- const firstHourData = outputHourly[actualHour.toString()] || [];
2429
- const nextHour = (actualHour + 1) % 24;
2430
- const nextHourData = outputHourly[nextHour.toString()] || [];
2431
- hourData = [
2432
- ...Array.isArray(firstHourData) ? firstHourData.slice(shiftStart.minute) : [],
2433
- ...Array.isArray(nextHourData) ? nextHourData.slice(0, shiftStart.minute) : []
2434
- ];
2435
- } else if (hourIndex < hoursElapsed - 1) {
2436
- const currentHourData = outputHourly[actualHour.toString()] || [];
2437
- const nextHour = (actualHour + 1) % 24;
2438
- const nextHourData = outputHourly[nextHour.toString()] || [];
2439
- hourData = [
2440
- ...Array.isArray(currentHourData) ? currentHourData.slice(shiftStart.minute) : [],
2441
- ...Array.isArray(nextHourData) ? nextHourData.slice(0, shiftStart.minute) : []
2442
- ];
2443
- } else {
2444
- const isLastHourPartial = hourIndex === hoursElapsed - 1 && elapsedMinutes % 60 > 0;
2445
- if (isLastHourPartial) {
2446
- minutesInThisHour = elapsedMinutes % 60;
2447
- const currentHourData = outputHourly[actualHour.toString()] || [];
2448
- if (shiftStart.minute > 0) {
2449
- const nextHour = (actualHour + 1) % 24;
2450
- const nextHourData = outputHourly[nextHour.toString()] || [];
2451
- const firstPart = Array.isArray(currentHourData) ? currentHourData.slice(shiftStart.minute) : [];
2452
- const secondPart = Array.isArray(nextHourData) ? nextHourData.slice(0, Math.min(shiftStart.minute, minutesInThisHour)) : [];
2453
- hourData = [...firstPart, ...secondPart].slice(0, minutesInThisHour);
2454
- } else {
2455
- hourData = Array.isArray(currentHourData) ? currentHourData.slice(0, minutesInThisHour) : [];
2456
- }
2457
- } else {
2458
- const currentHourData = outputHourly[actualHour.toString()] || [];
2459
- const nextHour = (actualHour + 1) % 24;
2460
- const nextHourData = outputHourly[nextHour.toString()] || [];
2461
- hourData = [
2462
- ...Array.isArray(currentHourData) ? currentHourData.slice(shiftStart.minute) : [],
2463
- ...Array.isArray(nextHourData) ? nextHourData.slice(0, shiftStart.minute) : []
2464
- ];
2465
- }
2466
- }
2590
+ const outputHourly = this.normalizeOutputHourly(record.output_hourly || {});
2591
+ const outputArray = Array.isArray(record.output_array) ? record.output_array : [];
2592
+ let uptimeMinutes = 0;
2593
+ let downtimeMinutes = 0;
2594
+ const MIN_DOWNTIME_MINUTES = 2;
2595
+ let currentDownRun = 0;
2596
+ for (let minuteIndex = 0; minuteIndex < completedMinutes; minuteIndex++) {
2597
+ const minuteDate = addMinutes(shiftStartDate, minuteIndex);
2598
+ const status = this.deriveStatusForMinute(
2599
+ minuteIndex,
2600
+ minuteDate,
2601
+ outputHourly,
2602
+ outputArray,
2603
+ timezone
2604
+ );
2605
+ if (status === "down") {
2606
+ currentDownRun += 1;
2467
2607
  } else {
2468
- hourData = outputHourly[actualHour.toString()] || [];
2469
- if (hourIndex === hoursElapsed - 1) {
2470
- const remainingMinutes = elapsedMinutes % 60;
2471
- if (remainingMinutes > 0) {
2472
- minutesInThisHour = remainingMinutes;
2473
- hourData = Array.isArray(hourData) ? hourData.slice(0, minutesInThisHour) : [];
2608
+ if (currentDownRun > 0) {
2609
+ if (currentDownRun >= MIN_DOWNTIME_MINUTES) {
2610
+ downtimeMinutes += currentDownRun;
2611
+ } else {
2612
+ uptimeMinutes += currentDownRun;
2474
2613
  }
2614
+ currentDownRun = 0;
2475
2615
  }
2616
+ uptimeMinutes += 1;
2476
2617
  }
2477
- const hasXArchitecture = Array.isArray(hourData) && hourData.some((val) => val === "x");
2478
- if (hasXArchitecture) {
2479
- const xCount = hourData.filter((val) => val === "x").length;
2480
- if (xCount <= 2) {
2481
- actualMinutes += minutesInThisHour;
2482
- } else {
2483
- const uptimeCount = hourData.filter((val) => val !== "x").length;
2484
- actualMinutes += Math.min(uptimeCount, minutesInThisHour);
2485
- }
2618
+ }
2619
+ if (currentDownRun > 0) {
2620
+ if (currentDownRun >= MIN_DOWNTIME_MINUTES) {
2621
+ downtimeMinutes += currentDownRun;
2486
2622
  } else {
2487
- const arrayLength = Array.isArray(hourData) ? hourData.length : 0;
2488
- if (arrayLength >= Math.min(58, minutesInThisHour - 2)) {
2489
- actualMinutes += minutesInThisHour;
2490
- } else {
2491
- actualMinutes += Math.min(arrayLength, minutesInThisHour);
2492
- }
2623
+ uptimeMinutes += currentDownRun;
2493
2624
  }
2494
- totalPossibleMinutes += minutesInThisHour;
2625
+ currentDownRun = 0;
2495
2626
  }
2496
- const percentage = totalPossibleMinutes > 0 ? Math.round(actualMinutes / totalPossibleMinutes * 1e3) / 10 : 100;
2627
+ const completedWindow = uptimeMinutes + downtimeMinutes;
2628
+ const percentage = completedWindow > 0 ? Number((uptimeMinutes / completedWindow * 100).toFixed(1)) : 100;
2497
2629
  uptimeMap.set(record.workspace_id, {
2498
- expectedMinutes: totalPossibleMinutes,
2499
- actualMinutes,
2630
+ expectedMinutes: completedMinutes,
2631
+ actualMinutes: uptimeMinutes,
2500
2632
  percentage,
2501
2633
  lastCalculated: (/* @__PURE__ */ new Date()).toISOString()
2502
2634
  });
@@ -10868,6 +11000,65 @@ var useWorkspaceHealthStatus = (workspaceId) => {
10868
11000
  refetch: fetchHealthStatus
10869
11001
  };
10870
11002
  };
11003
+ var useWorkspaceUptimeTimeline = (options) => {
11004
+ const {
11005
+ workspaceId,
11006
+ companyId,
11007
+ enabled = true,
11008
+ refreshInterval
11009
+ } = options;
11010
+ const [timeline, setTimeline] = useState(null);
11011
+ const [loading, setLoading] = useState(false);
11012
+ const [error, setError] = useState(null);
11013
+ const isFetchingRef = useRef(false);
11014
+ const intervalRef = useRef(null);
11015
+ const fetchTimeline = useCallback(async () => {
11016
+ if (!enabled) return;
11017
+ if (!workspaceId || !companyId) {
11018
+ setTimeline(null);
11019
+ return;
11020
+ }
11021
+ if (isFetchingRef.current) return;
11022
+ try {
11023
+ isFetchingRef.current = true;
11024
+ setLoading(true);
11025
+ setError(null);
11026
+ const data = await workspaceHealthService.getWorkspaceUptimeTimeline(
11027
+ workspaceId,
11028
+ companyId
11029
+ );
11030
+ setTimeline(data);
11031
+ } catch (err) {
11032
+ console.error("[useWorkspaceUptimeTimeline] Failed to fetch timeline:", err);
11033
+ setError({ message: err?.message || "Failed to load uptime timeline", code: err?.code || "FETCH_ERROR" });
11034
+ } finally {
11035
+ setLoading(false);
11036
+ isFetchingRef.current = false;
11037
+ }
11038
+ }, [enabled, workspaceId, companyId]);
11039
+ useEffect(() => {
11040
+ fetchTimeline();
11041
+ }, [fetchTimeline]);
11042
+ useEffect(() => {
11043
+ if (!refreshInterval || refreshInterval <= 0 || !enabled) {
11044
+ return;
11045
+ }
11046
+ intervalRef.current = setInterval(() => {
11047
+ fetchTimeline();
11048
+ }, refreshInterval);
11049
+ return () => {
11050
+ if (intervalRef.current) {
11051
+ clearInterval(intervalRef.current);
11052
+ }
11053
+ };
11054
+ }, [refreshInterval, enabled, fetchTimeline]);
11055
+ return {
11056
+ timeline,
11057
+ loading,
11058
+ error,
11059
+ refetch: fetchTimeline
11060
+ };
11061
+ };
10871
11062
  function useDateFormatter() {
10872
11063
  const { defaultTimezone, defaultLocale, dateFormatOptions, timeFormatOptions, dateTimeFormatOptions } = useDateTimeConfig();
10873
11064
  const formatDate = useCallback(
@@ -10883,7 +11074,7 @@ function useDateFormatter() {
10883
11074
  },
10884
11075
  [defaultTimezone, defaultLocale, dateFormatOptions]
10885
11076
  );
10886
- const formatTime4 = useCallback(
11077
+ const formatTime5 = useCallback(
10887
11078
  (date, formatString) => {
10888
11079
  const dateObj = typeof date === "string" ? parseISO(date) : date;
10889
11080
  if (!isValid(dateObj)) return "Invalid Time";
@@ -10914,7 +11105,7 @@ function useDateFormatter() {
10914
11105
  }, []);
10915
11106
  return {
10916
11107
  formatDate,
10917
- formatTime: formatTime4,
11108
+ formatTime: formatTime5,
10918
11109
  formatDateTime,
10919
11110
  getNow,
10920
11111
  timezone: defaultTimezone || "UTC",
@@ -23900,7 +24091,7 @@ var HourlyOutputChartComponent = ({
23900
24091
  endHour = Math.floor(endDecimalHour) % 24;
23901
24092
  endMinute = Math.round(endDecimalHour % 1 * 60);
23902
24093
  }
23903
- const formatTime4 = (h, m) => {
24094
+ const formatTime5 = (h, m) => {
23904
24095
  const period = h >= 12 ? "PM" : "AM";
23905
24096
  const hour12 = h === 0 ? 12 : h > 12 ? h - 12 : h;
23906
24097
  if (m === 0) {
@@ -23908,9 +24099,9 @@ var HourlyOutputChartComponent = ({
23908
24099
  }
23909
24100
  return `${hour12}:${m.toString().padStart(2, "0")}${period}`;
23910
24101
  };
23911
- return `${formatTime4(startHour, startMinute)}-${formatTime4(endHour, endMinute)}`;
24102
+ return `${formatTime5(startHour, startMinute)}-${formatTime5(endHour, endMinute)}`;
23912
24103
  }, [shiftStartTime.decimalHour, SHIFT_DURATION, shiftEndTime]);
23913
- const formatTimeRange = React23__default.useCallback((hourIndex) => {
24104
+ const formatTimeRange2 = React23__default.useCallback((hourIndex) => {
23914
24105
  const isLastHour = hourIndex === SHIFT_DURATION - 1;
23915
24106
  const startDecimalHour = shiftStartTime.decimalHour + hourIndex;
23916
24107
  const startHour = Math.floor(startDecimalHour) % 24;
@@ -23924,12 +24115,12 @@ var HourlyOutputChartComponent = ({
23924
24115
  endHour = Math.floor(endDecimalHour) % 24;
23925
24116
  endMinute = Math.round(endDecimalHour % 1 * 60);
23926
24117
  }
23927
- const formatTime4 = (h, m) => {
24118
+ const formatTime5 = (h, m) => {
23928
24119
  const period = h >= 12 ? "PM" : "AM";
23929
24120
  const hour12 = h === 0 ? 12 : h > 12 ? h - 12 : h;
23930
24121
  return `${hour12}:${m.toString().padStart(2, "0")} ${period}`;
23931
24122
  };
23932
- return `${formatTime4(startHour, startMinute)} - ${formatTime4(endHour, endMinute)}`;
24123
+ return `${formatTime5(startHour, startMinute)} - ${formatTime5(endHour, endMinute)}`;
23933
24124
  }, [shiftStartTime.decimalHour, SHIFT_DURATION, shiftEndTime]);
23934
24125
  const chartData = React23__default.useMemo(() => {
23935
24126
  return Array.from({ length: SHIFT_DURATION }, (_, i) => {
@@ -23990,7 +24181,7 @@ var HourlyOutputChartComponent = ({
23990
24181
  idleMinutes = idleArray.filter((val) => val === "1" || val === 1).length;
23991
24182
  return {
23992
24183
  hour: formatHour(i),
23993
- timeRange: formatTimeRange(i),
24184
+ timeRange: formatTimeRange2(i),
23994
24185
  output: animatedData[i] || 0,
23995
24186
  originalOutput: data[i] || 0,
23996
24187
  // Keep original data for labels
@@ -23999,7 +24190,7 @@ var HourlyOutputChartComponent = ({
23999
24190
  idleArray
24000
24191
  };
24001
24192
  });
24002
- }, [animatedData, data, pphThreshold, idleTimeHourly, shiftStartTime.hour, shiftStartTime.minute, shiftEndTime, formatHour, formatTimeRange, SHIFT_DURATION]);
24193
+ }, [animatedData, data, pphThreshold, idleTimeHourly, shiftStartTime.hour, shiftStartTime.minute, shiftEndTime, formatHour, formatTimeRange2, SHIFT_DURATION]);
24003
24194
  const IdleBar = React23__default.useMemo(() => {
24004
24195
  if (!idleBarState.visible) return null;
24005
24196
  return /* @__PURE__ */ jsx(
@@ -25100,7 +25291,7 @@ var SOPComplianceChart = ({
25100
25291
  }
25101
25292
  };
25102
25293
  }, [data, animateToNewData, mockData]);
25103
- const formatTime4 = (minuteIndex) => {
25294
+ const formatTime5 = (minuteIndex) => {
25104
25295
  const totalMinutes = shiftStartHour * 60 + minuteIndex;
25105
25296
  const hours = Math.floor(totalMinutes / 60) % 24;
25106
25297
  const minutes = totalMinutes % 60;
@@ -25112,7 +25303,7 @@ var SOPComplianceChart = ({
25112
25303
  const hasDataForMinute = index < animatedData.length - 10;
25113
25304
  return {
25114
25305
  minute: index,
25115
- time: formatTime4(index),
25306
+ time: formatTime5(index),
25116
25307
  compliance: hasDataForMinute ? animatedData[index] : null
25117
25308
  };
25118
25309
  });
@@ -25546,7 +25737,7 @@ var DateTimeDisplay = ({
25546
25737
  const {
25547
25738
  defaultTimezone
25548
25739
  } = useDateTimeConfig();
25549
- const { formatDate, formatTime: formatTime4 } = useDateFormatter();
25740
+ const { formatDate, formatTime: formatTime5 } = useDateFormatter();
25550
25741
  const [now2, setNow] = useState(() => getCurrentTimeInZone(defaultTimezone || "UTC"));
25551
25742
  useEffect(() => {
25552
25743
  const timerId = setInterval(() => {
@@ -25558,7 +25749,7 @@ var DateTimeDisplay = ({
25558
25749
  return null;
25559
25750
  }
25560
25751
  const formattedDate = showDate ? formatDate(now2) : "";
25561
- const formattedTime = showTime ? formatTime4(now2) : "";
25752
+ const formattedTime = showTime ? formatTime5(now2) : "";
25562
25753
  return /* @__PURE__ */ jsxs("div", { className: clsx_default("flex items-center space-x-2 text-sm text-gray-700 dark:text-gray-300", className), children: [
25563
25754
  showDate && /* @__PURE__ */ jsx("span", { className: "date-display", "aria-label": `Current date: ${formattedDate}`, children: formattedDate }),
25564
25755
  showDate && showTime && formattedDate && formattedTime && /* @__PURE__ */ jsx("span", { className: "separator", "aria-hidden": "true", children: "|" }),
@@ -25722,7 +25913,7 @@ var BreakNotificationPopup = ({
25722
25913
  const handlePrevious = () => {
25723
25914
  setCurrentIndex((prev) => (prev - 1 + visibleBreaks.length) % visibleBreaks.length);
25724
25915
  };
25725
- const formatTime4 = (minutes) => {
25916
+ const formatTime5 = (minutes) => {
25726
25917
  const hours = Math.floor(minutes / 60);
25727
25918
  const mins = minutes % 60;
25728
25919
  if (hours > 0) {
@@ -25796,9 +25987,9 @@ var BreakNotificationPopup = ({
25796
25987
  formatTo12Hour(currentBreak.endTime)
25797
25988
  ] }),
25798
25989
  /* @__PURE__ */ jsxs("p", { className: "text-xs text-gray-500 mb-2", children: [
25799
- formatTime4(currentBreak.elapsedMinutes),
25990
+ formatTime5(currentBreak.elapsedMinutes),
25800
25991
  " elapsed of ",
25801
- formatTime4(currentBreak.duration),
25992
+ formatTime5(currentBreak.duration),
25802
25993
  " total"
25803
25994
  ] }),
25804
25995
  /* @__PURE__ */ jsx("div", { className: "w-full bg-gray-200 rounded-full h-1.5", children: /* @__PURE__ */ jsx(
@@ -31464,7 +31655,7 @@ function DiagnosisVideoModal({
31464
31655
  }
31465
31656
  loadClip();
31466
31657
  }, [clipId, supabase, transformPlaylistUrls]);
31467
- const formatTime4 = (seconds) => {
31658
+ const formatTime5 = (seconds) => {
31468
31659
  const mins = Math.floor(seconds / 60);
31469
31660
  const secs = Math.floor(seconds % 60);
31470
31661
  return `${mins}:${secs.toString().padStart(2, "0")}`;
@@ -31656,9 +31847,9 @@ function DiagnosisVideoModal({
31656
31847
  }
31657
31848
  ),
31658
31849
  /* @__PURE__ */ jsxs("span", { className: "text-sm font-medium", children: [
31659
- formatTime4(currentTime),
31850
+ formatTime5(currentTime),
31660
31851
  " / ",
31661
- formatTime4(duration)
31852
+ formatTime5(duration)
31662
31853
  ] }),
31663
31854
  /* @__PURE__ */ jsx(
31664
31855
  "input",
@@ -33478,45 +33669,35 @@ var LinePdfGenerator = ({
33478
33669
  const doc = new jsPDF$1();
33479
33670
  const pageHeight = doc.internal.pageSize.height;
33480
33671
  const addHeaderPage1 = () => {
33481
- doc.setFontSize(12);
33672
+ doc.setFontSize(14);
33482
33673
  doc.setFont("helvetica", "bold");
33483
- doc.setTextColor(70, 70, 70);
33674
+ doc.setTextColor(50, 50, 50);
33484
33675
  doc.text("OPTIFYE.AI", 20, 15);
33485
- doc.setFontSize(10);
33676
+ doc.setFontSize(9);
33486
33677
  doc.setFont("helvetica", "normal");
33487
33678
  doc.setTextColor(100, 100, 100);
33488
- const reportText = "REAL-TIME PERFORMANCE REPORT";
33489
- const reportTextWidth = doc.getStringUnitWidth(reportText) * 10 / doc.internal.scaleFactor;
33490
- doc.text(reportText, doc.internal.pageSize.width - 20 - reportTextWidth, 15);
33491
- doc.setDrawColor(220, 220, 220);
33492
- doc.setLineWidth(0.3);
33679
+ const generatedText = `Generated on ${(/* @__PURE__ */ new Date()).toLocaleString("en-IN", { timeZone: "Asia/Kolkata" })}`;
33680
+ const generatedTextWidth = doc.getStringUnitWidth(generatedText) * 9 / doc.internal.scaleFactor;
33681
+ doc.text(generatedText, doc.internal.pageSize.width - 20 - generatedTextWidth, 15);
33682
+ doc.setDrawColor(200, 200, 200);
33683
+ doc.setLineWidth(0.5);
33493
33684
  doc.line(20, 20, 190, 20);
33494
33685
  };
33495
33686
  const addHeaderPage2 = () => {
33496
- doc.setFontSize(12);
33687
+ doc.setFontSize(14);
33497
33688
  doc.setFont("helvetica", "bold");
33498
- doc.setTextColor(70, 70, 70);
33689
+ doc.setTextColor(50, 50, 50);
33499
33690
  doc.text("OPTIFYE.AI", 20, 15);
33500
- doc.setFontSize(10);
33691
+ doc.setFontSize(9);
33501
33692
  doc.setFont("helvetica", "normal");
33502
33693
  doc.setTextColor(100, 100, 100);
33503
- const reportText = "REAL-TIME PERFORMANCE REPORT";
33504
- const reportTextWidth = doc.getStringUnitWidth(reportText) * 10 / doc.internal.scaleFactor;
33505
- doc.text(reportText, doc.internal.pageSize.width - 20 - reportTextWidth, 15);
33506
- doc.text("Page 2", doc.internal.pageSize.width - 30, pageHeight - 15);
33507
- doc.setDrawColor(220, 220, 220);
33508
- doc.setLineWidth(0.3);
33509
- doc.line(20, 20, 190, 20);
33510
- return 35;
33511
- };
33512
- const addFooter = (pageNum) => {
33513
- doc.setFontSize(9);
33514
- doc.setTextColor(130, 130, 130);
33515
33694
  const generatedText = `Generated on ${(/* @__PURE__ */ new Date()).toLocaleString("en-IN", { timeZone: "Asia/Kolkata" })}`;
33516
- doc.text(generatedText, 20, pageHeight - 15);
33517
- if (pageNum === 1) {
33518
- doc.text("Page 1", doc.internal.pageSize.width - 30, pageHeight - 15);
33519
- }
33695
+ const generatedTextWidth = doc.getStringUnitWidth(generatedText) * 9 / doc.internal.scaleFactor;
33696
+ doc.text(generatedText, doc.internal.pageSize.width - 20 - generatedTextWidth, 15);
33697
+ doc.setDrawColor(200, 200, 200);
33698
+ doc.setLineWidth(0.5);
33699
+ doc.line(20, 20, 190, 20);
33700
+ return 30;
33520
33701
  };
33521
33702
  addHeaderPage1();
33522
33703
  doc.setFontSize(26);
@@ -33562,37 +33743,47 @@ var LinePdfGenerator = ({
33562
33743
  doc.setDrawColor(200, 200, 200);
33563
33744
  doc.setLineWidth(0.5);
33564
33745
  doc.line(20, 53, 190, 53);
33565
- doc.setFontSize(16);
33746
+ const perfOverviewStartY = 58;
33747
+ doc.setFillColor(245, 245, 245);
33748
+ doc.roundedRect(15, perfOverviewStartY, 180, 50, 3, 3, "F");
33749
+ doc.setFontSize(18);
33566
33750
  doc.setFont("helvetica", "bold");
33567
- doc.setTextColor(70, 70, 70);
33568
- doc.text("Line Performance Overview", 20, 65);
33751
+ doc.setTextColor(40, 40, 40);
33752
+ doc.text("Line Performance Overview", 20, 68);
33569
33753
  doc.setTextColor(0, 0, 0);
33570
33754
  const createKPIBox = (y) => {
33571
- doc.setFillColor(250, 250, 250);
33572
- doc.roundedRect(22, y - 7, 165, 12, 1, 1, "F");
33755
+ doc.setFillColor(255, 255, 255);
33756
+ doc.roundedRect(22, y - 7, 165, 12, 2, 2, "F");
33757
+ doc.setDrawColor(230, 230, 230);
33758
+ doc.setLineWidth(0.2);
33759
+ doc.roundedRect(22, y - 7, 165, 12, 2, 2, "S");
33573
33760
  };
33574
- createKPIBox(77);
33575
- doc.setFontSize(12);
33761
+ const kpiStartY = 80;
33762
+ const kpiSpacing = 10;
33763
+ createKPIBox(kpiStartY);
33764
+ doc.setFontSize(11);
33576
33765
  doc.setFont("helvetica", "normal");
33577
- doc.text("Output:", 25, 77);
33766
+ doc.text("Output:", 25, kpiStartY);
33578
33767
  doc.setFont("helvetica", "bold");
33579
- doc.text(`${lineInfo.metrics.current_output} / ${lineInfo.metrics.line_threshold}`, 120, 77);
33580
- createKPIBox(87);
33768
+ doc.text(`${lineInfo.metrics.current_output} / ${lineInfo.metrics.line_threshold}`, 120, kpiStartY);
33769
+ createKPIBox(kpiStartY + kpiSpacing);
33581
33770
  doc.setFont("helvetica", "normal");
33582
- doc.text("Underperforming Workspaces:", 25, 87);
33771
+ doc.text("Underperforming Workspaces:", 25, kpiStartY + kpiSpacing);
33583
33772
  doc.setFont("helvetica", "bold");
33584
- doc.text(`${lineInfo.metrics.underperforming_workspaces} / ${lineInfo.metrics.total_workspaces}`, 120, 87);
33585
- createKPIBox(97);
33773
+ doc.text(`${lineInfo.metrics.underperforming_workspaces} / ${lineInfo.metrics.total_workspaces}`, 120, kpiStartY + kpiSpacing);
33774
+ createKPIBox(kpiStartY + kpiSpacing * 2);
33586
33775
  doc.setFont("helvetica", "normal");
33587
- doc.text("Average Efficiency:", 25, 97);
33776
+ doc.text("Average Efficiency:", 25, kpiStartY + kpiSpacing * 2);
33588
33777
  doc.setFont("helvetica", "bold");
33589
- doc.text(`${lineInfo.metrics.avg_efficiency.toFixed(1)}%`, 120, 97);
33590
- doc.setDrawColor(200, 200, 200);
33591
- doc.line(20, 110, 190, 110);
33592
- doc.setFontSize(16);
33778
+ doc.text(`${lineInfo.metrics.avg_efficiency.toFixed(1)}%`, 120, kpiStartY + kpiSpacing * 2);
33779
+ doc.setDrawColor(180, 180, 180);
33780
+ doc.setLineWidth(0.8);
33781
+ doc.line(20, 118, 190, 118);
33782
+ const hourlyOverviewStartY = 123;
33783
+ doc.setFontSize(18);
33593
33784
  doc.setFont("helvetica", "bold");
33594
- doc.setTextColor(70, 70, 70);
33595
- doc.text("Hourly Output Overview", 20, 135);
33785
+ doc.setTextColor(40, 40, 40);
33786
+ doc.text("Hourly Output Overview", 20, 133);
33596
33787
  doc.setTextColor(0, 0, 0);
33597
33788
  const getHourlyTimeRanges = (startTimeStr, endTimeStr) => {
33598
33789
  const [hours, minutes] = startTimeStr.split(":");
@@ -33629,7 +33820,7 @@ var LinePdfGenerator = ({
33629
33820
  }
33630
33821
  hourEndTime.setSeconds(0);
33631
33822
  hourEndTime.setMilliseconds(0);
33632
- const formatTime4 = (date2) => {
33823
+ const formatTime5 = (date2) => {
33633
33824
  return date2.toLocaleTimeString("en-IN", {
33634
33825
  hour: "2-digit",
33635
33826
  minute: "2-digit",
@@ -33637,7 +33828,7 @@ var LinePdfGenerator = ({
33637
33828
  timeZone: "Asia/Kolkata"
33638
33829
  });
33639
33830
  };
33640
- return `${formatTime4(hourStartTime)} - ${formatTime4(hourEndTime)}`;
33831
+ return `${formatTime5(hourStartTime)} - ${formatTime5(hourEndTime)}`;
33641
33832
  });
33642
33833
  };
33643
33834
  const hourlyTimeRanges = lineInfo.metrics.shift_start ? getHourlyTimeRanges(lineInfo.metrics.shift_start, lineInfo.metrics.shift_end) : [];
@@ -33784,86 +33975,94 @@ var LinePdfGenerator = ({
33784
33975
  return Math.round(lineInfo.metrics.current_output / shiftDuration);
33785
33976
  });
33786
33977
  }
33978
+ const tableHeaderY = 143;
33979
+ const tableStartY = 151;
33980
+ const rowSpacing = 8;
33981
+ const bottomPadding = 8;
33982
+ const hourlyTableHeight = hourlyTimeRanges.length * rowSpacing;
33983
+ const backgroundHeight = tableStartY - hourlyOverviewStartY + hourlyTableHeight + bottomPadding;
33984
+ doc.setFillColor(245, 245, 245);
33985
+ doc.roundedRect(15, hourlyOverviewStartY, 180, backgroundHeight, 3, 3, "F");
33787
33986
  doc.setFontSize(11);
33788
33987
  doc.setFont("helvetica", "bold");
33789
- doc.setFillColor(245, 245, 245);
33790
- doc.roundedRect(20, 140, 170, 8, 1, 1, "F");
33791
- doc.text("Time Range", 25, 145);
33792
- doc.text("Actual Output", 80, 145);
33793
- doc.text("Target Output", 125, 145);
33794
- doc.text("Status", 170, 145);
33795
- doc.setLineWidth(0.2);
33796
- doc.setDrawColor(220, 220, 220);
33797
- doc.line(20, 148, 190, 148);
33988
+ doc.text("Time Range", 25, tableHeaderY);
33989
+ doc.text("Output", 80, tableHeaderY);
33990
+ doc.text("Target", 125, tableHeaderY);
33991
+ doc.text("Status", 170, tableHeaderY);
33992
+ doc.setLineWidth(0.3);
33993
+ doc.setDrawColor(200, 200, 200);
33994
+ doc.line(20, 146, 190, 146);
33798
33995
  doc.setFont("helvetica", "normal");
33799
- let yPos = 155;
33800
- const rowSpacing = 7;
33996
+ let yPos = tableStartY;
33997
+ const lineDateForTable = new Date(lineInfo.date);
33998
+ const todayForTable = /* @__PURE__ */ new Date();
33999
+ todayForTable.setHours(0, 0, 0, 0);
34000
+ lineDateForTable.setHours(0, 0, 0, 0);
34001
+ const isTodayForTable = lineDateForTable.getTime() === todayForTable.getTime();
34002
+ let currentHour = 24;
34003
+ if (isTodayForTable) {
34004
+ const now2 = /* @__PURE__ */ new Date();
34005
+ const currentTimeIST = new Date(now2.toLocaleString("en-US", { timeZone: "Asia/Kolkata" }));
34006
+ currentHour = currentTimeIST.getHours();
34007
+ }
33801
34008
  hourlyTimeRanges.forEach((timeRange, index) => {
33802
- if (index % 2 === 0) {
33803
- doc.setFillColor(252, 252, 252);
33804
- doc.roundedRect(20, yPos - 5, 170, 8, 1, 1, "F");
33805
- }
33806
- const actualOutput = hourlyActualOutput[index];
33807
- const isOverTarget = actualOutput >= targetOutputPerHour;
34009
+ const actualOutput = hourlyActualOutput[index] || 0;
34010
+ const [startHourStr] = (lineInfo.metrics.shift_start || "6:00").split(":");
34011
+ const startHour = parseInt(startHourStr);
34012
+ const hourNumber = (startHour + index) % 24;
34013
+ const dataCollected = !isTodayForTable || hourNumber < currentHour;
34014
+ const outputStr = dataCollected ? actualOutput.toString() : "TBD";
34015
+ const targetStr = targetOutputPerHour.toString();
33808
34016
  doc.text(timeRange, 25, yPos);
33809
- doc.text(actualOutput.toString(), 80, yPos);
33810
- doc.text(targetOutputPerHour.toString(), 125, yPos);
33811
- if (isOverTarget) {
33812
- doc.setDrawColor(0, 171, 69);
33813
- doc.setLineWidth(0.5);
33814
- const tickX = 170;
33815
- const tickY = yPos - 1;
33816
- doc.line(tickX - 2, tickY, tickX, tickY + 1.5);
33817
- doc.line(tickX, tickY + 1.5, tickX + 2, tickY - 2);
34017
+ doc.text(outputStr, 80, yPos);
34018
+ doc.text(targetStr, 125, yPos);
34019
+ if (!dataCollected) {
34020
+ doc.setTextColor(100, 100, 100);
34021
+ doc.text("-", 170, yPos);
34022
+ } else if (actualOutput >= targetOutputPerHour) {
34023
+ doc.setTextColor(0, 171, 69);
34024
+ doc.setFont("ZapfDingbats", "normal");
34025
+ doc.text("4", 170, yPos);
34026
+ doc.setFont("helvetica", "normal");
33818
34027
  } else {
33819
- doc.setDrawColor(227, 67, 41);
33820
- doc.setLineWidth(0.5);
33821
- const crossX = 170;
33822
- const crossY = yPos - 1;
33823
- doc.line(crossX - 2, crossY - 2, crossX + 2, crossY + 2);
33824
- doc.line(crossX - 2, crossY + 2, crossX + 2, crossY - 2);
34028
+ doc.setTextColor(227, 67, 41);
34029
+ doc.text("\xD7", 170, yPos);
33825
34030
  }
33826
- doc.setDrawColor(220, 220, 220);
33827
- doc.setLineWidth(0.2);
34031
+ doc.setTextColor(0, 0, 0);
33828
34032
  yPos += rowSpacing;
33829
34033
  });
33830
- doc.roundedRect(20, 140, 170, yPos - 140 - 3, 1, 1, "S");
33831
- addFooter(1);
33832
34034
  doc.addPage();
33833
34035
  yPos = addHeaderPage2();
33834
- doc.setFontSize(16);
34036
+ const workspaceSectionStartY = yPos;
34037
+ const sortedWorkspaces = [...workspaceData].sort((a, b) => (a.efficiency || 0) - (b.efficiency || 0)).slice(0, 10);
34038
+ const wsRowCount = sortedWorkspaces.length > 0 ? sortedWorkspaces.length : 1;
34039
+ const wsTableHeight = 10 + 8 + 7 + wsRowCount * 8 + 8;
34040
+ doc.setFillColor(245, 245, 245);
34041
+ doc.roundedRect(15, workspaceSectionStartY, 180, wsTableHeight, 3, 3, "F");
34042
+ doc.setFontSize(18);
33835
34043
  doc.setFont("helvetica", "bold");
33836
- doc.setTextColor(70, 70, 70);
34044
+ doc.setTextColor(40, 40, 40);
33837
34045
  doc.text("Poorest Performing Workspaces", 20, yPos);
33838
34046
  doc.setTextColor(0, 0, 0);
33839
34047
  yPos += 10;
33840
34048
  doc.setFontSize(11);
33841
34049
  doc.setFont("helvetica", "bold");
33842
- doc.setFillColor(245, 245, 245);
33843
- doc.roundedRect(20, yPos, 170, 8, 1, 1, "F");
33844
34050
  yPos += 5;
33845
34051
  doc.text("Workspace", 25, yPos);
33846
- doc.text("Current/Ideal", 85, yPos);
34052
+ doc.text("Current/Target", 85, yPos);
33847
34053
  doc.text("Efficiency", 145, yPos);
33848
34054
  yPos += 3;
33849
- doc.setLineWidth(0.2);
33850
- doc.setDrawColor(220, 220, 220);
34055
+ doc.setLineWidth(0.3);
34056
+ doc.setDrawColor(200, 200, 200);
33851
34057
  doc.line(20, yPos, 190, yPos);
33852
34058
  doc.setFont("helvetica", "normal");
33853
- const tableStartY = yPos;
33854
34059
  yPos += 7;
33855
- const sortedWorkspaces = [...workspaceData].sort((a, b) => (a.efficiency || 0) - (b.efficiency || 0)).slice(0, 10);
33856
34060
  if (sortedWorkspaces.length === 0) {
33857
34061
  doc.text("No workspace data available", 25, yPos);
33858
34062
  yPos += 10;
33859
34063
  } else {
33860
34064
  sortedWorkspaces.forEach((ws, index) => {
33861
- if (index % 2 === 0) {
33862
- doc.setFillColor(252, 252, 252);
33863
- doc.roundedRect(20, yPos - 5, 170, 8, 1, 1, "F");
33864
- }
33865
34065
  const workspaceName = getWorkspaceDisplayName(ws.workspace_name, lineInfo.line_id);
33866
- const maxWidth = 55;
33867
34066
  const truncatedName = workspaceName.length > 25 ? workspaceName.substring(0, 22) + "..." : workspaceName;
33868
34067
  doc.text(truncatedName, 25, yPos);
33869
34068
  doc.text(`${ws.action_count || 0} / ${ws.action_threshold || 0}`, 85, yPos);
@@ -33871,9 +34070,6 @@ var LinePdfGenerator = ({
33871
34070
  yPos += 8;
33872
34071
  });
33873
34072
  }
33874
- const wsTableHeight = yPos - tableStartY - 3;
33875
- doc.roundedRect(20, tableStartY, 170, wsTableHeight, 1, 1, "S");
33876
- addFooter(2);
33877
34073
  doc.save(`${lineInfo.line_name}_${date.split(",")[0]}.pdf`);
33878
34074
  } catch (error) {
33879
34075
  console.error("PDF generation failed:", error);
@@ -35047,25 +35243,25 @@ var WorkspacePdfGenerator = ({ workspace, className }) => {
35047
35243
  doc.setFont("helvetica", "bold");
35048
35244
  doc.setTextColor(50, 50, 50);
35049
35245
  doc.text("OPTIFYE.AI", 20, 15);
35050
- doc.setFontSize(11);
35246
+ doc.setFontSize(9);
35051
35247
  doc.setFont("helvetica", "normal");
35052
- doc.setTextColor(80, 80, 80);
35053
- const reportText = "REAL-TIME PERFORMANCE REPORT";
35054
- const reportTextWidth = doc.getStringUnitWidth(reportText) * 11 / doc.internal.scaleFactor;
35055
- doc.text(reportText, doc.internal.pageSize.width - 20 - reportTextWidth, 15);
35248
+ doc.setTextColor(100, 100, 100);
35249
+ const generatedText = `Generated on ${(/* @__PURE__ */ new Date()).toLocaleString("en-IN", { timeZone: "Asia/Kolkata" })}`;
35250
+ const generatedTextWidth = doc.getStringUnitWidth(generatedText) * 9 / doc.internal.scaleFactor;
35251
+ doc.text(generatedText, doc.internal.pageSize.width - 20 - generatedTextWidth, 15);
35056
35252
  doc.setDrawColor(200, 200, 200);
35057
35253
  doc.setLineWidth(0.5);
35058
35254
  doc.line(20, 20, 190, 20);
35059
35255
  doc.setFillColor(250, 250, 250);
35060
- doc.roundedRect(15, 25, 180, 55, 3, 3, "F");
35256
+ doc.roundedRect(15, 25, 180, 53, 3, 3, "F");
35061
35257
  doc.setFontSize(32);
35062
35258
  doc.setFont("helvetica", "bold");
35063
35259
  doc.setTextColor(0, 0, 0);
35064
- doc.text(lineName, 20, 40);
35260
+ doc.text(lineName, 20, 39);
35065
35261
  doc.setFontSize(22);
35066
35262
  doc.setFont("helvetica", "normal");
35067
35263
  doc.setTextColor(40, 40, 40);
35068
- doc.text(getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id), 20, 52);
35264
+ doc.text(getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id), 20, 51);
35069
35265
  doc.setFontSize(13);
35070
35266
  doc.setFont("helvetica", "normal");
35071
35267
  doc.setTextColor(60, 60, 60);
@@ -35076,8 +35272,8 @@ var WorkspacePdfGenerator = ({ workspace, className }) => {
35076
35272
  timeZone: "Asia/Kolkata"
35077
35273
  });
35078
35274
  const shiftType = "Day Shift";
35079
- doc.text(`${date}`, 20, 65);
35080
- doc.text(`${shiftType}`, 20, 73);
35275
+ doc.text(`${date}`, 20, 63);
35276
+ doc.text(`${shiftType}`, 20, 71);
35081
35277
  const currentTime = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-IN", {
35082
35278
  hour: "2-digit",
35083
35279
  minute: "2-digit",
@@ -35090,11 +35286,11 @@ var WorkspacePdfGenerator = ({ workspace, className }) => {
35090
35286
  });
35091
35287
  doc.setFontSize(12);
35092
35288
  doc.setTextColor(80, 80, 80);
35093
- doc.text(`Report Period: ${shiftStartTime} - ${currentTime}`, 20, 81);
35289
+ doc.text(`Report Period: ${shiftStartTime} - ${currentTime}`, 20, 79);
35094
35290
  doc.setTextColor(0, 0, 0);
35095
35291
  doc.setDrawColor(180, 180, 180);
35096
35292
  doc.setLineWidth(0.8);
35097
- doc.line(20, 90, 190, 90);
35293
+ doc.line(20, 88, 190, 88);
35098
35294
  const createKPIBox = (y) => {
35099
35295
  doc.setFillColor(255, 255, 255);
35100
35296
  doc.roundedRect(22, y - 7, 165, 12, 2, 2, "F");
@@ -35102,66 +35298,72 @@ var WorkspacePdfGenerator = ({ workspace, className }) => {
35102
35298
  doc.setLineWidth(0.2);
35103
35299
  doc.roundedRect(22, y - 7, 165, 12, 2, 2, "S");
35104
35300
  };
35301
+ const perfOverviewStartY = 93;
35105
35302
  doc.setFillColor(245, 245, 245);
35106
- doc.roundedRect(15, 95, 180, 60, 3, 3, "F");
35303
+ doc.roundedRect(15, perfOverviewStartY, 180, 60, 3, 3, "F");
35107
35304
  doc.setFontSize(18);
35108
35305
  doc.setFont("helvetica", "bold");
35109
35306
  doc.setTextColor(40, 40, 40);
35110
- doc.text("Performance Overview", 20, 105);
35307
+ doc.text("Performance Overview", 20, 103);
35111
35308
  doc.setTextColor(0, 0, 0);
35112
- const kpiStartY = 117;
35309
+ const kpiStartY = 115;
35113
35310
  const kpiSpacing = 10;
35114
35311
  createKPIBox(kpiStartY);
35115
35312
  doc.setFontSize(11);
35116
35313
  doc.setFont("helvetica", "normal");
35117
- doc.text("Progress:", 25, kpiStartY);
35314
+ doc.text("Current Output/Target Output:", 25, kpiStartY);
35118
35315
  doc.setFont("helvetica", "bold");
35119
- doc.text(`${(workspace.total_actions / workspace.target_output * 100).toFixed(0)}% of Today's target`, 120, kpiStartY);
35316
+ doc.text(`${workspace.total_actions} / ${workspace.target_output}`, 120, kpiStartY);
35120
35317
  createKPIBox(kpiStartY + kpiSpacing);
35121
35318
  doc.setFont("helvetica", "normal");
35122
- doc.text("Current Output/Ideal Output:", 25, kpiStartY + kpiSpacing);
35319
+ doc.text("Efficiency:", 25, kpiStartY + kpiSpacing);
35123
35320
  doc.setFont("helvetica", "bold");
35124
- doc.text(`${workspace.total_actions} / ${workspace.target_output}`, 120, kpiStartY + kpiSpacing);
35321
+ doc.text(`${(workspace.avg_efficiency || 0).toFixed(1)}% (Target: 80%)`, 120, kpiStartY + kpiSpacing);
35125
35322
  createKPIBox(kpiStartY + kpiSpacing * 2);
35126
35323
  doc.setFont("helvetica", "normal");
35127
- doc.text("Efficiency:", 25, kpiStartY + kpiSpacing * 2);
35128
- doc.setFont("helvetica", "bold");
35129
- doc.text(`${(workspace.avg_efficiency || 0).toFixed(1)}% (Target: 80%)`, 120, kpiStartY + kpiSpacing * 2);
35130
- createKPIBox(kpiStartY + kpiSpacing * 3);
35131
- doc.setFont("helvetica", "normal");
35132
- doc.text("PPH (Pieces Per Hour):", 25, kpiStartY + kpiSpacing * 3);
35324
+ doc.text("PPH (Pieces Per Hour):", 25, kpiStartY + kpiSpacing * 2);
35133
35325
  doc.setFont("helvetica", "bold");
35134
- doc.text(`${workspace.avg_pph.toFixed(1)} (Standard: ${workspace.pph_threshold.toFixed(1)})`, 120, kpiStartY + kpiSpacing * 3);
35326
+ doc.text(`${workspace.avg_pph.toFixed(1)} (Standard: ${workspace.pph_threshold.toFixed(1)})`, 120, kpiStartY + kpiSpacing * 2);
35135
35327
  doc.setDrawColor(180, 180, 180);
35136
35328
  doc.setLineWidth(0.8);
35137
- doc.line(20, 170, 190, 170);
35329
+ doc.line(20, 163, 190, 163);
35330
+ const hourlyPerfStartY = 168;
35331
+ const hourlyData = workspace.hourly_action_counts || [];
35332
+ const hourlyTarget = workspace.pph_threshold;
35333
+ const tableStartY = 199;
35334
+ const rowHeight = 8;
35335
+ const bottomPadding = 8;
35336
+ const backgroundHeight = tableStartY - hourlyPerfStartY + hourlyData.length * rowHeight + bottomPadding;
35138
35337
  doc.setFillColor(245, 245, 245);
35139
- doc.roundedRect(15, 175, 180, 85, 3, 3, "F");
35338
+ doc.roundedRect(15, hourlyPerfStartY, 180, backgroundHeight, 3, 3, "F");
35140
35339
  doc.setFontSize(18);
35141
35340
  doc.setFont("helvetica", "bold");
35142
35341
  doc.setTextColor(40, 40, 40);
35143
- doc.text("Hourly Performance", 20, 185);
35342
+ doc.text("Hourly Performance", 20, 178);
35144
35343
  doc.setTextColor(0, 0, 0);
35145
35344
  doc.setFontSize(11);
35146
35345
  doc.setFont("helvetica", "bold");
35147
- doc.setFillColor(245, 245, 245);
35148
- doc.roundedRect(20, 177, 170, 8, 1, 1, "F");
35149
- doc.text("Time Range", 25, 182);
35150
- doc.text("Output", 95, 182);
35151
- doc.text("Target", 135, 182);
35152
- doc.text("Status", 170, 182);
35153
- doc.setLineWidth(0.2);
35154
- doc.setDrawColor(220, 220, 220);
35155
- doc.line(20, 185, 190, 185);
35346
+ doc.text("Time Range", 25, 188);
35347
+ doc.text("Output", 95, 188);
35348
+ doc.text("Target", 135, 188);
35349
+ doc.text("Status", 170, 188);
35350
+ doc.setLineWidth(0.3);
35351
+ doc.setDrawColor(200, 200, 200);
35352
+ doc.line(20, 191, 190, 191);
35156
35353
  doc.setFont("helvetica", "normal");
35157
- let yPos = 191;
35158
- const hourlyData = workspace.hourly_action_counts || [];
35159
- const hourlyTarget = workspace.pph_threshold;
35354
+ let yPos = tableStartY;
35355
+ const workspaceDate = new Date(workspace.date);
35356
+ const today = /* @__PURE__ */ new Date();
35357
+ today.setHours(0, 0, 0, 0);
35358
+ workspaceDate.setHours(0, 0, 0, 0);
35359
+ const isToday = workspaceDate.getTime() === today.getTime();
35360
+ let currentHour = 24;
35361
+ if (isToday) {
35362
+ const now2 = /* @__PURE__ */ new Date();
35363
+ const currentTimeIST = new Date(now2.toLocaleString("en-US", { timeZone: "Asia/Kolkata" }));
35364
+ currentHour = currentTimeIST.getHours();
35365
+ }
35160
35366
  hourlyData.forEach((output, index) => {
35161
- if (index % 2 === 0) {
35162
- doc.setFillColor(252, 252, 252);
35163
- doc.roundedRect(20, yPos - 5, 170, 8, 1, 1, "F");
35164
- }
35165
35367
  const startTime = /* @__PURE__ */ new Date(`2000-01-01 ${workspace.shift_start}`);
35166
35368
  startTime.setHours(startTime.getHours() + index);
35167
35369
  const endTime = new Date(startTime);
@@ -35173,14 +35375,17 @@ var WorkspacePdfGenerator = ({ workspace, className }) => {
35173
35375
  hour: "numeric",
35174
35376
  hour12: true
35175
35377
  })}`;
35176
- const outputStr = output.toString();
35378
+ const hourNumber = startTime.getHours();
35379
+ const dataCollected = !isToday || hourNumber < currentHour;
35380
+ const outputStr = dataCollected ? output.toString() : "TBD";
35177
35381
  const targetStr = hourlyTarget.toString();
35178
- const outputX = 95 + doc.getStringUnitWidth(outputStr) * doc.getFontSize() / (2 * doc.internal.scaleFactor);
35179
- const targetX = 135 + doc.getStringUnitWidth(targetStr) * doc.getFontSize() / (2 * doc.internal.scaleFactor);
35180
35382
  doc.text(timeRange, 25, yPos);
35181
- doc.text(outputStr, outputX, yPos);
35182
- doc.text(targetStr, targetX, yPos);
35183
- if (output >= hourlyTarget) {
35383
+ doc.text(outputStr, 95, yPos);
35384
+ doc.text(targetStr, 135, yPos);
35385
+ if (!dataCollected) {
35386
+ doc.setTextColor(100, 100, 100);
35387
+ doc.text("-", 170, yPos);
35388
+ } else if (output >= hourlyTarget) {
35184
35389
  doc.setTextColor(0, 171, 69);
35185
35390
  doc.setFont("ZapfDingbats", "normal");
35186
35391
  doc.text("4", 170, yPos);
@@ -35190,15 +35395,8 @@ var WorkspacePdfGenerator = ({ workspace, className }) => {
35190
35395
  doc.text("\xD7", 170, yPos);
35191
35396
  }
35192
35397
  doc.setTextColor(0, 0, 0);
35193
- yPos += 8;
35398
+ yPos += rowHeight;
35194
35399
  });
35195
- doc.setLineWidth(0.2);
35196
- doc.setDrawColor(220, 220, 220);
35197
- doc.roundedRect(20, 187, 170, yPos - 187 - 3, 1, 1, "S");
35198
- doc.setFontSize(9);
35199
- doc.setTextColor(130, 130, 130);
35200
- const generatedText = `Generated on ${(/* @__PURE__ */ new Date()).toLocaleString("en-IN", { timeZone: "Asia/Kolkata" })}`;
35201
- doc.text(generatedText, 20, 280);
35202
35400
  doc.save(`${workspace.workspace_name}_${date.split(",")[0]}.pdf`);
35203
35401
  } catch (error) {
35204
35402
  console.error("PDF generation failed:", error);
@@ -36278,7 +36476,8 @@ var WorkspaceHealthCard = ({
36278
36476
  workspace,
36279
36477
  onClick,
36280
36478
  showDetails = true,
36281
- className = ""
36479
+ className = "",
36480
+ onViewDetails
36282
36481
  }) => {
36283
36482
  const getStatusConfig = () => {
36284
36483
  switch (workspace.status) {
@@ -36331,38 +36530,101 @@ var WorkspaceHealthCard = ({
36331
36530
  onClick(workspace);
36332
36531
  }
36333
36532
  };
36533
+ const handleViewDetails = (event) => {
36534
+ event.stopPropagation();
36535
+ event.preventDefault();
36536
+ if (onViewDetails) {
36537
+ onViewDetails(workspace);
36538
+ }
36539
+ };
36334
36540
  const handleKeyDown = (event) => {
36335
36541
  if (onClick && (event.key === "Enter" || event.key === " ")) {
36336
36542
  event.preventDefault();
36337
36543
  onClick(workspace);
36338
36544
  }
36339
36545
  };
36546
+ const formatDuration3 = (minutes) => {
36547
+ if (!minutes || minutes <= 0) return "0 min";
36548
+ const rounded = Math.max(Math.round(minutes), 0);
36549
+ const days = Math.floor(rounded / 1440);
36550
+ const hours = Math.floor(rounded % 1440 / 60);
36551
+ const mins = rounded % 60;
36552
+ const parts = [];
36553
+ if (days) parts.push(`${days} day${days === 1 ? "" : "s"}`);
36554
+ if (hours) parts.push(`${hours} hr${hours === 1 ? "" : "s"}`);
36555
+ if (mins) parts.push(`${mins} min${mins === 1 ? "" : "s"}`);
36556
+ if (!parts.length) {
36557
+ parts.push("1 min");
36558
+ }
36559
+ return parts.join(" ");
36560
+ };
36340
36561
  const formatTimeAgo = (timeString) => {
36341
- return timeString.replace("about ", "").replace(" ago", "");
36562
+ if (!timeString) return "Unknown";
36563
+ const cleaned = timeString.replace("about ", "").trim();
36564
+ if (cleaned.toLowerCase() === "never") return "Never";
36565
+ const minuteMatch = cleaned.match(/(\d+)\s*m/);
36566
+ if (!minuteMatch) {
36567
+ return cleaned;
36568
+ }
36569
+ const minutes = parseInt(minuteMatch[1], 10);
36570
+ if (!Number.isFinite(minutes)) return cleaned;
36571
+ if (minutes < 1) return "Just now";
36572
+ if (minutes < 60) {
36573
+ return `${minutes} min ago`;
36574
+ }
36575
+ const hours = Math.floor(minutes / 60);
36576
+ const remainingMinutes = minutes % 60;
36577
+ if (hours < 24) {
36578
+ const parts2 = [`${hours} hr${hours === 1 ? "" : "s"}`];
36579
+ if (remainingMinutes) {
36580
+ parts2.push(`${remainingMinutes} min`);
36581
+ }
36582
+ return `${parts2.join(" ")} ago`;
36583
+ }
36584
+ const days = Math.floor(hours / 24);
36585
+ const remainingHours = hours % 24;
36586
+ const parts = [`${days} day${days === 1 ? "" : "s"}`];
36587
+ if (remainingHours) {
36588
+ parts.push(`${remainingHours} hr${remainingHours === 1 ? "" : "s"}`);
36589
+ }
36590
+ return `${parts.join(" ")} ago`;
36342
36591
  };
36343
- const formatDowntime = (uptimeDetails) => {
36344
- if (!uptimeDetails) return "";
36592
+ const getDowntimeConfig = (uptimeDetails) => {
36593
+ if (!uptimeDetails) {
36594
+ if (workspace.status === "healthy") {
36595
+ return {
36596
+ text: "0m",
36597
+ className: "text-emerald-600 dark:text-emerald-400",
36598
+ label: "Total Downtime"
36599
+ };
36600
+ }
36601
+ return { text: "--", className: "text-slate-400", label: "Total Downtime" };
36602
+ }
36345
36603
  const downtimeMinutes = Math.max(0, uptimeDetails.expectedMinutes - uptimeDetails.actualMinutes);
36346
- if (downtimeMinutes === 0) return "No downtime";
36347
- if (downtimeMinutes < 1) return "< 1 min downtime";
36348
- if (downtimeMinutes < 60) return `${downtimeMinutes} min downtime`;
36349
- const hours = Math.floor(downtimeMinutes / 60);
36350
- const minutes = downtimeMinutes % 60;
36351
- if (minutes === 0) {
36352
- return `${hours} hr downtime`;
36604
+ if (downtimeMinutes === 0) {
36605
+ return {
36606
+ text: "0m",
36607
+ className: "text-emerald-600 dark:text-emerald-400",
36608
+ label: "Total Downtime"
36609
+ };
36353
36610
  }
36354
- return `${hours} hr ${minutes} min downtime`;
36611
+ return {
36612
+ text: `${formatDuration3(downtimeMinutes)}`,
36613
+ className: downtimeMinutes > 60 ? "text-rose-600 dark:text-rose-400" : "text-amber-600 dark:text-amber-400",
36614
+ label: "Total Downtime"
36615
+ };
36355
36616
  };
36617
+ const downtimeConfig = getDowntimeConfig(workspace.uptimeDetails);
36356
36618
  return /* @__PURE__ */ jsx(
36357
36619
  Card2,
36358
36620
  {
36359
36621
  className: clsx(
36360
- "relative overflow-hidden transition-all duration-300",
36622
+ "relative overflow-hidden transition-all duration-300 h-full flex flex-col",
36361
36623
  "bg-gradient-to-br",
36362
36624
  config.gradient,
36363
36625
  "border",
36364
36626
  config.border,
36365
- "shadow-sm hover:shadow-md",
36627
+ "shadow-sm hover:shadow-lg hover:border-blue-300 dark:hover:border-blue-700",
36366
36628
  onClick && "cursor-pointer hover:scale-[1.01]",
36367
36629
  workspace.isStale && "opacity-90",
36368
36630
  className
@@ -36372,7 +36634,7 @@ var WorkspaceHealthCard = ({
36372
36634
  tabIndex: onClick ? 0 : void 0,
36373
36635
  role: onClick ? "button" : void 0,
36374
36636
  "aria-label": `Workspace ${workspace.workspace_display_name} status: ${workspace.status}`,
36375
- children: /* @__PURE__ */ jsxs("div", { className: "p-4", children: [
36637
+ children: /* @__PURE__ */ jsxs("div", { className: "p-4 flex-grow flex flex-col", children: [
36376
36638
  /* @__PURE__ */ jsx("div", { className: "mb-3", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-start gap-2 sm:gap-3", children: [
36377
36639
  /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
36378
36640
  /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-gray-900 dark:text-gray-100 mb-1 break-words", children: workspace.workspace_display_name || `Workspace ${workspace.workspace_id.slice(0, 8)}` }),
@@ -36394,21 +36656,20 @@ var WorkspaceHealthCard = ({
36394
36656
  /* @__PURE__ */ jsx("span", { className: "font-medium", children: formatTimeAgo(workspace.timeSinceLastUpdate) })
36395
36657
  ] })
36396
36658
  ] }),
36397
- workspace.uptimePercentage !== void 0 && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
36398
- /* @__PURE__ */ jsx(Activity, { className: "h-3.5 w-3.5 text-gray-400" }),
36399
- /* @__PURE__ */ jsxs("span", { className: "text-sm text-gray-600 dark:text-gray-400 whitespace-nowrap", children: [
36400
- "Uptime today: ",
36401
- /* @__PURE__ */ jsxs("span", { className: "font-medium", children: [
36402
- workspace.uptimePercentage.toFixed(1),
36403
- "%"
36404
- ] })
36405
- ] })
36406
- ] }),
36407
- workspace.uptimeDetails && workspace.uptimeDetails.expectedMinutes > workspace.uptimeDetails.actualMinutes && workspace.uptimePercentage !== void 0 && /* @__PURE__ */ jsx("div", { className: "flex", children: /* @__PURE__ */ jsx("span", { className: clsx(
36408
- "inline-flex items-center px-2 py-0.5 rounded text-xs font-medium",
36409
- 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"
36410
- ), children: formatDowntime(workspace.uptimeDetails) }) })
36411
- ] })
36659
+ /* @__PURE__ */ jsx("div", { className: "mt-3 pt-3 border-t border-slate-100 dark:border-slate-800", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
36660
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-slate-500 uppercase tracking-wide", children: "Total Downtime" }),
36661
+ /* @__PURE__ */ jsx("span", { className: clsx("text-sm font-semibold", downtimeConfig.className), children: downtimeConfig.text })
36662
+ ] }) })
36663
+ ] }),
36664
+ onViewDetails && /* @__PURE__ */ jsx("div", { className: "pt-4 mt-auto", children: /* @__PURE__ */ jsx(
36665
+ "button",
36666
+ {
36667
+ onClick: handleViewDetails,
36668
+ className: "w-full inline-flex items-center justify-center rounded-lg border border-slate-200/60 bg-white/60 px-3 py-2 text-sm font-medium text-slate-700 shadow-sm backdrop-blur-sm transition-all hover:bg-white/90 hover:text-slate-900 dark:bg-white/10 dark:text-slate-200 dark:border-white/10 dark:hover:bg-white/20",
36669
+ type: "button",
36670
+ children: "View shift timeline"
36671
+ }
36672
+ ) })
36412
36673
  ] })
36413
36674
  }
36414
36675
  );
@@ -36416,8 +36677,11 @@ var WorkspaceHealthCard = ({
36416
36677
  var CompactWorkspaceHealthCard = ({
36417
36678
  workspace,
36418
36679
  onClick,
36419
- className = ""
36680
+ className = "",
36681
+ onViewDetails
36420
36682
  }) => {
36683
+ const downtimeMinutes = workspace.uptimeDetails ? Math.max(0, workspace.uptimeDetails.expectedMinutes - workspace.uptimeDetails.actualMinutes) : null;
36684
+ const downtimeLabel = downtimeMinutes === null ? "No downtime data" : downtimeMinutes === 0 ? "No downtime" : `${downtimeMinutes} min down`;
36421
36685
  const getStatusConfig = () => {
36422
36686
  switch (workspace.status) {
36423
36687
  case "healthy":
@@ -36457,6 +36721,13 @@ var CompactWorkspaceHealthCard = ({
36457
36721
  onClick(workspace);
36458
36722
  }
36459
36723
  };
36724
+ const handleViewDetails = (event) => {
36725
+ event.stopPropagation();
36726
+ event.preventDefault();
36727
+ if (onViewDetails) {
36728
+ onViewDetails(workspace);
36729
+ }
36730
+ };
36460
36731
  return /* @__PURE__ */ jsxs(
36461
36732
  "div",
36462
36733
  {
@@ -36484,14 +36755,20 @@ var CompactWorkspaceHealthCard = ({
36484
36755
  workspace.uptimePercentage.toFixed(1),
36485
36756
  "%"
36486
36757
  ] }),
36487
- workspace.uptimeDetails && workspace.uptimeDetails.expectedMinutes > workspace.uptimeDetails.actualMinutes && /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-400 dark:text-gray-400", children: "\u2022" }),
36488
- workspace.uptimeDetails && workspace.uptimeDetails.expectedMinutes > workspace.uptimeDetails.actualMinutes && /* @__PURE__ */ jsxs("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: [
36489
- Math.max(0, workspace.uptimeDetails.expectedMinutes - workspace.uptimeDetails.actualMinutes),
36490
- "m down"
36491
- ] })
36758
+ downtimeMinutes !== null && downtimeMinutes > 0 && /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-400 dark:text-gray-400", children: "\u2022" }),
36759
+ downtimeLabel && /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: downtimeLabel })
36492
36760
  ] }),
36493
36761
  /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: workspace.timeSinceLastUpdate }),
36494
- /* @__PURE__ */ jsx("div", { className: clsx("h-2 w-2 rounded-full", config.dot) })
36762
+ /* @__PURE__ */ jsx("div", { className: clsx("h-2 w-2 rounded-full", config.dot) }),
36763
+ onViewDetails && /* @__PURE__ */ jsx(
36764
+ "button",
36765
+ {
36766
+ onClick: handleViewDetails,
36767
+ className: "rounded-full border border-gray-200 px-2 py-0.5 text-[11px] font-medium text-gray-600 hover:bg-gray-50",
36768
+ type: "button",
36769
+ children: "View"
36770
+ }
36771
+ )
36495
36772
  ] })
36496
36773
  ]
36497
36774
  }
@@ -36500,6 +36777,7 @@ var CompactWorkspaceHealthCard = ({
36500
36777
  var HealthStatusGrid = ({
36501
36778
  workspaces,
36502
36779
  onWorkspaceClick,
36780
+ onWorkspaceViewDetails,
36503
36781
  showFilters = true,
36504
36782
  groupBy: initialGroupBy = "none",
36505
36783
  className = ""
@@ -36687,7 +36965,8 @@ var HealthStatusGrid = ({
36687
36965
  {
36688
36966
  workspace,
36689
36967
  onClick: onWorkspaceClick,
36690
- showDetails: true
36968
+ showDetails: true,
36969
+ onViewDetails: onWorkspaceViewDetails
36691
36970
  },
36692
36971
  workspace.workspace_id
36693
36972
  )) })
@@ -40223,7 +40502,7 @@ var AIAgentView = () => {
40223
40502
  }
40224
40503
  return formattedLines.join("");
40225
40504
  };
40226
- const formatTime4 = (timestamp) => {
40505
+ const formatTime5 = (timestamp) => {
40227
40506
  const date = new Date(timestamp);
40228
40507
  return date.toLocaleTimeString([], {
40229
40508
  hour: "2-digit",
@@ -41487,7 +41766,7 @@ var AIAgentView = () => {
41487
41766
  }
41488
41767
  ),
41489
41768
  /* @__PURE__ */ jsxs("div", { className: `mt-1.5 sm:mt-2 flex items-center gap-2 text-xs text-gray-400 ${message.role === "user" ? "justify-end" : "justify-start"}`, children: [
41490
- /* @__PURE__ */ jsx("span", { children: formatTime4(message.created_at) }),
41769
+ /* @__PURE__ */ jsx("span", { children: formatTime5(message.created_at) }),
41491
41770
  message.role === "assistant" && message.id !== -1 && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
41492
41771
  /* @__PURE__ */ jsx("div", { className: "w-1 h-1 bg-gray-300 rounded-full" }),
41493
41772
  /* @__PURE__ */ jsx("span", { children: "Axel" })
@@ -44318,8 +44597,7 @@ var KPIsOverviewView = ({
44318
44597
  " Shift"
44319
44598
  ] })
44320
44599
  ] })
44321
- ] }) }),
44322
- /* @__PURE__ */ jsx("p", { className: "text-xs sm:text-sm text-gray-600 text-center mt-2 sm:mt-3 px-2 sm:px-0", children: "Click on any line to view detailed performance metrics" })
44600
+ ] }) })
44323
44601
  ] }) }),
44324
44602
  /* @__PURE__ */ jsx("main", { className: "flex-1 p-3 sm:p-4 md:p-6 overflow-y-auto", children: /* @__PURE__ */ 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__ */ jsx(
44325
44603
  LineCard,
@@ -48636,18 +48914,41 @@ var useWorkspaceHealth = (options) => {
48636
48914
  const isFetchingRef = useRef(false);
48637
48915
  const refreshIntervalRef = useRef(null);
48638
48916
  const healthTable = databaseConfig?.tables?.workspace_health || "workspace_health_status";
48639
- const calculateHealthStatus = (workspace) => {
48640
- if (!workspace.last_heartbeat) return "unknown";
48641
- const now2 = /* @__PURE__ */ new Date();
48642
- const lastHeartbeat = new Date(workspace.last_heartbeat);
48643
- const minutesSince = (now2.getTime() - lastHeartbeat.getTime()) / 6e4;
48644
- if (minutesSince < 5) return "healthy";
48645
- if (minutesSince < 15) return "warning";
48646
- return "unhealthy";
48647
- };
48917
+ const computeSummary = useCallback((data) => {
48918
+ const total = data.length;
48919
+ const healthy = data.filter((w) => w.status === "healthy").length;
48920
+ const unhealthy = data.filter((w) => w.status === "unhealthy").length;
48921
+ const warning6 = data.filter((w) => w.status === "warning").length;
48922
+ let uptimePercentage = total > 0 ? healthy / total * 100 : 100;
48923
+ let totalDowntimeMinutes;
48924
+ const withUptime = data.filter(
48925
+ (w) => w.uptimePercentage !== void 0 && w.uptimeDetails !== void 0
48926
+ );
48927
+ if (withUptime.length > 0) {
48928
+ const totalUptime = withUptime.reduce((sum, w) => sum + (w.uptimePercentage || 0), 0);
48929
+ uptimePercentage = totalUptime / withUptime.length;
48930
+ totalDowntimeMinutes = withUptime.reduce((sum, w) => {
48931
+ if (w.uptimeDetails) {
48932
+ return sum + Math.max(0, w.uptimeDetails.expectedMinutes - w.uptimeDetails.actualMinutes);
48933
+ }
48934
+ return sum;
48935
+ }, 0);
48936
+ }
48937
+ return {
48938
+ totalWorkspaces: total,
48939
+ healthyWorkspaces: healthy,
48940
+ unhealthyWorkspaces: unhealthy,
48941
+ warningWorkspaces: warning6,
48942
+ uptimePercentage,
48943
+ totalDowntimeMinutes,
48944
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
48945
+ };
48946
+ }, []);
48648
48947
  const fetchWorkspacesHealth = useCallback(async () => {
48649
- if (!supabase || isFetchingRef.current) return;
48948
+ if (isFetchingRef.current) return;
48650
48949
  if (!options.companyId) {
48950
+ setWorkspaces([]);
48951
+ setSummary(computeSummary([]));
48651
48952
  setLoading(false);
48652
48953
  return;
48653
48954
  }
@@ -48655,39 +48956,12 @@ var useWorkspaceHealth = (options) => {
48655
48956
  isFetchingRef.current = true;
48656
48957
  setLoading(true);
48657
48958
  setError(null);
48658
- let query = supabase.from(healthTable).select("*").eq("company_id", options.companyId);
48659
- if (options.lineId) {
48660
- query = query.eq("line_id", options.lineId);
48661
- }
48662
- query = query.order("workspace_display_name", { ascending: true });
48663
- const { data, error: fetchError } = await query;
48664
- if (fetchError) throw fetchError;
48665
- const healthData = data || [];
48666
- const workspacesWithStatus = healthData.map((ws) => {
48667
- const status = calculateHealthStatus(ws);
48668
- const now2 = /* @__PURE__ */ new Date();
48669
- const lastUpdate = ws.last_heartbeat ? new Date(ws.last_heartbeat) : null;
48670
- const timeSinceLastUpdate = lastUpdate ? `${Math.floor((now2.getTime() - lastUpdate.getTime()) / 6e4)}m ago` : "Never";
48671
- return {
48672
- ...ws,
48673
- status,
48674
- timeSinceLastUpdate,
48675
- isStale: !lastUpdate || now2.getTime() - lastUpdate.getTime() > 15 * 6e4
48676
- };
48959
+ const workspacesWithStatus = await workspaceHealthService.getWorkspaceHealthStatus({
48960
+ lineId: options.lineId,
48961
+ companyId: options.companyId
48677
48962
  });
48678
48963
  setWorkspaces(workspacesWithStatus);
48679
- const total = workspacesWithStatus.length;
48680
- const healthy = workspacesWithStatus.filter((w) => w.status === "healthy").length;
48681
- const unhealthy = workspacesWithStatus.filter((w) => w.status === "unhealthy").length;
48682
- const warning6 = workspacesWithStatus.filter((w) => w.status === "warning").length;
48683
- setSummary({
48684
- totalWorkspaces: total,
48685
- healthyWorkspaces: healthy,
48686
- unhealthyWorkspaces: unhealthy,
48687
- warningWorkspaces: warning6,
48688
- uptimePercentage: total > 0 ? healthy / total * 100 : 100,
48689
- lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
48690
- });
48964
+ setSummary(computeSummary(workspacesWithStatus));
48691
48965
  } catch (err) {
48692
48966
  console.error("[useWorkspaceHealth] Error fetching workspace health:", err);
48693
48967
  setError({ message: err.message, code: err.code || "FETCH_ERROR" });
@@ -48696,7 +48970,7 @@ var useWorkspaceHealth = (options) => {
48696
48970
  setLoading(false);
48697
48971
  isFetchingRef.current = false;
48698
48972
  }
48699
- }, [supabase, options.companyId, options.lineId, healthTable]);
48973
+ }, [options.companyId, options.lineId, computeSummary]);
48700
48974
  useEffect(() => {
48701
48975
  fetchWorkspacesHealth();
48702
48976
  }, [fetchWorkspacesHealth]);
@@ -48741,6 +49015,395 @@ var useWorkspaceHealth = (options) => {
48741
49015
  refetch: fetchWorkspacesHealth
48742
49016
  };
48743
49017
  };
49018
+ var STATUS_COLORS = {
49019
+ up: "bg-emerald-500",
49020
+ down: "bg-rose-500",
49021
+ pending: "bg-gray-200"
49022
+ };
49023
+ var STATUS_TITLES = {
49024
+ up: "Uptime",
49025
+ down: "Downtime",
49026
+ pending: "Pending"
49027
+ };
49028
+ var formatTime4 = (date, timezone) => new Intl.DateTimeFormat("en-IN", {
49029
+ hour: "numeric",
49030
+ minute: "2-digit",
49031
+ hour12: true,
49032
+ timeZone: timezone
49033
+ }).format(date);
49034
+ var formatDuration = (minutes) => {
49035
+ if (minutes < 1) return "<1 min";
49036
+ if (minutes < 60) return `${minutes} min`;
49037
+ const hours = Math.floor(minutes / 60);
49038
+ const remainder = minutes % 60;
49039
+ if (remainder === 0) return `${hours} hr${hours > 1 ? "s" : ""}`;
49040
+ return `${hours} hr ${remainder} min`;
49041
+ };
49042
+ var formatDowntimeLabel = (minutes, includeSuffix = true) => {
49043
+ if (!minutes || minutes <= 0) {
49044
+ return includeSuffix ? "No downtime" : "0 min";
49045
+ }
49046
+ const rounded = Math.max(Math.round(minutes), 0);
49047
+ const days = Math.floor(rounded / 1440);
49048
+ const hours = Math.floor(rounded % 1440 / 60);
49049
+ const mins = rounded % 60;
49050
+ const parts = [];
49051
+ if (days) parts.push(`${days} day${days === 1 ? "" : "s"}`);
49052
+ if (hours) parts.push(`${hours} hr${hours === 1 ? "" : "s"}`);
49053
+ if (mins) parts.push(`${mins} min${mins === 1 ? "" : "s"}`);
49054
+ if (!parts.length) {
49055
+ parts.push("1 min");
49056
+ }
49057
+ const label = parts.join(" ");
49058
+ return includeSuffix ? `${label} down` : label;
49059
+ };
49060
+ var UptimeTimelineStrip = ({
49061
+ points,
49062
+ totalMinutes,
49063
+ shiftStart,
49064
+ shiftEnd,
49065
+ timezone,
49066
+ className = "",
49067
+ uptimePercentage = 0,
49068
+ downtimeMinutes = 0
49069
+ }) => {
49070
+ const segments = useMemo(() => {
49071
+ if (!points.length || totalMinutes <= 0) return [];
49072
+ const result = [];
49073
+ let current = {
49074
+ status: points[0].status,
49075
+ length: 1,
49076
+ startMinuteIndex: points[0].minuteIndex,
49077
+ startTimestamp: points[0].timestamp,
49078
+ endTimestamp: points[0].timestamp
49079
+ };
49080
+ for (let i = 1; i < points.length; i++) {
49081
+ const point = points[i];
49082
+ if (point.status === current.status) {
49083
+ current.length += 1;
49084
+ current.endTimestamp = point.timestamp;
49085
+ } else {
49086
+ result.push(current);
49087
+ current = {
49088
+ status: point.status,
49089
+ length: 1,
49090
+ startMinuteIndex: point.minuteIndex,
49091
+ startTimestamp: point.timestamp,
49092
+ endTimestamp: point.timestamp
49093
+ };
49094
+ }
49095
+ }
49096
+ result.push(current);
49097
+ return result;
49098
+ }, [points, totalMinutes]);
49099
+ const markers = useMemo(() => {
49100
+ if (totalMinutes <= 0) return [];
49101
+ const startDate = new Date(shiftStart);
49102
+ const endDate = new Date(shiftEnd);
49103
+ const roundedTotal = Math.max(totalMinutes, 1);
49104
+ const markerInterval = totalMinutes > 360 ? 120 : 60;
49105
+ const markerList = [];
49106
+ for (let minute = 0; minute <= roundedTotal; minute += markerInterval) {
49107
+ const markerMinute = Math.min(minute, roundedTotal);
49108
+ const markerDate = addMinutes(startDate, markerMinute);
49109
+ markerList.push({
49110
+ minute: markerMinute,
49111
+ label: formatTime4(markerDate, timezone)
49112
+ });
49113
+ }
49114
+ const endLabel = formatTime4(endDate, timezone);
49115
+ if (!markerList.some((marker) => marker.minute === roundedTotal)) {
49116
+ markerList.push({
49117
+ minute: roundedTotal,
49118
+ label: endLabel
49119
+ });
49120
+ } else {
49121
+ markerList[markerList.length - 1] = {
49122
+ minute: roundedTotal,
49123
+ label: endLabel
49124
+ };
49125
+ }
49126
+ return markerList;
49127
+ }, [shiftStart, shiftEnd, timezone, totalMinutes]);
49128
+ if (!points.length || totalMinutes <= 0) {
49129
+ return /* @__PURE__ */ jsx("div", { className: "w-full rounded-xl border border-dashed border-gray-200 bg-gray-50/50 p-6 text-center text-sm text-gray-600", children: "No uptime data available for this shift yet." });
49130
+ }
49131
+ return /* @__PURE__ */ jsxs("div", { className: `relative w-full ${className}`, children: [
49132
+ /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center mb-3 text-sm", children: /* @__PURE__ */ jsxs("span", { className: "text-gray-900 font-semibold", children: [
49133
+ uptimePercentage.toFixed(1),
49134
+ " % uptime ",
49135
+ downtimeMinutes > 0 && /* @__PURE__ */ jsxs("span", { className: "text-gray-600 font-normal", children: [
49136
+ "(",
49137
+ formatDowntimeLabel(downtimeMinutes),
49138
+ ")"
49139
+ ] })
49140
+ ] }) }),
49141
+ /* @__PURE__ */ jsx("div", { className: "relative flex h-4 overflow-hidden rounded-lg border border-gray-200 shadow-sm bg-white", children: segments.map((segment, index) => {
49142
+ const startDate = new Date(segment.startTimestamp);
49143
+ const endDate = new Date(segment.endTimestamp);
49144
+ if (segment.length > 1) {
49145
+ endDate.setMinutes(endDate.getMinutes() + 1);
49146
+ }
49147
+ const tooltip = `${STATUS_TITLES[segment.status]} \u2022 ${formatDuration(segment.length)} (${formatTime4(
49148
+ startDate,
49149
+ timezone
49150
+ )} - ${formatTime4(endDate, timezone)})`;
49151
+ return /* @__PURE__ */ jsx(
49152
+ "div",
49153
+ {
49154
+ className: `${STATUS_COLORS[segment.status]} transition-all hover:opacity-80 cursor-pointer`,
49155
+ style: { flex: segment.length },
49156
+ title: tooltip
49157
+ },
49158
+ `${segment.status}-${segment.startMinuteIndex}-${index}`
49159
+ );
49160
+ }) }),
49161
+ /* @__PURE__ */ jsx("div", { className: "pointer-events-none relative w-full mt-4 min-h-6", children: markers.map((marker) => {
49162
+ const left = totalMinutes > 0 ? marker.minute / totalMinutes * 100 : 0;
49163
+ return /* @__PURE__ */ jsxs(
49164
+ "div",
49165
+ {
49166
+ className: "absolute flex -translate-x-1/2 flex-col items-center",
49167
+ style: { left: `${left}%` },
49168
+ children: [
49169
+ /* @__PURE__ */ jsx("span", { className: "mb-1.5 h-2 w-px bg-gray-300" }),
49170
+ /* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium text-gray-600 whitespace-nowrap", children: marker.label })
49171
+ ]
49172
+ },
49173
+ `${marker.label}-${marker.minute}`
49174
+ );
49175
+ }) })
49176
+ ] });
49177
+ };
49178
+ var UptimeTimelineStrip_default = UptimeTimelineStrip;
49179
+ var SHORT_INTERRUPT_THRESHOLD_MINUTES = 3;
49180
+ var formatDuration2 = (minutes) => {
49181
+ if (!minutes || minutes <= 0) return "0 min";
49182
+ const rounded = Math.max(Math.round(minutes), 0);
49183
+ const days = Math.floor(rounded / 1440);
49184
+ const hours = Math.floor(rounded % 1440 / 60);
49185
+ const mins = rounded % 60;
49186
+ const parts = [];
49187
+ if (days) parts.push(`${days} day${days === 1 ? "" : "s"}`);
49188
+ if (hours) parts.push(`${hours} hr${hours === 1 ? "" : "s"}`);
49189
+ if (mins) parts.push(`${mins} min${mins === 1 ? "" : "s"}`);
49190
+ if (!parts.length) {
49191
+ parts.push("1 min");
49192
+ }
49193
+ return parts.join(" ");
49194
+ };
49195
+ var formatDowntimeLabel2 = (minutes, includeSuffix = true) => {
49196
+ if (!minutes || minutes <= 0) {
49197
+ return includeSuffix ? "No downtime" : "0 min";
49198
+ }
49199
+ const label = formatDuration2(minutes);
49200
+ return includeSuffix ? `${label} down` : label;
49201
+ };
49202
+ var formatTimeRange = (start, end, timezone) => {
49203
+ const formatter = new Intl.DateTimeFormat("en-IN", {
49204
+ hour: "numeric",
49205
+ minute: "2-digit",
49206
+ hour12: true,
49207
+ timeZone: timezone
49208
+ });
49209
+ return `${formatter.format(start)} - ${formatter.format(end)}`;
49210
+ };
49211
+ var WorkspaceUptimeDetailModal = ({
49212
+ workspace,
49213
+ isOpen,
49214
+ onClose
49215
+ }) => {
49216
+ const timezone = useAppTimezone() || "UTC";
49217
+ const logsContainerRef = useRef(null);
49218
+ const [showScrollIndicator, setShowScrollIndicator] = useState(false);
49219
+ const {
49220
+ timeline,
49221
+ loading,
49222
+ error,
49223
+ refetch
49224
+ } = useWorkspaceUptimeTimeline({
49225
+ workspaceId: workspace?.workspace_id,
49226
+ companyId: workspace?.company_id,
49227
+ enabled: isOpen && Boolean(workspace?.workspace_id && workspace?.company_id),
49228
+ refreshInterval: 6e4
49229
+ });
49230
+ useEffect(() => {
49231
+ if (!isOpen || !workspace) return;
49232
+ const handleKeyDown = (event) => {
49233
+ if (event.key === "Escape") {
49234
+ onClose();
49235
+ }
49236
+ };
49237
+ window.addEventListener("keydown", handleKeyDown);
49238
+ return () => {
49239
+ window.removeEventListener("keydown", handleKeyDown);
49240
+ };
49241
+ }, [isOpen, onClose, workspace]);
49242
+ const shiftStart = timeline ? new Date(timeline.shiftStart) : null;
49243
+ const shiftEnd = timeline ? new Date(timeline.shiftEnd) : null;
49244
+ const downtimeSegments = timeline?.downtimeSegments || [];
49245
+ downtimeSegments.length;
49246
+ const downtimeMinutes = timeline?.downtimeMinutes ?? 0;
49247
+ const uptimePercentage = timeline?.uptimePercentage ?? workspace?.uptimePercentage ?? 0;
49248
+ const allInterruptionsSorted = useMemo(
49249
+ () => [...downtimeSegments].sort(
49250
+ (a, b) => new Date(b.startTime).getTime() - new Date(a.startTime).getTime()
49251
+ ),
49252
+ [downtimeSegments]
49253
+ );
49254
+ useEffect(() => {
49255
+ const checkScroll = () => {
49256
+ const container2 = logsContainerRef.current;
49257
+ if (container2) {
49258
+ const hasScroll = container2.scrollHeight > container2.clientHeight;
49259
+ const isAtBottom = container2.scrollHeight - container2.scrollTop <= container2.clientHeight + 10;
49260
+ setShowScrollIndicator(hasScroll && !isAtBottom);
49261
+ }
49262
+ };
49263
+ checkScroll();
49264
+ const container = logsContainerRef.current;
49265
+ if (container) {
49266
+ container.addEventListener("scroll", checkScroll);
49267
+ return () => container.removeEventListener("scroll", checkScroll);
49268
+ }
49269
+ }, [downtimeSegments]);
49270
+ const renderSegment = (segment) => {
49271
+ const start = new Date(segment.startTime);
49272
+ const end = new Date(segment.endTime);
49273
+ const isMajor = segment.durationMinutes >= SHORT_INTERRUPT_THRESHOLD_MINUTES;
49274
+ const containerClasses = isMajor ? "border-rose-200 bg-rose-50" : "border-gray-200 bg-white";
49275
+ return /* @__PURE__ */ jsxs(
49276
+ "div",
49277
+ {
49278
+ className: `rounded-lg border px-5 py-3 ${containerClasses}`,
49279
+ children: [
49280
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-gray-900", children: formatTimeRange(start, end, timezone) }),
49281
+ /* @__PURE__ */ jsxs("p", { className: "text-xs text-gray-600 mt-1", children: [
49282
+ "Duration: ",
49283
+ formatDowntimeLabel2(segment.durationMinutes, false)
49284
+ ] })
49285
+ ]
49286
+ },
49287
+ `${segment.startMinuteIndex}-${segment.endMinuteIndex}`
49288
+ );
49289
+ };
49290
+ if (!isOpen || !workspace) {
49291
+ return null;
49292
+ }
49293
+ return /* @__PURE__ */ jsx(
49294
+ "div",
49295
+ {
49296
+ className: "fixed inset-0 z-[60] flex items-center justify-center bg-black/40 backdrop-blur-sm p-4",
49297
+ onClick: onClose,
49298
+ "aria-modal": "true",
49299
+ role: "dialog",
49300
+ "aria-labelledby": "uptime-detail-title",
49301
+ children: /* @__PURE__ */ jsxs(
49302
+ "div",
49303
+ {
49304
+ className: "relative flex w-full max-w-4xl max-h-[90vh] flex-col rounded-2xl bg-white shadow-2xl border border-gray-100",
49305
+ onClick: (event) => event.stopPropagation(),
49306
+ role: "document",
49307
+ "aria-labelledby": "uptime-detail-title",
49308
+ children: [
49309
+ /* @__PURE__ */ jsxs("header", { className: "flex items-start justify-between border-b border-gray-100 px-8 py-6 sticky top-0 z-10 bg-white rounded-t-2xl", children: [
49310
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0 mr-4", children: [
49311
+ /* @__PURE__ */ jsx("h2", { id: "uptime-detail-title", className: "text-2xl font-semibold text-gray-900 truncate mb-3", children: workspace.workspace_display_name || `Workspace ${workspace.workspace_id.slice(0, 6)}` }),
49312
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5 text-sm text-gray-600", children: [
49313
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-gray-900", children: timeline?.shiftLabel || "Current Shift" }),
49314
+ shiftStart && shiftEnd && /* @__PURE__ */ jsxs(Fragment, { children: [
49315
+ /* @__PURE__ */ jsx("span", { className: "text-gray-300", children: "\u2022" }),
49316
+ /* @__PURE__ */ jsx("span", { className: "text-gray-600", children: formatTimeRange(shiftStart, shiftEnd, timezone) })
49317
+ ] })
49318
+ ] }),
49319
+ /* @__PURE__ */ jsxs("div", { className: "mt-2 flex items-center gap-2", children: [
49320
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
49321
+ /* @__PURE__ */ jsx("div", { className: `w-2 h-2 rounded-full ${workspace.status === "healthy" ? "bg-emerald-500 animate-pulse" : workspace.status === "warning" ? "bg-amber-500" : "bg-rose-500"}` }),
49322
+ /* @__PURE__ */ jsx("span", { className: `text-xs font-medium ${workspace.status === "healthy" ? "text-emerald-700" : workspace.status === "warning" ? "text-amber-700" : "text-rose-700"}`, children: workspace.status === "healthy" ? "Operational" : workspace.status === "warning" ? "Intermittent" : "Down" })
49323
+ ] }),
49324
+ /* @__PURE__ */ jsx("span", { className: "text-gray-300", children: "\u2022" }),
49325
+ /* @__PURE__ */ jsxs("p", { className: "text-xs text-gray-500", children: [
49326
+ "Last heartbeat ",
49327
+ workspace.timeSinceLastUpdate
49328
+ ] })
49329
+ ] })
49330
+ ] }),
49331
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 flex-shrink-0", children: [
49332
+ /* @__PURE__ */ jsxs(
49333
+ "button",
49334
+ {
49335
+ onClick: refetch,
49336
+ disabled: loading,
49337
+ className: "inline-flex items-center gap-2 rounded-lg border border-gray-200 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 disabled:cursor-not-allowed disabled:opacity-50 transition-all duration-200 shadow-sm",
49338
+ children: [
49339
+ /* @__PURE__ */ jsx(RefreshCw, { className: `h-4 w-4 ${loading ? "animate-spin" : ""}` }),
49340
+ /* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: "Refresh" })
49341
+ ]
49342
+ }
49343
+ ),
49344
+ /* @__PURE__ */ jsx(
49345
+ "button",
49346
+ {
49347
+ onClick: onClose,
49348
+ className: "rounded-lg p-2 text-gray-400 hover:bg-gray-100 hover:text-gray-600 transition-colors duration-200",
49349
+ "aria-label": "Close uptime details",
49350
+ children: /* @__PURE__ */ jsx(X, { className: "h-5 w-5" })
49351
+ }
49352
+ )
49353
+ ] })
49354
+ ] }),
49355
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto", children: /* @__PURE__ */ jsx("div", { className: "px-8 py-6 space-y-6", children: error ? /* @__PURE__ */ jsxs("div", { className: "rounded-xl border border-rose-100 bg-rose-50 p-5 text-sm text-rose-700", children: [
49356
+ /* @__PURE__ */ jsx("p", { className: "font-semibold mb-1", children: "Unable to load uptime details" }),
49357
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-rose-600/90", children: error.message })
49358
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
49359
+ /* @__PURE__ */ jsxs("div", { className: "relative pb-4 border-b border-gray-200", children: [
49360
+ /* @__PURE__ */ jsx(
49361
+ UptimeTimelineStrip_default,
49362
+ {
49363
+ points: timeline?.points || [],
49364
+ totalMinutes: timeline?.totalMinutes || 0,
49365
+ shiftStart: timeline?.shiftStart || (/* @__PURE__ */ new Date()).toISOString(),
49366
+ shiftEnd: timeline?.shiftEnd || (/* @__PURE__ */ new Date()).toISOString(),
49367
+ timezone,
49368
+ uptimePercentage,
49369
+ downtimeMinutes
49370
+ }
49371
+ ),
49372
+ loading && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm text-gray-600 mt-4", children: [
49373
+ /* @__PURE__ */ jsx(RefreshCw, { className: "h-4 w-4 animate-spin text-gray-500" }),
49374
+ /* @__PURE__ */ jsx("span", { children: "Updating timeline\u2026" })
49375
+ ] })
49376
+ ] }),
49377
+ /* @__PURE__ */ jsxs("div", { className: "pt-4", children: [
49378
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-gray-900 uppercase tracking-wider mb-3", children: "Downtime Logs" }),
49379
+ downtimeSegments.length === 0 ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-gray-100 bg-gray-50/50 px-5 py-4 text-center", children: /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-600", children: "No downtime events recorded for this shift." }) }) : /* @__PURE__ */ jsxs("div", { className: "relative", children: [
49380
+ /* @__PURE__ */ jsx(
49381
+ "div",
49382
+ {
49383
+ ref: logsContainerRef,
49384
+ className: "max-h-[400px] overflow-y-auto space-y-2 pr-2",
49385
+ style: {
49386
+ scrollbarWidth: "thin",
49387
+ scrollbarColor: "#CBD5E0 #F7FAFC"
49388
+ },
49389
+ children: allInterruptionsSorted.map((segment) => renderSegment(segment))
49390
+ }
49391
+ ),
49392
+ showScrollIndicator && /* @__PURE__ */ jsx("div", { className: "absolute bottom-0 left-0 right-0 h-12 bg-gradient-to-t from-white via-white/80 to-transparent pointer-events-none flex items-end justify-center pb-2", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 text-xs text-gray-500 animate-bounce", children: [
49393
+ /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4" }),
49394
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: "Scroll for more" }),
49395
+ /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4" })
49396
+ ] }) })
49397
+ ] })
49398
+ ] })
49399
+ ] }) }) })
49400
+ ]
49401
+ }
49402
+ )
49403
+ }
49404
+ );
49405
+ };
49406
+ var WorkspaceUptimeDetailModal_default = WorkspaceUptimeDetailModal;
48744
49407
  var WorkspaceHealthView = ({
48745
49408
  lineId,
48746
49409
  companyId,
@@ -48750,6 +49413,7 @@ var WorkspaceHealthView = ({
48750
49413
  const router = useRouter();
48751
49414
  const [groupBy, setGroupBy] = useState("line");
48752
49415
  const timezone = useAppTimezone();
49416
+ const [selectedWorkspace, setSelectedWorkspace] = useState(null);
48753
49417
  const operationalDate = getOperationalDate(timezone || "UTC");
48754
49418
  const currentHour = (/* @__PURE__ */ new Date()).getHours();
48755
49419
  const isNightShift = currentHour >= 18 || currentHour < 6;
@@ -48790,6 +49454,12 @@ var WorkspaceHealthView = ({
48790
49454
  },
48791
49455
  [router, onNavigate]
48792
49456
  );
49457
+ const handleViewDetails = useCallback((workspace) => {
49458
+ setSelectedWorkspace(workspace);
49459
+ }, []);
49460
+ const handleCloseDetails = useCallback(() => {
49461
+ setSelectedWorkspace(null);
49462
+ }, []);
48793
49463
  const handleExport = useCallback(() => {
48794
49464
  const csv = [
48795
49465
  ["Workspace", "Line", "Company", "Status", "Last Heartbeat", "Consecutive Misses"],
@@ -48847,178 +49517,189 @@ var WorkspaceHealthView = ({
48847
49517
  )
48848
49518
  ] }) }) }) });
48849
49519
  }
48850
- return /* @__PURE__ */ jsxs("div", { className: clsx("min-h-screen bg-slate-50", className), children: [
48851
- /* @__PURE__ */ jsxs("header", { className: "sticky top-0 z-10 px-3 sm:px-4 md:px-5 lg:px-6 py-2 sm:py-2.5 lg:py-3 flex flex-col shadow-sm bg-white", children: [
48852
- /* @__PURE__ */ jsxs("div", { className: "sm:hidden", children: [
48853
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-2", children: [
48854
- /* @__PURE__ */ jsx(
49520
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
49521
+ /* @__PURE__ */ jsxs("div", { className: clsx("min-h-screen bg-slate-50", className), children: [
49522
+ /* @__PURE__ */ jsxs("header", { className: "sticky top-0 z-10 px-3 sm:px-4 md:px-5 lg:px-6 py-2 sm:py-2.5 lg:py-3 flex flex-col shadow-sm bg-white", children: [
49523
+ /* @__PURE__ */ jsxs("div", { className: "sm:hidden", children: [
49524
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-1", children: [
49525
+ /* @__PURE__ */ jsx(
49526
+ BackButtonMinimal,
49527
+ {
49528
+ onClick: () => router.push("/"),
49529
+ text: "Back",
49530
+ size: "sm",
49531
+ "aria-label": "Navigate back to dashboard"
49532
+ }
49533
+ ),
49534
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
49535
+ /* @__PURE__ */ jsx(
49536
+ "button",
49537
+ {
49538
+ onClick: () => {
49539
+ refetch();
49540
+ },
49541
+ className: "p-1.5 text-gray-600 hover:bg-gray-100 rounded-lg transition-colors",
49542
+ "aria-label": "Refresh",
49543
+ children: /* @__PURE__ */ jsx(RefreshCw, { className: "h-4 w-4" })
49544
+ }
49545
+ ),
49546
+ /* @__PURE__ */ jsx(
49547
+ "button",
49548
+ {
49549
+ onClick: handleExport,
49550
+ className: "p-1.5 text-gray-600 hover:bg-gray-100 rounded-lg transition-colors",
49551
+ "aria-label": "Export CSV",
49552
+ children: /* @__PURE__ */ jsx(Download, { className: "h-4 w-4" })
49553
+ }
49554
+ )
49555
+ ] })
49556
+ ] }),
49557
+ /* @__PURE__ */ jsx("div", { className: "text-center", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-2", children: [
49558
+ /* @__PURE__ */ jsx("h1", { className: "text-base font-semibold text-gray-900", children: "System Health" }),
49559
+ /* @__PURE__ */ jsxs("div", { className: "relative flex h-2 w-2", children: [
49560
+ /* @__PURE__ */ jsx("span", { className: "animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-75" }),
49561
+ /* @__PURE__ */ jsx("span", { className: "relative inline-flex rounded-full h-2 w-2 bg-emerald-500" })
49562
+ ] })
49563
+ ] }) })
49564
+ ] }),
49565
+ /* @__PURE__ */ jsx("div", { className: "hidden sm:block", children: /* @__PURE__ */ jsxs("div", { className: "relative flex items-center", children: [
49566
+ /* @__PURE__ */ jsx("div", { className: "absolute left-0 z-10", children: /* @__PURE__ */ jsx(
48855
49567
  BackButtonMinimal,
48856
49568
  {
48857
49569
  onClick: () => router.push("/"),
48858
49570
  text: "Back",
48859
- size: "sm",
49571
+ size: "default",
48860
49572
  "aria-label": "Navigate back to dashboard"
48861
49573
  }
48862
- ),
48863
- /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
49574
+ ) }),
49575
+ /* @__PURE__ */ jsx("div", { className: "absolute left-1/2 transform -translate-x-1/2 max-w-[calc(100%-200px)]", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
49576
+ /* @__PURE__ */ jsx("h1", { className: "text-lg md:text-xl lg:text-2xl xl:text-3xl font-semibold text-gray-900 truncate", children: "System Health" }),
49577
+ /* @__PURE__ */ jsxs("div", { className: "relative flex h-2.5 w-2.5", children: [
49578
+ /* @__PURE__ */ jsx("span", { className: "animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-75" }),
49579
+ /* @__PURE__ */ jsx("span", { className: "relative inline-flex rounded-full h-2.5 w-2.5 bg-emerald-500" })
49580
+ ] })
49581
+ ] }) }),
49582
+ /* @__PURE__ */ jsxs("div", { className: "absolute right-0 flex gap-2", children: [
48864
49583
  /* @__PURE__ */ jsx(
48865
49584
  "button",
48866
49585
  {
48867
49586
  onClick: () => {
48868
49587
  refetch();
48869
49588
  },
48870
- className: "p-1.5 text-gray-600 hover:bg-gray-100 rounded-lg transition-colors",
49589
+ className: "p-2 text-gray-600 hover:bg-gray-100 rounded-lg transition-colors",
48871
49590
  "aria-label": "Refresh",
48872
- children: /* @__PURE__ */ jsx(RefreshCw, { className: "h-4 w-4" })
49591
+ children: /* @__PURE__ */ jsx(RefreshCw, { className: "h-5 w-5" })
48873
49592
  }
48874
49593
  ),
48875
49594
  /* @__PURE__ */ jsx(
48876
49595
  "button",
48877
49596
  {
48878
49597
  onClick: handleExport,
48879
- className: "p-1.5 text-gray-600 hover:bg-gray-100 rounded-lg transition-colors",
49598
+ className: "p-2 text-gray-600 hover:bg-gray-100 rounded-lg transition-colors",
48880
49599
  "aria-label": "Export CSV",
48881
- children: /* @__PURE__ */ jsx(Download, { className: "h-4 w-4" })
49600
+ children: /* @__PURE__ */ jsx(Download, { className: "h-5 w-5" })
48882
49601
  }
48883
49602
  )
48884
- ] })
48885
- ] }),
48886
- /* @__PURE__ */ jsx("div", { className: "text-center", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-2", children: [
48887
- /* @__PURE__ */ jsx("h1", { className: "text-base font-semibold text-gray-900", children: "System Health" }),
48888
- /* @__PURE__ */ jsxs("div", { className: "relative flex h-2 w-2", children: [
48889
- /* @__PURE__ */ jsx("span", { className: "animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-75" }),
48890
- /* @__PURE__ */ jsx("span", { className: "relative inline-flex rounded-full h-2 w-2 bg-emerald-500" })
49603
+ ] }),
49604
+ /* @__PURE__ */ jsx("div", { className: "w-full h-8" })
49605
+ ] }) }),
49606
+ /* @__PURE__ */ jsx("div", { className: "mt-1 sm:mt-2 bg-blue-50 px-2 sm:px-3 py-1.5 sm:py-2 rounded-lg", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-center gap-2 sm:gap-4", children: [
49607
+ /* @__PURE__ */ jsx("div", { className: "text-sm sm:text-base md:text-lg font-medium text-blue-600", children: /* @__PURE__ */ jsx(LiveTimer, {}) }),
49608
+ /* @__PURE__ */ jsx("div", { className: "hidden sm:block w-px h-4 bg-blue-300" }),
49609
+ /* @__PURE__ */ jsx("span", { className: "text-xs sm:text-sm md:text-base font-medium text-blue-600", children: formatDate(operationalDate) }),
49610
+ /* @__PURE__ */ jsx("div", { className: "hidden sm:block w-px h-4 bg-blue-300" }),
49611
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 sm:gap-2", children: [
49612
+ /* @__PURE__ */ jsx("div", { className: "text-blue-600", children: getShiftIcon(shiftType) }),
49613
+ /* @__PURE__ */ jsxs("span", { className: "text-xs sm:text-sm md:text-base font-medium text-blue-600", children: [
49614
+ shiftType,
49615
+ " Shift"
49616
+ ] })
48891
49617
  ] })
48892
49618
  ] }) })
48893
49619
  ] }),
48894
- /* @__PURE__ */ jsx("div", { className: "hidden sm:block", children: /* @__PURE__ */ jsxs("div", { className: "relative flex items-center", children: [
48895
- /* @__PURE__ */ jsx("div", { className: "absolute left-0 z-10", children: /* @__PURE__ */ jsx(
48896
- BackButtonMinimal,
49620
+ /* @__PURE__ */ jsxs("div", { className: "max-w-7xl mx-auto p-4 space-y-6", children: [
49621
+ summary && /* @__PURE__ */ jsxs(
49622
+ motion.div,
48897
49623
  {
48898
- onClick: () => router.push("/"),
48899
- text: "Back",
48900
- size: "default",
48901
- "aria-label": "Navigate back to dashboard"
48902
- }
48903
- ) }),
48904
- /* @__PURE__ */ jsx("div", { className: "absolute left-1/2 transform -translate-x-1/2 max-w-[calc(100%-200px)]", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
48905
- /* @__PURE__ */ jsx("h1", { className: "text-lg md:text-xl lg:text-2xl xl:text-3xl font-semibold text-gray-900 truncate", children: "System Health" }),
48906
- /* @__PURE__ */ jsxs("div", { className: "relative flex h-2.5 w-2.5", children: [
48907
- /* @__PURE__ */ jsx("span", { className: "animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-75" }),
48908
- /* @__PURE__ */ jsx("span", { className: "relative inline-flex rounded-full h-2.5 w-2.5 bg-emerald-500" })
48909
- ] })
48910
- ] }) }),
48911
- /* @__PURE__ */ jsxs("div", { className: "absolute right-0 flex gap-2", children: [
48912
- /* @__PURE__ */ jsx(
48913
- "button",
48914
- {
48915
- onClick: () => {
48916
- refetch();
48917
- },
48918
- className: "p-2 text-gray-600 hover:bg-gray-100 rounded-lg transition-colors",
48919
- "aria-label": "Refresh",
48920
- children: /* @__PURE__ */ jsx(RefreshCw, { className: "h-5 w-5" })
48921
- }
48922
- ),
48923
- /* @__PURE__ */ jsx(
48924
- "button",
48925
- {
48926
- onClick: handleExport,
48927
- className: "p-2 text-gray-600 hover:bg-gray-100 rounded-lg transition-colors",
48928
- "aria-label": "Export CSV",
48929
- children: /* @__PURE__ */ jsx(Download, { className: "h-5 w-5" })
48930
- }
48931
- )
48932
- ] }),
48933
- /* @__PURE__ */ jsx("div", { className: "w-full h-8" })
48934
- ] }) }),
48935
- /* @__PURE__ */ jsx("div", { className: "mt-2 sm:mt-3 bg-blue-50 px-2 sm:px-3 py-1.5 sm:py-2 rounded-lg", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-center gap-2 sm:gap-4", children: [
48936
- /* @__PURE__ */ jsx("div", { className: "text-sm sm:text-base md:text-lg font-medium text-blue-600", children: /* @__PURE__ */ jsx(LiveTimer, {}) }),
48937
- /* @__PURE__ */ jsx("div", { className: "hidden sm:block w-px h-4 bg-blue-300" }),
48938
- /* @__PURE__ */ jsx("span", { className: "text-xs sm:text-sm md:text-base font-medium text-blue-600", children: formatDate(operationalDate) }),
48939
- /* @__PURE__ */ jsx("div", { className: "hidden sm:block w-px h-4 bg-blue-300" }),
48940
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 sm:gap-2", children: [
48941
- /* @__PURE__ */ jsx("div", { className: "text-blue-600", children: getShiftIcon(shiftType) }),
48942
- /* @__PURE__ */ jsxs("span", { className: "text-xs sm:text-sm md:text-base font-medium text-blue-600", children: [
48943
- shiftType,
48944
- " Shift"
48945
- ] })
48946
- ] })
48947
- ] }) })
48948
- ] }),
48949
- /* @__PURE__ */ jsxs("div", { className: "max-w-7xl mx-auto p-4 space-y-6", children: [
48950
- summary && /* @__PURE__ */ jsxs(
48951
- motion.div,
48952
- {
48953
- initial: { opacity: 0, y: 20 },
48954
- animate: { opacity: 1, y: 0 },
48955
- transition: { duration: 0.3, delay: 0.1 },
48956
- className: "grid grid-cols-2 sm:grid-cols-2 md:grid-cols-5 gap-2 sm:gap-3 lg:gap-4",
48957
- children: [
48958
- /* @__PURE__ */ jsxs(Card2, { className: "col-span-2 sm:col-span-2 md:col-span-2 bg-white", children: [
48959
- /* @__PURE__ */ jsx(CardHeader2, { className: "pb-3", children: /* @__PURE__ */ jsx(CardTitle2, { className: "text-sm font-medium text-gray-500 dark:text-gray-400", children: "System Availability" }) }),
48960
- /* @__PURE__ */ jsxs(CardContent2, { children: [
48961
- /* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-2", children: [
48962
- /* @__PURE__ */ jsxs("span", { className: clsx("text-3xl font-bold", getUptimeColor(summary.uptimePercentage)), children: [
48963
- summary.uptimePercentage.toFixed(1),
48964
- "%"
49624
+ initial: { opacity: 0, y: 20 },
49625
+ animate: { opacity: 1, y: 0 },
49626
+ transition: { duration: 0.3, delay: 0.1 },
49627
+ className: "grid grid-cols-2 sm:grid-cols-2 md:grid-cols-5 gap-2 sm:gap-3 lg:gap-4",
49628
+ children: [
49629
+ /* @__PURE__ */ jsxs(Card2, { className: "col-span-2 sm:col-span-2 md:col-span-2 bg-white", children: [
49630
+ /* @__PURE__ */ jsx(CardHeader2, { className: "pb-3", children: /* @__PURE__ */ jsx(CardTitle2, { className: "text-sm font-medium text-gray-500 dark:text-gray-400", children: "System Availability" }) }),
49631
+ /* @__PURE__ */ jsxs(CardContent2, { children: [
49632
+ /* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-2", children: [
49633
+ /* @__PURE__ */ jsxs("span", { className: clsx("text-3xl font-bold", getUptimeColor(summary.uptimePercentage)), children: [
49634
+ summary.uptimePercentage.toFixed(1),
49635
+ "%"
49636
+ ] }),
49637
+ summary.uptimePercentage >= 97 ? /* @__PURE__ */ jsx(TrendingUp, { className: "h-5 w-5 text-green-500" }) : summary.uptimePercentage >= 90 ? /* @__PURE__ */ jsx(Activity, { className: "h-5 w-5 text-yellow-500" }) : /* @__PURE__ */ jsx(TrendingDown, { className: "h-5 w-5 text-red-500" })
48965
49638
  ] }),
48966
- summary.uptimePercentage >= 97 ? /* @__PURE__ */ jsx(TrendingUp, { className: "h-5 w-5 text-green-500" }) : summary.uptimePercentage >= 90 ? /* @__PURE__ */ jsx(Activity, { className: "h-5 w-5 text-yellow-500" }) : /* @__PURE__ */ jsx(TrendingDown, { className: "h-5 w-5 text-red-500" })
48967
- ] }),
48968
- /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: "Overall system uptime today" })
48969
- ] })
48970
- ] }),
48971
- /* @__PURE__ */ jsxs(Card2, { className: "bg-white", children: [
48972
- /* @__PURE__ */ jsx(CardHeader2, { className: "pb-3", children: /* @__PURE__ */ jsxs(CardTitle2, { className: "text-sm font-medium text-gray-500 dark:text-gray-400 flex items-center gap-2", children: [
48973
- getStatusIcon("healthy"),
48974
- "Healthy"
48975
- ] }) }),
48976
- /* @__PURE__ */ jsxs(CardContent2, { children: [
48977
- /* @__PURE__ */ jsx("p", { className: "text-2xl font-bold text-gray-900 dark:text-gray-50", children: summary.healthyWorkspaces }),
48978
- /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: "Operating normally" })
48979
- ] })
48980
- ] }),
48981
- /* @__PURE__ */ jsxs(Card2, { className: "bg-white", children: [
48982
- /* @__PURE__ */ jsx(CardHeader2, { className: "pb-3", children: /* @__PURE__ */ jsxs(CardTitle2, { className: "text-sm font-medium text-gray-500 dark:text-gray-400 flex items-center gap-2", children: [
48983
- getStatusIcon("warning"),
48984
- "Warning"
48985
- ] }) }),
48986
- /* @__PURE__ */ jsxs(CardContent2, { children: [
48987
- /* @__PURE__ */ jsx("p", { className: "text-2xl font-bold text-gray-900 dark:text-gray-50", children: summary.warningWorkspaces }),
48988
- /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: "Delayed updates" })
48989
- ] })
48990
- ] }),
48991
- /* @__PURE__ */ jsxs(Card2, { className: "bg-white", children: [
48992
- /* @__PURE__ */ jsx(CardHeader2, { className: "pb-3", children: /* @__PURE__ */ jsxs(CardTitle2, { className: "text-sm font-medium text-gray-500 dark:text-gray-400 flex items-center gap-2", children: [
48993
- getStatusIcon("unhealthy"),
48994
- "Unhealthy"
48995
- ] }) }),
48996
- /* @__PURE__ */ jsxs(CardContent2, { children: [
48997
- /* @__PURE__ */ jsx("p", { className: "text-2xl font-bold text-gray-900 dark:text-gray-50", children: summary.unhealthyWorkspaces }),
48998
- /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: "Requires attention" })
49639
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: "Overall system uptime today" })
49640
+ ] })
49641
+ ] }),
49642
+ /* @__PURE__ */ jsxs(Card2, { className: "bg-white", children: [
49643
+ /* @__PURE__ */ jsx(CardHeader2, { className: "pb-3", children: /* @__PURE__ */ jsxs(CardTitle2, { className: "text-sm font-medium text-gray-500 dark:text-gray-400 flex items-center gap-2", children: [
49644
+ getStatusIcon("healthy"),
49645
+ "Healthy"
49646
+ ] }) }),
49647
+ /* @__PURE__ */ jsxs(CardContent2, { children: [
49648
+ /* @__PURE__ */ jsx("p", { className: "text-2xl font-bold text-gray-900 dark:text-gray-50", children: summary.healthyWorkspaces }),
49649
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: "Operating normally" })
49650
+ ] })
49651
+ ] }),
49652
+ /* @__PURE__ */ jsxs(Card2, { className: "bg-white", children: [
49653
+ /* @__PURE__ */ jsx(CardHeader2, { className: "pb-3", children: /* @__PURE__ */ jsxs(CardTitle2, { className: "text-sm font-medium text-gray-500 dark:text-gray-400 flex items-center gap-2", children: [
49654
+ getStatusIcon("warning"),
49655
+ "Warning"
49656
+ ] }) }),
49657
+ /* @__PURE__ */ jsxs(CardContent2, { children: [
49658
+ /* @__PURE__ */ jsx("p", { className: "text-2xl font-bold text-gray-900 dark:text-gray-50", children: summary.warningWorkspaces }),
49659
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: "Delayed updates" })
49660
+ ] })
49661
+ ] }),
49662
+ /* @__PURE__ */ jsxs(Card2, { className: "bg-white", children: [
49663
+ /* @__PURE__ */ jsx(CardHeader2, { className: "pb-3", children: /* @__PURE__ */ jsxs(CardTitle2, { className: "text-sm font-medium text-gray-500 dark:text-gray-400 flex items-center gap-2", children: [
49664
+ getStatusIcon("unhealthy"),
49665
+ "Unhealthy"
49666
+ ] }) }),
49667
+ /* @__PURE__ */ jsxs(CardContent2, { children: [
49668
+ /* @__PURE__ */ jsx("p", { className: "text-2xl font-bold text-gray-900 dark:text-gray-50", children: summary.unhealthyWorkspaces }),
49669
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: "Requires attention" })
49670
+ ] })
48999
49671
  ] })
49000
- ] })
49001
- ]
49002
- }
49003
- ),
49004
- /* @__PURE__ */ jsx(
49005
- motion.div,
49006
- {
49007
- initial: { opacity: 0, y: 20 },
49008
- animate: { opacity: 1, y: 0 },
49009
- transition: { duration: 0.3, delay: 0.2 },
49010
- children: /* @__PURE__ */ jsx(
49011
- HealthStatusGrid,
49012
- {
49013
- workspaces,
49014
- onWorkspaceClick: handleWorkspaceClick,
49015
- showFilters: true,
49016
- groupBy
49017
- }
49018
- )
49019
- }
49020
- )
49021
- ] })
49672
+ ]
49673
+ }
49674
+ ),
49675
+ /* @__PURE__ */ jsx(
49676
+ motion.div,
49677
+ {
49678
+ initial: { opacity: 0, y: 20 },
49679
+ animate: { opacity: 1, y: 0 },
49680
+ transition: { duration: 0.3, delay: 0.2 },
49681
+ children: /* @__PURE__ */ jsx(
49682
+ HealthStatusGrid,
49683
+ {
49684
+ workspaces,
49685
+ onWorkspaceClick: handleWorkspaceClick,
49686
+ onWorkspaceViewDetails: handleViewDetails,
49687
+ showFilters: true,
49688
+ groupBy
49689
+ }
49690
+ )
49691
+ }
49692
+ )
49693
+ ] })
49694
+ ] }),
49695
+ /* @__PURE__ */ jsx(
49696
+ WorkspaceUptimeDetailModal_default,
49697
+ {
49698
+ workspace: selectedWorkspace,
49699
+ isOpen: Boolean(selectedWorkspace),
49700
+ onClose: handleCloseDetails
49701
+ }
49702
+ )
49022
49703
  ] });
49023
49704
  };
49024
49705
  var WorkspaceHealthView_default = withAuth(WorkspaceHealthView, {
@@ -52587,4 +53268,4 @@ function shuffleArray(array) {
52587
53268
  return shuffled;
52588
53269
  }
52589
53270
 
52590
- export { ACTION_NAMES, AIAgentView_default as AIAgentView, AcceptInvite, AcceptInviteView_default as AcceptInviteView, AdvancedFilterDialog, AdvancedFilterPanel, AudioService, AuthCallback, AuthCallbackView_default as AuthCallbackView, AuthProvider, AuthService, AuthenticatedBottleneckClipsView, AuthenticatedFactoryView, AuthenticatedHelpView, AuthenticatedHomeView, AuthenticatedShiftsView, AuthenticatedTargetsView, AuthenticatedTicketsView, AuthenticatedWorkspaceHealthView, AxelNotificationPopup, AxelOrb, BackButton, BackButtonMinimal, BarChart, BaseHistoryCalendar, BottleneckClipsModal, BottleneckClipsView_default as BottleneckClipsView, BottlenecksContent, BreakNotificationPopup, CachePrefetchStatus, Card2 as Card, CardContent2 as CardContent, CardDescription2 as CardDescription, CardFooter2 as CardFooter, CardHeader2 as CardHeader, CardTitle2 as CardTitle, ClipFilterProvider, CompactWorkspaceHealthCard, CongratulationsOverlay, CroppedHlsVideoPlayer, CroppedVideoPlayer, CycleTimeChart, CycleTimeOverTimeChart, DEFAULT_ANALYTICS_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_CONFIG, DEFAULT_DATABASE_CONFIG, DEFAULT_DATE_TIME_CONFIG, DEFAULT_ENDPOINTS_CONFIG, DEFAULT_ENTITY_CONFIG, DEFAULT_MAP_VIEW_CONFIG, DEFAULT_SHIFT_CONFIG, DEFAULT_THEME_CONFIG, DEFAULT_VIDEO_CONFIG, DEFAULT_WORKSPACE_CONFIG, DEFAULT_WORKSPACE_POSITIONS, DashboardHeader, DashboardLayout, DashboardOverridesProvider, DashboardProvider, DateDisplay_default as DateDisplay, DateTimeDisplay, DebugAuth, DebugAuthView_default as DebugAuthView, DetailedHealthStatus, DiagnosisVideoModal, EmptyStateMessage, EncouragementOverlay, FactoryView_default as FactoryView, FileManagerFilters, FilterDialogTrigger, FirstTimeLoginDebug, FirstTimeLoginHandler, GaugeChart, GridComponentsPlaceholder, HamburgerButton, Header, HealthStatusGrid, HealthStatusIndicator, HelpView_default as HelpView, HlsVideoPlayer, HomeView_default as HomeView, HourlyOutputChart2 as HourlyOutputChart, ISTTimer_default as ISTTimer, InlineEditableText, InteractiveOnboardingTour, InvitationService, KPICard, KPIDetailView_default as KPIDetailView, KPIGrid, KPIHeader, KPISection, KPIsOverviewView_default as KPIsOverviewView, LINE_1_UUID, LINE_2_UUID, LargeOutputProgressChart, LeaderboardDetailView_default as LeaderboardDetailView, Legend6 as Legend, LineChart, LineHistoryCalendar, LineMonthlyHistory, LineMonthlyPdfGenerator, LinePdfExportButton, LinePdfGenerator, LineWhatsAppShareButton, LinesService, LiveTimer, LoadingInline, LoadingOverlay_default as LoadingOverlay, LoadingPage_default as LoadingPage, LoadingSkeleton, LoadingState, LoginPage, LoginView_default as LoginView, Logo, MainLayout, MapGridView, MetricCard_default as MetricCard, MinimalOnboardingPopup, NewClipsNotification, NoWorkspaceData, OnboardingDemo, OnboardingTour, OptifyeAgentClient, OptifyeLogoLoader_default as OptifyeLogoLoader, OutputProgressChart, PageHeader, PieChart4 as PieChart, PlayPauseIndicator, PrefetchConfigurationError, PrefetchError, PrefetchEvents, PrefetchStatus, PrefetchTimeoutError, ProfileView_default as ProfileView, RegistryProvider, S3ClipsSupabaseService as S3ClipsService, S3Service, SKUManagementView, SOPComplianceChart, SSEChatClient, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, ShiftDisplay_default as ShiftDisplay, ShiftsView_default as ShiftsView, SideNavBar, SignupWithInvitation, SilentErrorBoundary, SimpleOnboardingPopup, SingleVideoStream_default as SingleVideoStream, Skeleton, SubscriptionManager, SubscriptionManagerProvider, SupabaseProvider, SupervisorDropdown_default as SupervisorDropdown, SupervisorManagementView_default as SupervisorManagementView, SupervisorService, TargetWorkspaceGrid, TargetsView_default as TargetsView, TeamManagementView_default as TeamManagementView, ThreadSidebar, TicketHistory_default as TicketHistory, TicketHistoryService, TicketsView_default as TicketsView, TimeDisplay_default as TimeDisplay, TimePickerDropdown, Timer_default as Timer, TimezoneProvider, TimezoneService, UserManagementService, UserService, VideoCard, VideoGridView, VideoPlayer, VideoPreloader, WORKSPACE_POSITIONS, WhatsAppShareButton, WorkspaceCard, WorkspaceDetailView_default as WorkspaceDetailView, WorkspaceDisplayNameExample, WorkspaceGrid, WorkspaceGridItem, WorkspaceHealthCard, WorkspaceHealthView_default as WorkspaceHealthView, WorkspaceHistoryCalendar, WorkspaceMetricCards, WorkspaceMetricCardsImpl, WorkspaceMonthlyDataFetcher, WorkspaceMonthlyHistory, WorkspaceMonthlyPdfGenerator, WorkspacePdfExportButton, WorkspacePdfGenerator, WorkspaceWhatsAppShareButton, actionService, apiUtils, authCoreService, authOTPService, authRateLimitService, checkRateLimit2 as checkRateLimit, clearAllRateLimits2 as clearAllRateLimits, clearRateLimit2 as clearRateLimit, clearS3VideoCache, clearS3VideoFromCache, clearWorkspaceDisplayNamesCache, cn, createInvitationService, createLinesService, createStreamProxyHandler, createSupabaseClient, createSupervisorService, createThrottledReload, createUserManagementService, createUserService, dashboardService, deleteThread, forceRefreshWorkspaceDisplayNames, formatDateInZone, formatDateTimeInZone, formatISTDate, formatIdleTime, formatRelativeTime, formatTimeInZone, fromUrlFriendlyName, getAllLineDisplayNames, getAllThreadMessages, getAllWorkspaceDisplayNamesAsync, getAnonClient, getBrowserName, getCameraNumber, getCompanyMetricsTableName, getConfigurableShortWorkspaceDisplayName, getConfigurableWorkspaceDisplayName, getConfiguredLineIds, getCoreSessionRecordingProperties, getCoreSessionReplayUrl, getCurrentShift, getCurrentTimeInZone, getDashboardHeaderTimeInZone, getDaysDifferenceInZone, getDefaultCameraStreamUrl, getDefaultLineId, getDefaultTabForWorkspace, getLineDisplayName, getManufacturingInsights, getMetricsTablePrefix, getNextUpdateInterval, getOperationalDate, getS3SignedUrl, getS3VideoSrc, getShortWorkspaceDisplayName, getShortWorkspaceDisplayNameAsync, getStoredWorkspaceMappings, getSubscriptionManager, getThreadMessages, getUserThreads, getUserThreadsPaginated, getWorkspaceDisplayName, getWorkspaceDisplayNameAsync, getWorkspaceDisplayNamesMap, getWorkspaceFromUrl, getWorkspaceNavigationParams, identifyCoreUser, initializeCoreMixpanel, isLegacyConfiguration, isPrefetchError, isSafari, isTransitionPeriod, isUrlPermanentlyFailed, isValidFactoryViewConfiguration, isValidLineInfoPayload, isValidPrefetchParams, isValidPrefetchStatus, isValidWorkspaceDetailedMetricsPayload, isValidWorkspaceMetricsPayload, isWorkspaceDisplayNamesLoaded, isWorkspaceDisplayNamesLoading, linesService, mergeWithDefaultConfig, migrateLegacyConfiguration, optifyeAgentClient, parseS3Uri, preInitializeWorkspaceDisplayNames, preloadS3Video, preloadS3VideoUrl, preloadS3VideosUrl, preloadVideoUrl, preloadVideosUrl, qualityService, realtimeService, refreshWorkspaceDisplayNames, resetCoreMixpanel, resetFailedUrl, resetSubscriptionManager, s3VideoPreloader, shuffleArray, simulateApiDelay, skuService, startCoreSessionRecording, stopCoreSessionRecording, storeWorkspaceMapping, streamProxyConfig, throttledReloadDashboard, toUrlFriendlyName, trackCoreEvent, trackCorePageView, updateThreadTitle, useAccessControl, useActiveBreaks, useActiveLineId, useAllWorkspaceMetrics, useAnalyticsConfig, useAppTimezone, useAudioService, useAuth, useAuthConfig, useAxelNotifications, useCanSaveTargets, useClipFilter, useClipTypes, useClipTypesWithCounts, useComponentOverride, useCustomConfig, useDashboardConfig, useDashboardMetrics, useDatabaseConfig, useDateFormatter, useDateTimeConfig, useDynamicShiftConfig, useEndpointsConfig, useEntityConfig, useFactoryOverviewMetrics, useFeatureFlags, useFormatNumber, useHasLineAccess, useHistoricWorkspaceMetrics, useHlsStream, useHlsStreamWithCropping, useHookOverride, useHourEndTimer, useHourlyTargetAchievements, useHourlyTargetMisses, useLeaderboardMetrics, useLineDetailedMetrics, useLineKPIs, useLineMetrics, useLineShiftConfig, useLineSupervisor, useLineWorkspaceMetrics, useMessages, useMetrics, useNavigation, useOverrides, usePageOverride, usePrefetchClipCounts, useRealtimeLineMetrics, useRegistry, useSKUs, useSessionKeepAlive, useShiftConfig, useShifts, useSubscriptionManager, useSubscriptionManagerSafe, useSupabase, useSupabaseClient, useTargets, useTeamManagementPermissions, useTheme, useThemeConfig, useThreads, useTicketHistory, useTimezoneContext, useUserLineAccess, useVideoConfig, useWorkspaceConfig, useWorkspaceDetailedMetrics, useWorkspaceDisplayName, useWorkspaceDisplayNames, useWorkspaceDisplayNamesMap, useWorkspaceHealthById, useWorkspaceHealthStatus, useWorkspaceMetrics, useWorkspaceNavigation, useWorkspaceOperators, userService, videoPrefetchManager, videoPreloader, whatsappService, withAccessControl, withAuth, withRegistry, withTimezone, workspaceHealthService, workspaceService };
53271
+ export { ACTION_NAMES, AIAgentView_default as AIAgentView, AcceptInvite, AcceptInviteView_default as AcceptInviteView, AdvancedFilterDialog, AdvancedFilterPanel, AudioService, AuthCallback, AuthCallbackView_default as AuthCallbackView, AuthProvider, AuthService, AuthenticatedBottleneckClipsView, AuthenticatedFactoryView, AuthenticatedHelpView, AuthenticatedHomeView, AuthenticatedShiftsView, AuthenticatedTargetsView, AuthenticatedTicketsView, AuthenticatedWorkspaceHealthView, AxelNotificationPopup, AxelOrb, BackButton, BackButtonMinimal, BarChart, BaseHistoryCalendar, BottleneckClipsModal, BottleneckClipsView_default as BottleneckClipsView, BottlenecksContent, BreakNotificationPopup, CachePrefetchStatus, Card2 as Card, CardContent2 as CardContent, CardDescription2 as CardDescription, CardFooter2 as CardFooter, CardHeader2 as CardHeader, CardTitle2 as CardTitle, ClipFilterProvider, CompactWorkspaceHealthCard, CongratulationsOverlay, CroppedHlsVideoPlayer, CroppedVideoPlayer, CycleTimeChart, CycleTimeOverTimeChart, DEFAULT_ANALYTICS_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_CONFIG, DEFAULT_DATABASE_CONFIG, DEFAULT_DATE_TIME_CONFIG, DEFAULT_ENDPOINTS_CONFIG, DEFAULT_ENTITY_CONFIG, DEFAULT_MAP_VIEW_CONFIG, DEFAULT_SHIFT_CONFIG, DEFAULT_THEME_CONFIG, DEFAULT_VIDEO_CONFIG, DEFAULT_WORKSPACE_CONFIG, DEFAULT_WORKSPACE_POSITIONS, DashboardHeader, DashboardLayout, DashboardOverridesProvider, DashboardProvider, DateDisplay_default as DateDisplay, DateTimeDisplay, DebugAuth, DebugAuthView_default as DebugAuthView, DetailedHealthStatus, DiagnosisVideoModal, EmptyStateMessage, EncouragementOverlay, FactoryView_default as FactoryView, FileManagerFilters, FilterDialogTrigger, FirstTimeLoginDebug, FirstTimeLoginHandler, GaugeChart, GridComponentsPlaceholder, HamburgerButton, Header, HealthStatusGrid, HealthStatusIndicator, HelpView_default as HelpView, HlsVideoPlayer, HomeView_default as HomeView, HourlyOutputChart2 as HourlyOutputChart, ISTTimer_default as ISTTimer, InlineEditableText, InteractiveOnboardingTour, InvitationService, KPICard, KPIDetailView_default as KPIDetailView, KPIGrid, KPIHeader, KPISection, KPIsOverviewView_default as KPIsOverviewView, LINE_1_UUID, LINE_2_UUID, LargeOutputProgressChart, LeaderboardDetailView_default as LeaderboardDetailView, Legend6 as Legend, LineChart, LineHistoryCalendar, LineMonthlyHistory, LineMonthlyPdfGenerator, LinePdfExportButton, LinePdfGenerator, LineWhatsAppShareButton, LinesService, LiveTimer, LoadingInline, LoadingOverlay_default as LoadingOverlay, LoadingPage_default as LoadingPage, LoadingSkeleton, LoadingState, LoginPage, LoginView_default as LoginView, Logo, MainLayout, MapGridView, MetricCard_default as MetricCard, MinimalOnboardingPopup, NewClipsNotification, NoWorkspaceData, OnboardingDemo, OnboardingTour, OptifyeAgentClient, OptifyeLogoLoader_default as OptifyeLogoLoader, OutputProgressChart, PageHeader, PieChart4 as PieChart, PlayPauseIndicator, PrefetchConfigurationError, PrefetchError, PrefetchEvents, PrefetchStatus, PrefetchTimeoutError, ProfileView_default as ProfileView, RegistryProvider, S3ClipsSupabaseService as S3ClipsService, S3Service, SKUManagementView, SOPComplianceChart, SSEChatClient, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, ShiftDisplay_default as ShiftDisplay, ShiftsView_default as ShiftsView, SideNavBar, SignupWithInvitation, SilentErrorBoundary, SimpleOnboardingPopup, SingleVideoStream_default as SingleVideoStream, Skeleton, SubscriptionManager, SubscriptionManagerProvider, SupabaseProvider, SupervisorDropdown_default as SupervisorDropdown, SupervisorManagementView_default as SupervisorManagementView, SupervisorService, TargetWorkspaceGrid, TargetsView_default as TargetsView, TeamManagementView_default as TeamManagementView, ThreadSidebar, TicketHistory_default as TicketHistory, TicketHistoryService, TicketsView_default as TicketsView, TimeDisplay_default as TimeDisplay, TimePickerDropdown, Timer_default as Timer, TimezoneProvider, TimezoneService, UserManagementService, UserService, VideoCard, VideoGridView, VideoPlayer, VideoPreloader, WORKSPACE_POSITIONS, WhatsAppShareButton, WorkspaceCard, WorkspaceDetailView_default as WorkspaceDetailView, WorkspaceDisplayNameExample, WorkspaceGrid, WorkspaceGridItem, WorkspaceHealthCard, WorkspaceHealthView_default as WorkspaceHealthView, WorkspaceHistoryCalendar, WorkspaceMetricCards, WorkspaceMetricCardsImpl, WorkspaceMonthlyDataFetcher, WorkspaceMonthlyHistory, WorkspaceMonthlyPdfGenerator, WorkspacePdfExportButton, WorkspacePdfGenerator, WorkspaceWhatsAppShareButton, actionService, apiUtils, authCoreService, authOTPService, authRateLimitService, checkRateLimit2 as checkRateLimit, clearAllRateLimits2 as clearAllRateLimits, clearRateLimit2 as clearRateLimit, clearS3VideoCache, clearS3VideoFromCache, clearWorkspaceDisplayNamesCache, cn, createInvitationService, createLinesService, createStreamProxyHandler, createSupabaseClient, createSupervisorService, createThrottledReload, createUserManagementService, createUserService, dashboardService, deleteThread, forceRefreshWorkspaceDisplayNames, formatDateInZone, formatDateTimeInZone, formatISTDate, formatIdleTime, formatRelativeTime, formatTimeInZone, fromUrlFriendlyName, getAllLineDisplayNames, getAllThreadMessages, getAllWorkspaceDisplayNamesAsync, getAnonClient, getBrowserName, getCameraNumber, getCompanyMetricsTableName, getConfigurableShortWorkspaceDisplayName, getConfigurableWorkspaceDisplayName, getConfiguredLineIds, getCoreSessionRecordingProperties, getCoreSessionReplayUrl, getCurrentShift, getCurrentTimeInZone, getDashboardHeaderTimeInZone, getDaysDifferenceInZone, getDefaultCameraStreamUrl, getDefaultLineId, getDefaultTabForWorkspace, getLineDisplayName, getManufacturingInsights, getMetricsTablePrefix, getNextUpdateInterval, getOperationalDate, getS3SignedUrl, getS3VideoSrc, getShortWorkspaceDisplayName, getShortWorkspaceDisplayNameAsync, getStoredWorkspaceMappings, getSubscriptionManager, getThreadMessages, getUserThreads, getUserThreadsPaginated, getWorkspaceDisplayName, getWorkspaceDisplayNameAsync, getWorkspaceDisplayNamesMap, getWorkspaceFromUrl, getWorkspaceNavigationParams, identifyCoreUser, initializeCoreMixpanel, isLegacyConfiguration, isPrefetchError, isSafari, isTransitionPeriod, isUrlPermanentlyFailed, isValidFactoryViewConfiguration, isValidLineInfoPayload, isValidPrefetchParams, isValidPrefetchStatus, isValidWorkspaceDetailedMetricsPayload, isValidWorkspaceMetricsPayload, isWorkspaceDisplayNamesLoaded, isWorkspaceDisplayNamesLoading, linesService, mergeWithDefaultConfig, migrateLegacyConfiguration, optifyeAgentClient, parseS3Uri, preInitializeWorkspaceDisplayNames, preloadS3Video, preloadS3VideoUrl, preloadS3VideosUrl, preloadVideoUrl, preloadVideosUrl, qualityService, realtimeService, refreshWorkspaceDisplayNames, resetCoreMixpanel, resetFailedUrl, resetSubscriptionManager, s3VideoPreloader, shuffleArray, simulateApiDelay, skuService, startCoreSessionRecording, stopCoreSessionRecording, storeWorkspaceMapping, streamProxyConfig, throttledReloadDashboard, toUrlFriendlyName, trackCoreEvent, trackCorePageView, updateThreadTitle, useAccessControl, useActiveBreaks, useActiveLineId, useAllWorkspaceMetrics, useAnalyticsConfig, useAppTimezone, useAudioService, useAuth, useAuthConfig, useAxelNotifications, useCanSaveTargets, useClipFilter, useClipTypes, useClipTypesWithCounts, useComponentOverride, useCustomConfig, useDashboardConfig, useDashboardMetrics, useDatabaseConfig, useDateFormatter, useDateTimeConfig, useDynamicShiftConfig, useEndpointsConfig, useEntityConfig, useFactoryOverviewMetrics, useFeatureFlags, useFormatNumber, useHasLineAccess, useHistoricWorkspaceMetrics, useHlsStream, useHlsStreamWithCropping, useHookOverride, useHourEndTimer, useHourlyTargetAchievements, useHourlyTargetMisses, useLeaderboardMetrics, useLineDetailedMetrics, useLineKPIs, useLineMetrics, useLineShiftConfig, useLineSupervisor, useLineWorkspaceMetrics, useMessages, useMetrics, useNavigation, useOverrides, usePageOverride, usePrefetchClipCounts, useRealtimeLineMetrics, useRegistry, useSKUs, useSessionKeepAlive, useShiftConfig, useShifts, useSubscriptionManager, useSubscriptionManagerSafe, useSupabase, useSupabaseClient, useTargets, useTeamManagementPermissions, useTheme, useThemeConfig, useThreads, useTicketHistory, useTimezoneContext, useUserLineAccess, useVideoConfig, useWorkspaceConfig, useWorkspaceDetailedMetrics, useWorkspaceDisplayName, useWorkspaceDisplayNames, useWorkspaceDisplayNamesMap, useWorkspaceHealthById, useWorkspaceHealthStatus, useWorkspaceMetrics, useWorkspaceNavigation, useWorkspaceOperators, useWorkspaceUptimeTimeline, userService, videoPrefetchManager, videoPreloader, whatsappService, withAccessControl, withAuth, withRegistry, withTimezone, workspaceHealthService, workspaceService };