@optifye/dashboard-core 6.9.11 → 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.css +120 -12
- package/dist/index.d.mts +52 -1
- package/dist/index.d.ts +52 -1
- package/dist/index.js +1857 -731
- package/dist/index.mjs +1859 -734
- package/package.json +1 -1
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 (
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
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
|
|
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
|
|
2263
|
+
const enabledByLineAndId = /* @__PURE__ */ new Set();
|
|
2264
|
+
const enabledByLineAndName = /* @__PURE__ */ new Set();
|
|
2180
2265
|
enabledWorkspaces.forEach((w) => {
|
|
2181
|
-
|
|
2182
|
-
|
|
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
|
|
2186
|
-
|
|
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
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
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",
|
|
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
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
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
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
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
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2625
|
+
currentDownRun = 0;
|
|
2495
2626
|
}
|
|
2496
|
-
const
|
|
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:
|
|
2499
|
-
actualMinutes,
|
|
2630
|
+
expectedMinutes: completedMinutes,
|
|
2631
|
+
actualMinutes: uptimeMinutes,
|
|
2500
2632
|
percentage,
|
|
2501
2633
|
lastCalculated: (/* @__PURE__ */ new Date()).toISOString()
|
|
2502
2634
|
});
|
|
@@ -2896,8 +3028,8 @@ var AuthService = class {
|
|
|
2896
3028
|
"Authorization": `Bearer ${accessToken}`,
|
|
2897
3029
|
"Content-Type": "application/json"
|
|
2898
3030
|
},
|
|
2899
|
-
timeout:
|
|
2900
|
-
//
|
|
3031
|
+
timeout: 1e4,
|
|
3032
|
+
// 10 seconds
|
|
2901
3033
|
retries: 1,
|
|
2902
3034
|
silentErrors: false
|
|
2903
3035
|
// We want to know about auth errors
|
|
@@ -2934,8 +3066,8 @@ var AuthService = class {
|
|
|
2934
3066
|
"Authorization": `Bearer ${accessToken}`,
|
|
2935
3067
|
"Content-Type": "application/json"
|
|
2936
3068
|
},
|
|
2937
|
-
timeout:
|
|
2938
|
-
//
|
|
3069
|
+
timeout: 1e4,
|
|
3070
|
+
// 10 seconds
|
|
2939
3071
|
retries: 2,
|
|
2940
3072
|
// More retries for validation
|
|
2941
3073
|
silentErrors: true,
|
|
@@ -2967,8 +3099,8 @@ var AuthService = class {
|
|
|
2967
3099
|
"Authorization": `Bearer ${accessToken}`,
|
|
2968
3100
|
"Content-Type": "application/json"
|
|
2969
3101
|
},
|
|
2970
|
-
timeout:
|
|
2971
|
-
//
|
|
3102
|
+
timeout: 1e4,
|
|
3103
|
+
// 10 seconds
|
|
2972
3104
|
retries: 1,
|
|
2973
3105
|
silentErrors: false
|
|
2974
3106
|
}
|
|
@@ -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
|
|
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:
|
|
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
|
|
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 `${
|
|
24102
|
+
return `${formatTime5(startHour, startMinute)}-${formatTime5(endHour, endMinute)}`;
|
|
23912
24103
|
}, [shiftStartTime.decimalHour, SHIFT_DURATION, shiftEndTime]);
|
|
23913
|
-
const
|
|
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
|
|
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 `${
|
|
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:
|
|
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,
|
|
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
|
|
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:
|
|
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:
|
|
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 ?
|
|
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
|
|
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
|
-
|
|
25990
|
+
formatTime5(currentBreak.elapsedMinutes),
|
|
25800
25991
|
" elapsed of ",
|
|
25801
|
-
|
|
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(
|
|
@@ -26102,64 +26293,208 @@ var getSeverityColor = (severity) => {
|
|
|
26102
26293
|
return "bg-gray-500";
|
|
26103
26294
|
}
|
|
26104
26295
|
};
|
|
26105
|
-
var
|
|
26106
|
-
|
|
26296
|
+
var formatTime2 = (seconds) => {
|
|
26297
|
+
if (!seconds || isNaN(seconds)) return "0:00";
|
|
26298
|
+
const h = Math.floor(seconds / 3600);
|
|
26299
|
+
const m = Math.floor(seconds % 3600 / 60);
|
|
26300
|
+
const s = Math.floor(seconds % 60);
|
|
26301
|
+
if (h > 0) {
|
|
26302
|
+
return `${h}:${m.toString().padStart(2, "0")}:${s.toString().padStart(2, "0")}`;
|
|
26303
|
+
}
|
|
26304
|
+
return `${m}:${s.toString().padStart(2, "0")}`;
|
|
26305
|
+
};
|
|
26306
|
+
var VideoControls = ({
|
|
26107
26307
|
isPlaying,
|
|
26108
|
-
|
|
26308
|
+
currentTime,
|
|
26309
|
+
duration,
|
|
26310
|
+
buffered,
|
|
26311
|
+
showControls,
|
|
26312
|
+
controlsPinned = false,
|
|
26313
|
+
onTogglePinControls,
|
|
26314
|
+
playbackRate = 1,
|
|
26315
|
+
onPlayPause,
|
|
26316
|
+
onSeek,
|
|
26317
|
+
onSeekStart,
|
|
26318
|
+
onSeekEnd,
|
|
26319
|
+
onToggleFullscreen,
|
|
26320
|
+
onPlaybackRateChange,
|
|
26321
|
+
className = ""
|
|
26109
26322
|
}) => {
|
|
26110
|
-
const [
|
|
26111
|
-
const [
|
|
26323
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
26324
|
+
const [dragTime, setDragTime] = useState(0);
|
|
26325
|
+
const [isHoveringProgressBar, setIsHoveringProgressBar] = useState(false);
|
|
26326
|
+
const [showSpeedMenu, setShowSpeedMenu] = useState(false);
|
|
26327
|
+
const speedMenuRef = useRef(null);
|
|
26328
|
+
const progressColor = "#4b5563";
|
|
26329
|
+
const controlsVisible = showControls || controlsPinned;
|
|
26330
|
+
const getPercentage = (current, total) => {
|
|
26331
|
+
if (!total || total === 0) return 0;
|
|
26332
|
+
return Math.min(Math.max(current / total * 100, 0), 100);
|
|
26333
|
+
};
|
|
26334
|
+
const handleSeekChange = (e) => {
|
|
26335
|
+
const newTime = parseFloat(e.target.value);
|
|
26336
|
+
setDragTime(newTime);
|
|
26337
|
+
onSeek(newTime);
|
|
26338
|
+
};
|
|
26339
|
+
const handleSeekStart = () => {
|
|
26340
|
+
setIsDragging(true);
|
|
26341
|
+
setDragTime(currentTime);
|
|
26342
|
+
onSeekStart?.();
|
|
26343
|
+
};
|
|
26344
|
+
const handleSeekEnd = () => {
|
|
26345
|
+
setIsDragging(false);
|
|
26346
|
+
onSeekEnd?.();
|
|
26347
|
+
};
|
|
26112
26348
|
useEffect(() => {
|
|
26113
|
-
|
|
26114
|
-
|
|
26115
|
-
|
|
26116
|
-
|
|
26117
|
-
|
|
26118
|
-
|
|
26119
|
-
|
|
26120
|
-
|
|
26121
|
-
|
|
26122
|
-
|
|
26123
|
-
|
|
26124
|
-
|
|
26125
|
-
|
|
26126
|
-
|
|
26127
|
-
}
|
|
26128
|
-
}, [show, duration]);
|
|
26129
|
-
if (!isVisible) return null;
|
|
26130
|
-
return /* @__PURE__ */ jsx(
|
|
26349
|
+
const handleClickOutside = (event) => {
|
|
26350
|
+
if (speedMenuRef.current && !speedMenuRef.current.contains(event.target)) {
|
|
26351
|
+
setShowSpeedMenu(false);
|
|
26352
|
+
}
|
|
26353
|
+
};
|
|
26354
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
26355
|
+
return () => {
|
|
26356
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
26357
|
+
};
|
|
26358
|
+
}, []);
|
|
26359
|
+
const displayTime = isDragging ? dragTime : currentTime;
|
|
26360
|
+
const progressPercent = getPercentage(displayTime, duration);
|
|
26361
|
+
const bufferedPercent = getPercentage(buffered, duration);
|
|
26362
|
+
return /* @__PURE__ */ jsxs(
|
|
26131
26363
|
"div",
|
|
26132
26364
|
{
|
|
26133
|
-
className:
|
|
26134
|
-
style: {
|
|
26135
|
-
|
|
26136
|
-
|
|
26137
|
-
|
|
26138
|
-
children: /* @__PURE__ */ jsx("div", { className: "bg-black/70 rounded-full p-6", children: isPlaying ? (
|
|
26139
|
-
// Play icon (triangle)
|
|
26140
|
-
/* @__PURE__ */ jsx(
|
|
26141
|
-
"svg",
|
|
26142
|
-
{
|
|
26143
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
26144
|
-
viewBox: "0 0 24 24",
|
|
26145
|
-
fill: "white",
|
|
26146
|
-
className: "w-16 h-16",
|
|
26147
|
-
children: /* @__PURE__ */ jsx("path", { d: "M8 5v14l11-7z" })
|
|
26148
|
-
}
|
|
26149
|
-
)
|
|
26150
|
-
) : (
|
|
26151
|
-
// Pause icon (two bars)
|
|
26152
|
-
/* @__PURE__ */ jsx(
|
|
26153
|
-
"svg",
|
|
26365
|
+
className: `absolute bottom-0 left-0 right-0 px-3 pb-3 pt-12 bg-gradient-to-t from-black/80 via-black/40 to-transparent transition-opacity duration-300 ${controlsVisible ? "opacity-100" : "opacity-0 pointer-events-none"} ${className}`,
|
|
26366
|
+
style: { touchAction: "none" },
|
|
26367
|
+
children: [
|
|
26368
|
+
/* @__PURE__ */ jsxs(
|
|
26369
|
+
"div",
|
|
26154
26370
|
{
|
|
26155
|
-
|
|
26156
|
-
|
|
26157
|
-
|
|
26158
|
-
|
|
26159
|
-
|
|
26371
|
+
className: "relative h-1 mb-4 group cursor-pointer",
|
|
26372
|
+
onMouseEnter: () => setIsHoveringProgressBar(true),
|
|
26373
|
+
onMouseLeave: () => setIsHoveringProgressBar(false),
|
|
26374
|
+
children: [
|
|
26375
|
+
/* @__PURE__ */ jsx("div", { className: "absolute -top-2 -bottom-2 left-0 right-0 z-20" }),
|
|
26376
|
+
/* @__PURE__ */ jsx("div", { className: "absolute top-0 left-0 right-0 bottom-0 bg-white/20 rounded-full overflow-hidden z-0", children: /* @__PURE__ */ jsx(
|
|
26377
|
+
"div",
|
|
26378
|
+
{
|
|
26379
|
+
className: "absolute top-0 left-0 bottom-0 bg-white/40 transition-all duration-200",
|
|
26380
|
+
style: { width: `${bufferedPercent}%` }
|
|
26381
|
+
}
|
|
26382
|
+
) }),
|
|
26383
|
+
/* @__PURE__ */ jsx(
|
|
26384
|
+
"div",
|
|
26385
|
+
{
|
|
26386
|
+
className: "absolute top-0 left-0 bottom-0 bg-[#007bff] transition-all duration-75 z-10",
|
|
26387
|
+
style: { width: `${progressPercent}%`, backgroundColor: progressColor },
|
|
26388
|
+
children: /* @__PURE__ */ jsx(
|
|
26389
|
+
"div",
|
|
26390
|
+
{
|
|
26391
|
+
className: `absolute right-0 top-1/2 -translate-y-1/2 translate-x-1/2 w-3 h-3 rounded-full shadow transform transition-transform duration-200 ${isHoveringProgressBar || isDragging ? "scale-100" : "scale-0"}`,
|
|
26392
|
+
style: { backgroundColor: progressColor }
|
|
26393
|
+
}
|
|
26394
|
+
)
|
|
26395
|
+
}
|
|
26396
|
+
),
|
|
26397
|
+
/* @__PURE__ */ jsx(
|
|
26398
|
+
"input",
|
|
26399
|
+
{
|
|
26400
|
+
type: "range",
|
|
26401
|
+
min: "0",
|
|
26402
|
+
max: duration || 100,
|
|
26403
|
+
step: "0.1",
|
|
26404
|
+
value: displayTime,
|
|
26405
|
+
onChange: handleSeekChange,
|
|
26406
|
+
onMouseDown: handleSeekStart,
|
|
26407
|
+
onMouseUp: handleSeekEnd,
|
|
26408
|
+
onTouchStart: handleSeekStart,
|
|
26409
|
+
onTouchEnd: handleSeekEnd,
|
|
26410
|
+
className: "absolute inset-0 w-full h-full opacity-0 cursor-pointer z-30 margin-0 padding-0"
|
|
26411
|
+
}
|
|
26412
|
+
)
|
|
26413
|
+
]
|
|
26160
26414
|
}
|
|
26161
|
-
)
|
|
26162
|
-
|
|
26415
|
+
),
|
|
26416
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-white", children: [
|
|
26417
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
|
|
26418
|
+
/* @__PURE__ */ jsx(
|
|
26419
|
+
"button",
|
|
26420
|
+
{
|
|
26421
|
+
onClick: (e) => {
|
|
26422
|
+
e.stopPropagation();
|
|
26423
|
+
onPlayPause();
|
|
26424
|
+
},
|
|
26425
|
+
className: "hover:text-[#007bff] transition-colors focus:outline-none",
|
|
26426
|
+
"aria-label": isPlaying ? "Pause" : "Play",
|
|
26427
|
+
children: isPlaying ? /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M6 4h4v16H6V4zm8 0h4v16h-4V4z" }) }) : /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M8 5v14l11-7z" }) })
|
|
26428
|
+
}
|
|
26429
|
+
),
|
|
26430
|
+
/* @__PURE__ */ jsxs("div", { className: "text-xs font-medium font-sans", children: [
|
|
26431
|
+
/* @__PURE__ */ jsx("span", { children: formatTime2(displayTime) }),
|
|
26432
|
+
/* @__PURE__ */ jsx("span", { className: "mx-1 text-white/70", children: "/" }),
|
|
26433
|
+
/* @__PURE__ */ jsx("span", { className: "text-white/70", children: formatTime2(duration) })
|
|
26434
|
+
] })
|
|
26435
|
+
] }),
|
|
26436
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
|
|
26437
|
+
onTogglePinControls && /* @__PURE__ */ jsx(
|
|
26438
|
+
"button",
|
|
26439
|
+
{
|
|
26440
|
+
onClick: (e) => {
|
|
26441
|
+
e.stopPropagation();
|
|
26442
|
+
onTogglePinControls();
|
|
26443
|
+
},
|
|
26444
|
+
className: `transition-colors focus:outline-none ${controlsPinned ? "text-[#007bff]" : "hover:text-[#007bff]"}`,
|
|
26445
|
+
"aria-label": controlsPinned ? "Unpin controls" : "Pin controls",
|
|
26446
|
+
title: controlsPinned ? "Unpin controls" : "Pin controls",
|
|
26447
|
+
children: /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "currentColor", children: controlsPinned ? /* @__PURE__ */ jsx("path", { d: "M9 3h6l-1 7h3v2h-4.5l-.5 4.5-2 1L10 12H6v-2h3z" }) : /* @__PURE__ */ jsx("path", { d: "M9 3h6l-1 7h3v2h-4v5l-2 1-1-6H6v-2h3z" }) })
|
|
26448
|
+
}
|
|
26449
|
+
),
|
|
26450
|
+
onPlaybackRateChange && /* @__PURE__ */ jsxs("div", { className: "relative", ref: speedMenuRef, children: [
|
|
26451
|
+
/* @__PURE__ */ jsxs(
|
|
26452
|
+
"button",
|
|
26453
|
+
{
|
|
26454
|
+
onClick: (e) => {
|
|
26455
|
+
e.stopPropagation();
|
|
26456
|
+
setShowSpeedMenu(!showSpeedMenu);
|
|
26457
|
+
},
|
|
26458
|
+
className: "text-xs font-medium hover:text-[#007bff] transition-colors focus:outline-none min-w-[32px]",
|
|
26459
|
+
"aria-label": "Playback Speed",
|
|
26460
|
+
children: [
|
|
26461
|
+
playbackRate,
|
|
26462
|
+
"x"
|
|
26463
|
+
]
|
|
26464
|
+
}
|
|
26465
|
+
),
|
|
26466
|
+
showSpeedMenu && /* @__PURE__ */ jsx("div", { className: "absolute bottom-full right-0 mb-2 bg-black/90 text-white rounded shadow-lg overflow-hidden z-50 min-w-[80px]", children: [0.5, 1, 1.5, 2, 2.5, 3, 4, 5].map((rate) => /* @__PURE__ */ jsxs(
|
|
26467
|
+
"button",
|
|
26468
|
+
{
|
|
26469
|
+
onClick: (e) => {
|
|
26470
|
+
e.stopPropagation();
|
|
26471
|
+
onPlaybackRateChange(rate);
|
|
26472
|
+
setShowSpeedMenu(false);
|
|
26473
|
+
},
|
|
26474
|
+
className: `block w-full text-left px-4 py-2 text-xs hover:bg-white/20 transition-colors ${playbackRate === rate ? "text-[#007bff] font-bold" : ""}`,
|
|
26475
|
+
children: [
|
|
26476
|
+
rate,
|
|
26477
|
+
"x"
|
|
26478
|
+
]
|
|
26479
|
+
},
|
|
26480
|
+
rate
|
|
26481
|
+
)) })
|
|
26482
|
+
] }),
|
|
26483
|
+
onToggleFullscreen && /* @__PURE__ */ jsx(
|
|
26484
|
+
"button",
|
|
26485
|
+
{
|
|
26486
|
+
onClick: (e) => {
|
|
26487
|
+
e.stopPropagation();
|
|
26488
|
+
onToggleFullscreen();
|
|
26489
|
+
},
|
|
26490
|
+
className: "hover:text-[#007bff] transition-colors focus:outline-none",
|
|
26491
|
+
"aria-label": "Toggle Fullscreen",
|
|
26492
|
+
children: /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z" }) })
|
|
26493
|
+
}
|
|
26494
|
+
)
|
|
26495
|
+
] })
|
|
26496
|
+
] })
|
|
26497
|
+
]
|
|
26163
26498
|
}
|
|
26164
26499
|
);
|
|
26165
26500
|
};
|
|
@@ -26287,9 +26622,15 @@ var HlsVideoPlayer = forwardRef(({
|
|
|
26287
26622
|
const blobUrlRef = useRef(null);
|
|
26288
26623
|
const [isReady, setIsReady] = useState(false);
|
|
26289
26624
|
const [isLoading, setIsLoading] = useState(true);
|
|
26290
|
-
const [
|
|
26291
|
-
const [
|
|
26292
|
-
const
|
|
26625
|
+
const [showControls, setShowControls] = useState(true);
|
|
26626
|
+
const [controlsPinned, setControlsPinned] = useState(false);
|
|
26627
|
+
const [isPlaying, setIsPlaying] = useState(false);
|
|
26628
|
+
const [currentTime, setCurrentTime] = useState(0);
|
|
26629
|
+
const [duration, setDuration] = useState(0);
|
|
26630
|
+
const [buffered, setBuffered] = useState(0);
|
|
26631
|
+
const [playbackRate, setPlaybackRate] = useState(1);
|
|
26632
|
+
const userSeekingRef = useRef(false);
|
|
26633
|
+
const controlsTimeoutRef = useRef(null);
|
|
26293
26634
|
const eventCallbacksRef = useRef({
|
|
26294
26635
|
onReady,
|
|
26295
26636
|
onPlay,
|
|
@@ -26468,8 +26809,6 @@ var HlsVideoPlayer = forwardRef(({
|
|
|
26468
26809
|
}
|
|
26469
26810
|
});
|
|
26470
26811
|
hls.on(Events.FRAG_LOADING, () => {
|
|
26471
|
-
setIsLoading(true);
|
|
26472
|
-
eventCallbacksRef.current.onLoadingChange?.(true);
|
|
26473
26812
|
});
|
|
26474
26813
|
hls.on(Events.FRAG_LOADED, () => {
|
|
26475
26814
|
setIsLoading(false);
|
|
@@ -26523,25 +26862,52 @@ var HlsVideoPlayer = forwardRef(({
|
|
|
26523
26862
|
const handleCanPlay = () => {
|
|
26524
26863
|
if (!hlsRef.current) {
|
|
26525
26864
|
setIsReady(true);
|
|
26526
|
-
onReady?.(player);
|
|
26527
26865
|
}
|
|
26866
|
+
setIsLoading(false);
|
|
26867
|
+
eventCallbacksRef.current.onLoadingChange?.(false);
|
|
26868
|
+
onReady?.(player);
|
|
26869
|
+
};
|
|
26870
|
+
const handlePlay = () => {
|
|
26871
|
+
setIsPlaying(true);
|
|
26872
|
+
eventCallbacksRef.current.onPlay?.(player);
|
|
26873
|
+
};
|
|
26874
|
+
const handlePause = () => {
|
|
26875
|
+
if (userSeekingRef.current && videoRef.current) {
|
|
26876
|
+
videoRef.current.play().catch((err) => console.warn("Auto-resume after seek pause failed:", err));
|
|
26877
|
+
return;
|
|
26878
|
+
}
|
|
26879
|
+
setIsPlaying(false);
|
|
26880
|
+
eventCallbacksRef.current.onPause?.(player);
|
|
26528
26881
|
};
|
|
26529
|
-
const handlePlay = () => eventCallbacksRef.current.onPlay?.(player);
|
|
26530
|
-
const handlePause = () => eventCallbacksRef.current.onPause?.(player);
|
|
26531
26882
|
const handlePlaying = () => {
|
|
26532
26883
|
setIsLoading(false);
|
|
26884
|
+
setIsPlaying(true);
|
|
26533
26885
|
eventCallbacksRef.current.onLoadingChange?.(false);
|
|
26534
26886
|
eventCallbacksRef.current.onPlaying?.(player);
|
|
26535
26887
|
};
|
|
26536
26888
|
const handleTimeUpdate = () => {
|
|
26537
26889
|
const currentTime2 = video.currentTime || 0;
|
|
26890
|
+
setCurrentTime(currentTime2);
|
|
26891
|
+
if (video.buffered.length > 0) {
|
|
26892
|
+
for (let i = 0; i < video.buffered.length; i++) {
|
|
26893
|
+
if (video.buffered.start(i) <= currentTime2 && video.buffered.end(i) >= currentTime2) {
|
|
26894
|
+
setBuffered(video.buffered.end(i));
|
|
26895
|
+
break;
|
|
26896
|
+
}
|
|
26897
|
+
}
|
|
26898
|
+
}
|
|
26538
26899
|
eventCallbacksRef.current.onTimeUpdate?.(player, currentTime2);
|
|
26539
26900
|
};
|
|
26540
26901
|
const handleDurationChange = () => {
|
|
26541
26902
|
const duration2 = video.duration || 0;
|
|
26903
|
+
setDuration(duration2);
|
|
26542
26904
|
eventCallbacksRef.current.onDurationChange?.(player, duration2);
|
|
26543
26905
|
};
|
|
26544
|
-
const handleEnded = () =>
|
|
26906
|
+
const handleEnded = () => {
|
|
26907
|
+
setIsPlaying(false);
|
|
26908
|
+
userSeekingRef.current = false;
|
|
26909
|
+
eventCallbacksRef.current.onEnded?.(player);
|
|
26910
|
+
};
|
|
26545
26911
|
const handleLoadStart = () => {
|
|
26546
26912
|
setIsLoading(true);
|
|
26547
26913
|
eventCallbacksRef.current.onLoadingChange?.(true);
|
|
@@ -26557,8 +26923,19 @@ var HlsVideoPlayer = forwardRef(({
|
|
|
26557
26923
|
setIsLoading(true);
|
|
26558
26924
|
eventCallbacksRef.current.onLoadingChange?.(true);
|
|
26559
26925
|
};
|
|
26560
|
-
const handleSeeking = () =>
|
|
26561
|
-
|
|
26926
|
+
const handleSeeking = () => {
|
|
26927
|
+
userSeekingRef.current = true;
|
|
26928
|
+
eventCallbacksRef.current.onSeeking?.(player);
|
|
26929
|
+
};
|
|
26930
|
+
const handleSeeked = () => {
|
|
26931
|
+
setIsLoading(false);
|
|
26932
|
+
eventCallbacksRef.current.onLoadingChange?.(false);
|
|
26933
|
+
if (videoRef.current) {
|
|
26934
|
+
videoRef.current.play().catch((err) => console.warn("Resume playback after seek failed:", err));
|
|
26935
|
+
}
|
|
26936
|
+
userSeekingRef.current = false;
|
|
26937
|
+
eventCallbacksRef.current.onSeeked?.(player);
|
|
26938
|
+
};
|
|
26562
26939
|
const handleError = () => {
|
|
26563
26940
|
const error = video.error;
|
|
26564
26941
|
if (error) {
|
|
@@ -26571,6 +26948,10 @@ var HlsVideoPlayer = forwardRef(({
|
|
|
26571
26948
|
eventCallbacksRef.current.onError?.(player, errorInfo);
|
|
26572
26949
|
}
|
|
26573
26950
|
};
|
|
26951
|
+
const handlePlaybackRateChange2 = (e) => {
|
|
26952
|
+
const target = e.target;
|
|
26953
|
+
setPlaybackRate(target.playbackRate);
|
|
26954
|
+
};
|
|
26574
26955
|
video.addEventListener("canplay", handleCanPlay);
|
|
26575
26956
|
video.addEventListener("play", handlePlay);
|
|
26576
26957
|
video.addEventListener("pause", handlePause);
|
|
@@ -26585,6 +26966,7 @@ var HlsVideoPlayer = forwardRef(({
|
|
|
26585
26966
|
video.addEventListener("seeking", handleSeeking);
|
|
26586
26967
|
video.addEventListener("seeked", handleSeeked);
|
|
26587
26968
|
video.addEventListener("error", handleError);
|
|
26969
|
+
video.addEventListener("ratechange", handlePlaybackRateChange2);
|
|
26588
26970
|
return () => {
|
|
26589
26971
|
video.removeEventListener("canplay", handleCanPlay);
|
|
26590
26972
|
video.removeEventListener("play", handlePlay);
|
|
@@ -26600,6 +26982,7 @@ var HlsVideoPlayer = forwardRef(({
|
|
|
26600
26982
|
video.removeEventListener("seeking", handleSeeking);
|
|
26601
26983
|
video.removeEventListener("seeked", handleSeeked);
|
|
26602
26984
|
video.removeEventListener("error", handleError);
|
|
26985
|
+
video.removeEventListener("ratechange", handlePlaybackRateChange2);
|
|
26603
26986
|
};
|
|
26604
26987
|
}, [
|
|
26605
26988
|
src,
|
|
@@ -26628,20 +27011,46 @@ var HlsVideoPlayer = forwardRef(({
|
|
|
26628
27011
|
}
|
|
26629
27012
|
}
|
|
26630
27013
|
}, [autoplay]);
|
|
27014
|
+
const resetControlsTimeout = useCallback(() => {
|
|
27015
|
+
if (controlsPinned) {
|
|
27016
|
+
setShowControls(true);
|
|
27017
|
+
return;
|
|
27018
|
+
}
|
|
27019
|
+
setShowControls(true);
|
|
27020
|
+
if (controlsTimeoutRef.current) {
|
|
27021
|
+
clearTimeout(controlsTimeoutRef.current);
|
|
27022
|
+
}
|
|
27023
|
+
if (isPlaying) {
|
|
27024
|
+
controlsTimeoutRef.current = setTimeout(() => {
|
|
27025
|
+
setShowControls(false);
|
|
27026
|
+
}, 3e3);
|
|
27027
|
+
}
|
|
27028
|
+
}, [isPlaying, controlsPinned]);
|
|
27029
|
+
const handleMouseMove = useCallback(() => {
|
|
27030
|
+
resetControlsTimeout();
|
|
27031
|
+
}, [resetControlsTimeout]);
|
|
27032
|
+
useEffect(() => {
|
|
27033
|
+
resetControlsTimeout();
|
|
27034
|
+
return () => {
|
|
27035
|
+
if (controlsTimeoutRef.current) {
|
|
27036
|
+
clearTimeout(controlsTimeoutRef.current);
|
|
27037
|
+
}
|
|
27038
|
+
};
|
|
27039
|
+
}, [isPlaying, resetControlsTimeout]);
|
|
26631
27040
|
const play = useCallback(() => {
|
|
26632
27041
|
return videoRef.current?.play();
|
|
26633
27042
|
}, []);
|
|
26634
27043
|
const pause = useCallback(() => {
|
|
26635
27044
|
videoRef.current?.pause();
|
|
26636
27045
|
}, []);
|
|
26637
|
-
const
|
|
27046
|
+
const currentTimeProp = useCallback((time2) => {
|
|
26638
27047
|
if (time2 !== void 0 && videoRef.current) {
|
|
26639
27048
|
videoRef.current.currentTime = time2;
|
|
26640
27049
|
return time2;
|
|
26641
27050
|
}
|
|
26642
27051
|
return videoRef.current?.currentTime || 0;
|
|
26643
27052
|
}, []);
|
|
26644
|
-
const
|
|
27053
|
+
const durationProp = useCallback(() => {
|
|
26645
27054
|
return videoRef.current?.duration || 0;
|
|
26646
27055
|
}, []);
|
|
26647
27056
|
const paused = useCallback(() => {
|
|
@@ -26654,95 +27063,144 @@ var HlsVideoPlayer = forwardRef(({
|
|
|
26654
27063
|
}
|
|
26655
27064
|
return videoRef.current?.muted ?? false;
|
|
26656
27065
|
}, []);
|
|
26657
|
-
const
|
|
27066
|
+
const volumeProp = useCallback((level) => {
|
|
26658
27067
|
if (level !== void 0 && videoRef.current) {
|
|
26659
27068
|
videoRef.current.volume = level;
|
|
26660
27069
|
return level;
|
|
26661
27070
|
}
|
|
26662
27071
|
return videoRef.current?.volume ?? 1;
|
|
26663
27072
|
}, []);
|
|
26664
|
-
const
|
|
27073
|
+
const playbackRateProp = useCallback((rate) => {
|
|
26665
27074
|
if (rate !== void 0 && videoRef.current) {
|
|
26666
27075
|
videoRef.current.playbackRate = rate;
|
|
26667
27076
|
return rate;
|
|
26668
27077
|
}
|
|
26669
27078
|
return videoRef.current?.playbackRate ?? 1;
|
|
26670
27079
|
}, []);
|
|
27080
|
+
const handleTogglePlay = useCallback(() => {
|
|
27081
|
+
if (videoRef.current) {
|
|
27082
|
+
if (videoRef.current.paused) {
|
|
27083
|
+
videoRef.current.play();
|
|
27084
|
+
} else {
|
|
27085
|
+
videoRef.current.pause();
|
|
27086
|
+
}
|
|
27087
|
+
}
|
|
27088
|
+
}, []);
|
|
27089
|
+
const handleSeek = useCallback((time2) => {
|
|
27090
|
+
if (videoRef.current) {
|
|
27091
|
+
videoRef.current.currentTime = time2;
|
|
27092
|
+
videoRef.current.play().catch((err) => console.warn("Resume playback failed during seek:", err));
|
|
27093
|
+
}
|
|
27094
|
+
}, []);
|
|
27095
|
+
const handleSeekStart = useCallback(() => {
|
|
27096
|
+
userSeekingRef.current = true;
|
|
27097
|
+
}, []);
|
|
27098
|
+
const handleSeekEnd = useCallback(() => {
|
|
27099
|
+
if (videoRef.current) {
|
|
27100
|
+
videoRef.current.play().catch((err) => console.warn("Resume playback failed after seek:", err));
|
|
27101
|
+
}
|
|
27102
|
+
}, []);
|
|
27103
|
+
const handlePlaybackRateChange = useCallback((rate) => {
|
|
27104
|
+
if (videoRef.current) {
|
|
27105
|
+
videoRef.current.playbackRate = rate;
|
|
27106
|
+
}
|
|
27107
|
+
}, []);
|
|
27108
|
+
const handleToggleFullscreen = useCallback(() => {
|
|
27109
|
+
if (videoContainerRef.current) {
|
|
27110
|
+
if (!document.fullscreenElement) {
|
|
27111
|
+
videoContainerRef.current.requestFullscreen();
|
|
27112
|
+
} else {
|
|
27113
|
+
document.exitFullscreen();
|
|
27114
|
+
}
|
|
27115
|
+
}
|
|
27116
|
+
}, []);
|
|
26671
27117
|
useImperativeHandle(ref, () => ({
|
|
26672
27118
|
hls: hlsRef.current,
|
|
26673
27119
|
video: videoRef.current,
|
|
26674
27120
|
play,
|
|
26675
27121
|
pause,
|
|
26676
|
-
currentTime,
|
|
26677
|
-
duration,
|
|
27122
|
+
currentTime: currentTimeProp,
|
|
27123
|
+
duration: durationProp,
|
|
26678
27124
|
paused,
|
|
26679
27125
|
mute,
|
|
26680
|
-
volume,
|
|
26681
|
-
playbackRate,
|
|
27126
|
+
volume: volumeProp,
|
|
27127
|
+
playbackRate: playbackRateProp,
|
|
26682
27128
|
dispose,
|
|
26683
27129
|
isReady,
|
|
26684
27130
|
// For backward compatibility with Video.js API
|
|
26685
27131
|
player: playerLikeObject()
|
|
26686
|
-
}), [play, pause,
|
|
26687
|
-
const
|
|
26688
|
-
if (!onClick
|
|
26689
|
-
|
|
26690
|
-
|
|
26691
|
-
|
|
26692
|
-
|
|
26693
|
-
|
|
26694
|
-
|
|
26695
|
-
|
|
26696
|
-
|
|
26697
|
-
|
|
26698
|
-
|
|
26699
|
-
|
|
26700
|
-
|
|
26701
|
-
|
|
26702
|
-
|
|
26703
|
-
|
|
26704
|
-
|
|
26705
|
-
"video",
|
|
27132
|
+
}), [play, pause, currentTimeProp, durationProp, paused, mute, volumeProp, playbackRateProp, dispose, isReady, playerLikeObject]);
|
|
27133
|
+
const handleContainerClick = useCallback(() => {
|
|
27134
|
+
if (!onClick && !controls) {
|
|
27135
|
+
handleTogglePlay();
|
|
27136
|
+
}
|
|
27137
|
+
if (onClick) {
|
|
27138
|
+
onClick();
|
|
27139
|
+
}
|
|
27140
|
+
}, [onClick, controls, handleTogglePlay]);
|
|
27141
|
+
return /* @__PURE__ */ jsxs(
|
|
27142
|
+
"div",
|
|
27143
|
+
{
|
|
27144
|
+
className: `hls-video-player-wrapper ${className} group`,
|
|
27145
|
+
style: { position: "relative", width: "100%", height: "100%" },
|
|
27146
|
+
onMouseMove: handleMouseMove,
|
|
27147
|
+
onMouseLeave: () => isPlaying && !controlsPinned && setShowControls(false),
|
|
27148
|
+
children: [
|
|
27149
|
+
/* @__PURE__ */ jsxs(
|
|
27150
|
+
"div",
|
|
26706
27151
|
{
|
|
26707
|
-
|
|
26708
|
-
|
|
26709
|
-
|
|
26710
|
-
|
|
26711
|
-
|
|
26712
|
-
|
|
26713
|
-
|
|
26714
|
-
|
|
26715
|
-
|
|
27152
|
+
className: "hls-video-player-container",
|
|
27153
|
+
ref: videoContainerRef,
|
|
27154
|
+
onClick: handleContainerClick,
|
|
27155
|
+
children: [
|
|
27156
|
+
/* @__PURE__ */ jsx(
|
|
27157
|
+
"video",
|
|
27158
|
+
{
|
|
27159
|
+
ref: videoRef,
|
|
27160
|
+
className: "hls-video-element",
|
|
27161
|
+
poster,
|
|
27162
|
+
controls: false,
|
|
27163
|
+
loop,
|
|
27164
|
+
muted,
|
|
27165
|
+
playsInline,
|
|
27166
|
+
autoPlay: autoplay,
|
|
27167
|
+
preload: "metadata"
|
|
27168
|
+
}
|
|
27169
|
+
),
|
|
27170
|
+
controls && /* @__PURE__ */ jsx(
|
|
27171
|
+
VideoControls,
|
|
27172
|
+
{
|
|
27173
|
+
isPlaying,
|
|
27174
|
+
currentTime,
|
|
27175
|
+
duration,
|
|
27176
|
+
buffered,
|
|
27177
|
+
showControls: controlsPinned || showControls || !isPlaying,
|
|
27178
|
+
controlsPinned,
|
|
27179
|
+
playbackRate,
|
|
27180
|
+
onPlayPause: handleTogglePlay,
|
|
27181
|
+
onSeek: handleSeek,
|
|
27182
|
+
onSeekStart: handleSeekStart,
|
|
27183
|
+
onSeekEnd: handleSeekEnd,
|
|
27184
|
+
onPlaybackRateChange: handlePlaybackRateChange,
|
|
27185
|
+
onTogglePinControls: () => setControlsPinned((prev) => {
|
|
27186
|
+
const next = !prev;
|
|
27187
|
+
if (next) {
|
|
27188
|
+
setShowControls(true);
|
|
27189
|
+
} else {
|
|
27190
|
+
resetControlsTimeout();
|
|
27191
|
+
}
|
|
27192
|
+
return next;
|
|
27193
|
+
}),
|
|
27194
|
+
onToggleFullscreen: handleToggleFullscreen
|
|
27195
|
+
}
|
|
27196
|
+
)
|
|
27197
|
+
]
|
|
26716
27198
|
}
|
|
26717
|
-
)
|
|
26718
|
-
|
|
26719
|
-
|
|
26720
|
-
|
|
26721
|
-
|
|
26722
|
-
"div",
|
|
26723
|
-
{
|
|
26724
|
-
onClick: handleClickWithIndicator,
|
|
26725
|
-
style: {
|
|
26726
|
-
position: "absolute",
|
|
26727
|
-
top: 0,
|
|
26728
|
-
left: 0,
|
|
26729
|
-
right: 0,
|
|
26730
|
-
bottom: 0,
|
|
26731
|
-
zIndex: 1,
|
|
26732
|
-
cursor: "pointer"
|
|
26733
|
-
},
|
|
26734
|
-
"aria-label": "Click to play/pause"
|
|
26735
|
-
}
|
|
26736
|
-
),
|
|
26737
|
-
onClick && !controls && /* @__PURE__ */ jsx(
|
|
26738
|
-
PlayPauseIndicator,
|
|
26739
|
-
{
|
|
26740
|
-
show: showIndicator,
|
|
26741
|
-
isPlaying: indicatorIsPlaying
|
|
26742
|
-
},
|
|
26743
|
-
indicatorKeyRef.current
|
|
26744
|
-
)
|
|
26745
|
-
] });
|
|
27199
|
+
),
|
|
27200
|
+
isLoading && !externalLoadingControl && /* @__PURE__ */ jsx("div", { className: "hls-video-player-loading", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) })
|
|
27201
|
+
]
|
|
27202
|
+
}
|
|
27203
|
+
);
|
|
26746
27204
|
});
|
|
26747
27205
|
HlsVideoPlayer.displayName = "HlsVideoPlayer";
|
|
26748
27206
|
var VideoPlayer = HlsVideoPlayer;
|
|
@@ -26750,6 +27208,7 @@ var CroppedHlsVideoPlayer = forwardRef(({
|
|
|
26750
27208
|
crop,
|
|
26751
27209
|
debug = false,
|
|
26752
27210
|
onClick,
|
|
27211
|
+
controls = true,
|
|
26753
27212
|
...videoProps
|
|
26754
27213
|
}, ref) => {
|
|
26755
27214
|
const {
|
|
@@ -26771,9 +27230,15 @@ var CroppedHlsVideoPlayer = forwardRef(({
|
|
|
26771
27230
|
const [isVideoReady, setIsVideoReady] = useState(false);
|
|
26772
27231
|
const [canvasDimensions, setCanvasDimensions] = useState({ width: 0, height: 0 });
|
|
26773
27232
|
const [isProcessing, setIsProcessing] = useState(false);
|
|
26774
|
-
const [
|
|
26775
|
-
const [
|
|
26776
|
-
const
|
|
27233
|
+
const [showControls, setShowControls] = useState(true);
|
|
27234
|
+
const [isPlaying, setIsPlaying] = useState(false);
|
|
27235
|
+
const [currentTime, setCurrentTime] = useState(0);
|
|
27236
|
+
const [duration, setDuration] = useState(0);
|
|
27237
|
+
const [buffered, setBuffered] = useState(0);
|
|
27238
|
+
const [playbackRate, setPlaybackRate] = useState(1);
|
|
27239
|
+
const controlsTimeoutRef = useRef(null);
|
|
27240
|
+
const userSeekingRef = useRef(false);
|
|
27241
|
+
const [controlsPinned, setControlsPinned] = useState(false);
|
|
26777
27242
|
const stopCanvasRendering = useCallback(() => {
|
|
26778
27243
|
if (animationFrameRef.current) {
|
|
26779
27244
|
cancelAnimationFrame(animationFrameRef.current);
|
|
@@ -26858,7 +27323,7 @@ var CroppedHlsVideoPlayer = forwardRef(({
|
|
|
26858
27323
|
const canvas = canvasRef.current;
|
|
26859
27324
|
const video = videoElementRef.current;
|
|
26860
27325
|
const ctx = canvas.getContext("2d");
|
|
26861
|
-
if (!ctx || video.
|
|
27326
|
+
if (!ctx || video.readyState < 2) {
|
|
26862
27327
|
return;
|
|
26863
27328
|
}
|
|
26864
27329
|
const videoWidth = video.videoWidth;
|
|
@@ -26885,7 +27350,9 @@ var CroppedHlsVideoPlayer = forwardRef(({
|
|
|
26885
27350
|
canvas.height
|
|
26886
27351
|
// Destination (full canvas)
|
|
26887
27352
|
);
|
|
26888
|
-
|
|
27353
|
+
if (!video.paused && !video.ended) {
|
|
27354
|
+
animationFrameRef.current = requestAnimationFrame(renderFrameToCanvas);
|
|
27355
|
+
}
|
|
26889
27356
|
}, [crop]);
|
|
26890
27357
|
const handleVideoReady = useCallback((player) => {
|
|
26891
27358
|
console.log("[CroppedHlsVideoPlayer] Video player ready");
|
|
@@ -26893,11 +27360,15 @@ var CroppedHlsVideoPlayer = forwardRef(({
|
|
|
26893
27360
|
if (videoEl) {
|
|
26894
27361
|
videoElementRef.current = videoEl;
|
|
26895
27362
|
setIsVideoReady(true);
|
|
27363
|
+
if (videoEl.readyState >= 2) {
|
|
27364
|
+
renderFrameToCanvas();
|
|
27365
|
+
}
|
|
26896
27366
|
}
|
|
26897
27367
|
onReadyProp?.(player);
|
|
26898
|
-
}, [onReadyProp]);
|
|
27368
|
+
}, [onReadyProp, renderFrameToCanvas]);
|
|
26899
27369
|
const handleVideoPlay = useCallback((player) => {
|
|
26900
27370
|
console.log("[CroppedHlsVideoPlayer] Video playing, starting canvas rendering");
|
|
27371
|
+
setIsPlaying(true);
|
|
26901
27372
|
if (crop && canvasRef.current) {
|
|
26902
27373
|
setIsProcessing(true);
|
|
26903
27374
|
renderFrameToCanvas();
|
|
@@ -26905,43 +27376,40 @@ var CroppedHlsVideoPlayer = forwardRef(({
|
|
|
26905
27376
|
onPlayProp?.(player);
|
|
26906
27377
|
}, [crop, renderFrameToCanvas, onPlayProp]);
|
|
26907
27378
|
const handleVideoPause = useCallback((player) => {
|
|
26908
|
-
console.log("[CroppedHlsVideoPlayer] Video paused, stopping canvas rendering
|
|
27379
|
+
console.log("[CroppedHlsVideoPlayer] Video paused, stopping canvas rendering (keeping last frame)");
|
|
27380
|
+
if (userSeekingRef.current && hiddenVideoRef.current) {
|
|
27381
|
+
hiddenVideoRef.current.play()?.catch(() => {
|
|
27382
|
+
});
|
|
27383
|
+
return;
|
|
27384
|
+
}
|
|
26909
27385
|
stopCanvasRendering();
|
|
26910
27386
|
setIsProcessing(false);
|
|
26911
|
-
|
|
26912
|
-
|
|
26913
|
-
if (ctx) {
|
|
26914
|
-
ctx.clearRect(0, 0, canvasRef.current.width, canvasRef.current.height);
|
|
26915
|
-
ctx.fillStyle = "black";
|
|
26916
|
-
ctx.fillRect(0, 0, canvasRef.current.width, canvasRef.current.height);
|
|
26917
|
-
}
|
|
26918
|
-
}
|
|
27387
|
+
setIsPlaying(false);
|
|
27388
|
+
renderFrameToCanvas();
|
|
26919
27389
|
onPauseProp?.(player);
|
|
26920
|
-
}, [stopCanvasRendering, onPauseProp]);
|
|
27390
|
+
}, [stopCanvasRendering, onPauseProp, renderFrameToCanvas]);
|
|
26921
27391
|
const handleVideoEnded = useCallback((player) => {
|
|
26922
|
-
console.log("[CroppedHlsVideoPlayer] Video ended,
|
|
27392
|
+
console.log("[CroppedHlsVideoPlayer] Video ended, stopping canvas rendering (keeping last frame)");
|
|
26923
27393
|
stopCanvasRendering();
|
|
26924
27394
|
setIsProcessing(false);
|
|
26925
|
-
|
|
26926
|
-
|
|
26927
|
-
if (ctx) {
|
|
26928
|
-
ctx.clearRect(0, 0, canvasRef.current.width, canvasRef.current.height);
|
|
26929
|
-
ctx.fillStyle = "black";
|
|
26930
|
-
ctx.fillRect(0, 0, canvasRef.current.width, canvasRef.current.height);
|
|
26931
|
-
}
|
|
26932
|
-
}
|
|
27395
|
+
setIsPlaying(false);
|
|
27396
|
+
userSeekingRef.current = false;
|
|
26933
27397
|
onEndedProp?.(player);
|
|
26934
27398
|
}, [stopCanvasRendering, onEndedProp]);
|
|
26935
27399
|
const handleSeeking = useCallback((player) => {
|
|
26936
27400
|
console.log("[CroppedHlsVideoPlayer] Video seeking");
|
|
26937
|
-
|
|
27401
|
+
userSeekingRef.current = true;
|
|
27402
|
+
if (crop) {
|
|
26938
27403
|
renderFrameToCanvas();
|
|
26939
27404
|
}
|
|
26940
27405
|
onSeekingProp?.(player);
|
|
26941
27406
|
}, [crop, renderFrameToCanvas, onSeekingProp]);
|
|
26942
27407
|
const handleSeeked = useCallback((player) => {
|
|
26943
27408
|
console.log("[CroppedHlsVideoPlayer] Video seeked");
|
|
26944
|
-
|
|
27409
|
+
hiddenVideoRef.current?.play()?.catch(() => {
|
|
27410
|
+
});
|
|
27411
|
+
userSeekingRef.current = false;
|
|
27412
|
+
if (crop) {
|
|
26945
27413
|
renderFrameToCanvas();
|
|
26946
27414
|
}
|
|
26947
27415
|
onSeekedProp?.(player);
|
|
@@ -26949,8 +27417,29 @@ var CroppedHlsVideoPlayer = forwardRef(({
|
|
|
26949
27417
|
const handleLoadedMetadata = useCallback((player) => {
|
|
26950
27418
|
console.log("[CroppedHlsVideoPlayer] Video metadata loaded");
|
|
26951
27419
|
calculateCanvasDimensions();
|
|
27420
|
+
if (hiddenVideoRef.current?.video) {
|
|
27421
|
+
setDuration(hiddenVideoRef.current.video.duration || 0);
|
|
27422
|
+
}
|
|
27423
|
+
requestAnimationFrame(() => renderFrameToCanvas());
|
|
26952
27424
|
onLoadedMetadataProp?.(player);
|
|
26953
|
-
}, [calculateCanvasDimensions, onLoadedMetadataProp]);
|
|
27425
|
+
}, [calculateCanvasDimensions, onLoadedMetadataProp, renderFrameToCanvas]);
|
|
27426
|
+
const handleTimeUpdate = useCallback((player, time2) => {
|
|
27427
|
+
setCurrentTime(time2);
|
|
27428
|
+
if (hiddenVideoRef.current?.video && hiddenVideoRef.current.video.buffered.length > 0) {
|
|
27429
|
+
const video = hiddenVideoRef.current.video;
|
|
27430
|
+
for (let i = 0; i < video.buffered.length; i++) {
|
|
27431
|
+
if (video.buffered.start(i) <= time2 && video.buffered.end(i) >= time2) {
|
|
27432
|
+
setBuffered(video.buffered.end(i));
|
|
27433
|
+
break;
|
|
27434
|
+
}
|
|
27435
|
+
}
|
|
27436
|
+
}
|
|
27437
|
+
videoProps.onTimeUpdate?.(player, time2);
|
|
27438
|
+
}, [videoProps.onTimeUpdate]);
|
|
27439
|
+
const handleDurationChange = useCallback((player, dur) => {
|
|
27440
|
+
setDuration(dur);
|
|
27441
|
+
videoProps.onDurationChange?.(player, dur);
|
|
27442
|
+
}, [videoProps.onDurationChange]);
|
|
26954
27443
|
useEffect(() => {
|
|
26955
27444
|
calculateCanvasDimensions();
|
|
26956
27445
|
const handleResize = () => {
|
|
@@ -26978,33 +27467,97 @@ var CroppedHlsVideoPlayer = forwardRef(({
|
|
|
26978
27467
|
stopCanvasRendering();
|
|
26979
27468
|
};
|
|
26980
27469
|
}, [stopCanvasRendering]);
|
|
27470
|
+
const resetControlsTimeout = useCallback(() => {
|
|
27471
|
+
if (controlsPinned) {
|
|
27472
|
+
setShowControls(true);
|
|
27473
|
+
return;
|
|
27474
|
+
}
|
|
27475
|
+
setShowControls(true);
|
|
27476
|
+
if (controlsTimeoutRef.current) {
|
|
27477
|
+
clearTimeout(controlsTimeoutRef.current);
|
|
27478
|
+
}
|
|
27479
|
+
if (isPlaying) {
|
|
27480
|
+
controlsTimeoutRef.current = setTimeout(() => {
|
|
27481
|
+
setShowControls(false);
|
|
27482
|
+
}, 3e3);
|
|
27483
|
+
}
|
|
27484
|
+
}, [isPlaying, controlsPinned]);
|
|
27485
|
+
const handleMouseMove = useCallback(() => {
|
|
27486
|
+
resetControlsTimeout();
|
|
27487
|
+
}, [resetControlsTimeout]);
|
|
27488
|
+
useEffect(() => {
|
|
27489
|
+
resetControlsTimeout();
|
|
27490
|
+
return () => {
|
|
27491
|
+
if (controlsTimeoutRef.current) {
|
|
27492
|
+
clearTimeout(controlsTimeoutRef.current);
|
|
27493
|
+
}
|
|
27494
|
+
};
|
|
27495
|
+
}, [isPlaying, resetControlsTimeout]);
|
|
27496
|
+
const handleTogglePlay = useCallback(() => {
|
|
27497
|
+
if (hiddenVideoRef.current?.video) {
|
|
27498
|
+
if (hiddenVideoRef.current.video.paused) {
|
|
27499
|
+
hiddenVideoRef.current.play();
|
|
27500
|
+
} else {
|
|
27501
|
+
hiddenVideoRef.current.pause();
|
|
27502
|
+
}
|
|
27503
|
+
}
|
|
27504
|
+
}, []);
|
|
27505
|
+
const handleSeek = useCallback((time2) => {
|
|
27506
|
+
if (hiddenVideoRef.current) {
|
|
27507
|
+
hiddenVideoRef.current.currentTime(time2);
|
|
27508
|
+
hiddenVideoRef.current.play()?.catch(() => {
|
|
27509
|
+
});
|
|
27510
|
+
setTimeout(() => renderFrameToCanvas(), 50);
|
|
27511
|
+
}
|
|
27512
|
+
}, [renderFrameToCanvas]);
|
|
27513
|
+
const handleSeekStart = useCallback(() => {
|
|
27514
|
+
userSeekingRef.current = true;
|
|
27515
|
+
}, []);
|
|
27516
|
+
const handleSeekEnd = useCallback(() => {
|
|
27517
|
+
if (hiddenVideoRef.current) {
|
|
27518
|
+
hiddenVideoRef.current.play()?.catch(() => {
|
|
27519
|
+
});
|
|
27520
|
+
}
|
|
27521
|
+
}, []);
|
|
27522
|
+
const handlePlaybackRateChange = useCallback((rate) => {
|
|
27523
|
+
if (hiddenVideoRef.current) {
|
|
27524
|
+
hiddenVideoRef.current.playbackRate(rate);
|
|
27525
|
+
setPlaybackRate(rate);
|
|
27526
|
+
}
|
|
27527
|
+
}, []);
|
|
27528
|
+
const handleToggleFullscreen = useCallback(() => {
|
|
27529
|
+
if (videoContainerRef.current) {
|
|
27530
|
+
if (!document.fullscreenElement) {
|
|
27531
|
+
videoContainerRef.current.requestFullscreen();
|
|
27532
|
+
} else {
|
|
27533
|
+
document.exitFullscreen();
|
|
27534
|
+
}
|
|
27535
|
+
}
|
|
27536
|
+
}, []);
|
|
26981
27537
|
if (!crop) {
|
|
26982
|
-
return /* @__PURE__ */ jsx(HlsVideoPlayer, { ref, ...videoProps, onClick });
|
|
26983
|
-
}
|
|
26984
|
-
const
|
|
26985
|
-
if (!onClick
|
|
26986
|
-
|
|
26987
|
-
|
|
26988
|
-
|
|
26989
|
-
setShowIndicator(false);
|
|
26990
|
-
setTimeout(() => {
|
|
26991
|
-
indicatorKeyRef.current += 1;
|
|
26992
|
-
setShowIndicator(true);
|
|
26993
|
-
}, 0);
|
|
26994
|
-
onClick();
|
|
27538
|
+
return /* @__PURE__ */ jsx(HlsVideoPlayer, { ref, ...videoProps, onClick, controls });
|
|
27539
|
+
}
|
|
27540
|
+
const handleClick = () => {
|
|
27541
|
+
if (!onClick && !controls) {
|
|
27542
|
+
handleTogglePlay();
|
|
27543
|
+
}
|
|
27544
|
+
if (onClick) onClick();
|
|
26995
27545
|
};
|
|
26996
27546
|
return /* @__PURE__ */ jsxs(
|
|
26997
27547
|
"div",
|
|
26998
27548
|
{
|
|
26999
27549
|
ref: videoContainerRef,
|
|
27000
|
-
className: `relative w-full h-full flex items-center justify-center bg-black ${onClick ? "cursor-pointer" : ""}
|
|
27001
|
-
onClick:
|
|
27550
|
+
className: `relative w-full h-full flex items-center justify-center bg-black group ${inheritedClassName} ${onClick || controls ? "cursor-pointer" : ""}`,
|
|
27551
|
+
onClick: handleClick,
|
|
27552
|
+
onMouseMove: handleMouseMove,
|
|
27553
|
+
onMouseLeave: () => isPlaying && !controlsPinned && setShowControls(false),
|
|
27002
27554
|
children: [
|
|
27003
27555
|
/* @__PURE__ */ jsx("div", { className: "hidden", children: /* @__PURE__ */ jsx(
|
|
27004
27556
|
HlsVideoPlayer,
|
|
27005
27557
|
{
|
|
27006
27558
|
ref: hiddenVideoRef,
|
|
27007
27559
|
...videoProps,
|
|
27560
|
+
controls: false,
|
|
27008
27561
|
onReady: handleVideoReady,
|
|
27009
27562
|
onPlay: handleVideoPlay,
|
|
27010
27563
|
onPause: handleVideoPause,
|
|
@@ -27014,7 +27567,9 @@ var CroppedHlsVideoPlayer = forwardRef(({
|
|
|
27014
27567
|
onLoadedMetadata: handleLoadedMetadata,
|
|
27015
27568
|
onLoadedData: videoProps.onLoadedData,
|
|
27016
27569
|
onPlaying: videoProps.onPlaying,
|
|
27017
|
-
onLoadingChange: videoProps.onLoadingChange
|
|
27570
|
+
onLoadingChange: videoProps.onLoadingChange,
|
|
27571
|
+
onTimeUpdate: handleTimeUpdate,
|
|
27572
|
+
onDurationChange: handleDurationChange
|
|
27018
27573
|
}
|
|
27019
27574
|
) }),
|
|
27020
27575
|
/* @__PURE__ */ jsx(
|
|
@@ -27031,8 +27586,8 @@ var CroppedHlsVideoPlayer = forwardRef(({
|
|
|
27031
27586
|
}
|
|
27032
27587
|
}
|
|
27033
27588
|
),
|
|
27034
|
-
!isVideoReady && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
|
|
27035
|
-
debug && isVideoReady && /* @__PURE__ */ jsxs("div", { className: "absolute top-2 left-2 bg-black/80 text-white text-xs p-2 rounded font-mono", children: [
|
|
27589
|
+
!isVideoReady && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center pointer-events-none", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
|
|
27590
|
+
debug && isVideoReady && /* @__PURE__ */ jsxs("div", { className: "absolute top-2 left-2 bg-black/80 text-white text-xs p-2 rounded font-mono pointer-events-none z-20", children: [
|
|
27036
27591
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
27037
27592
|
"Crop: ",
|
|
27038
27593
|
crop.x,
|
|
@@ -27056,13 +27611,32 @@ var CroppedHlsVideoPlayer = forwardRef(({
|
|
|
27056
27611
|
isProcessing ? "Yes" : "No"
|
|
27057
27612
|
] })
|
|
27058
27613
|
] }),
|
|
27059
|
-
|
|
27060
|
-
|
|
27614
|
+
controls && isVideoReady && /* @__PURE__ */ jsx(
|
|
27615
|
+
VideoControls,
|
|
27061
27616
|
{
|
|
27062
|
-
|
|
27063
|
-
|
|
27064
|
-
|
|
27065
|
-
|
|
27617
|
+
isPlaying,
|
|
27618
|
+
currentTime,
|
|
27619
|
+
duration,
|
|
27620
|
+
buffered,
|
|
27621
|
+
showControls: controlsPinned || showControls || !isPlaying,
|
|
27622
|
+
controlsPinned,
|
|
27623
|
+
playbackRate,
|
|
27624
|
+
onPlayPause: handleTogglePlay,
|
|
27625
|
+
onSeek: handleSeek,
|
|
27626
|
+
onSeekStart: handleSeekStart,
|
|
27627
|
+
onSeekEnd: handleSeekEnd,
|
|
27628
|
+
onPlaybackRateChange: handlePlaybackRateChange,
|
|
27629
|
+
onTogglePinControls: () => setControlsPinned((prev) => {
|
|
27630
|
+
const next = !prev;
|
|
27631
|
+
if (next) {
|
|
27632
|
+
setShowControls(true);
|
|
27633
|
+
} else {
|
|
27634
|
+
resetControlsTimeout();
|
|
27635
|
+
}
|
|
27636
|
+
return next;
|
|
27637
|
+
}),
|
|
27638
|
+
onToggleFullscreen: handleToggleFullscreen
|
|
27639
|
+
}
|
|
27066
27640
|
)
|
|
27067
27641
|
]
|
|
27068
27642
|
}
|
|
@@ -27601,6 +28175,67 @@ var SilentErrorBoundary = class extends React23__default.Component {
|
|
|
27601
28175
|
] }) });
|
|
27602
28176
|
}
|
|
27603
28177
|
};
|
|
28178
|
+
var PlayPauseIndicator = ({
|
|
28179
|
+
show,
|
|
28180
|
+
isPlaying,
|
|
28181
|
+
duration = 600
|
|
28182
|
+
}) => {
|
|
28183
|
+
const [isVisible, setIsVisible] = useState(false);
|
|
28184
|
+
const [isFading, setIsFading] = useState(false);
|
|
28185
|
+
useEffect(() => {
|
|
28186
|
+
if (show) {
|
|
28187
|
+
setIsVisible(true);
|
|
28188
|
+
setIsFading(false);
|
|
28189
|
+
const fadeTimer = setTimeout(() => {
|
|
28190
|
+
setIsFading(true);
|
|
28191
|
+
}, 100);
|
|
28192
|
+
const hideTimer = setTimeout(() => {
|
|
28193
|
+
setIsVisible(false);
|
|
28194
|
+
setIsFading(false);
|
|
28195
|
+
}, duration);
|
|
28196
|
+
return () => {
|
|
28197
|
+
clearTimeout(fadeTimer);
|
|
28198
|
+
clearTimeout(hideTimer);
|
|
28199
|
+
};
|
|
28200
|
+
}
|
|
28201
|
+
}, [show, duration]);
|
|
28202
|
+
if (!isVisible) return null;
|
|
28203
|
+
return /* @__PURE__ */ jsx(
|
|
28204
|
+
"div",
|
|
28205
|
+
{
|
|
28206
|
+
className: "absolute inset-0 flex items-center justify-center pointer-events-none z-10",
|
|
28207
|
+
style: {
|
|
28208
|
+
opacity: isFading ? 0 : 1,
|
|
28209
|
+
transition: `opacity ${duration - 100}ms ease-out`
|
|
28210
|
+
},
|
|
28211
|
+
children: /* @__PURE__ */ jsx("div", { className: "bg-black/70 rounded-full p-6", children: isPlaying ? (
|
|
28212
|
+
// Play icon (triangle)
|
|
28213
|
+
/* @__PURE__ */ jsx(
|
|
28214
|
+
"svg",
|
|
28215
|
+
{
|
|
28216
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
28217
|
+
viewBox: "0 0 24 24",
|
|
28218
|
+
fill: "white",
|
|
28219
|
+
className: "w-16 h-16",
|
|
28220
|
+
children: /* @__PURE__ */ jsx("path", { d: "M8 5v14l11-7z" })
|
|
28221
|
+
}
|
|
28222
|
+
)
|
|
28223
|
+
) : (
|
|
28224
|
+
// Pause icon (two bars)
|
|
28225
|
+
/* @__PURE__ */ jsx(
|
|
28226
|
+
"svg",
|
|
28227
|
+
{
|
|
28228
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
28229
|
+
viewBox: "0 0 24 24",
|
|
28230
|
+
fill: "white",
|
|
28231
|
+
className: "w-16 h-16",
|
|
28232
|
+
children: /* @__PURE__ */ jsx("path", { d: "M6 4h4v16H6V4zm8 0h4v16h-4V4z" })
|
|
28233
|
+
}
|
|
28234
|
+
)
|
|
28235
|
+
) })
|
|
28236
|
+
}
|
|
28237
|
+
);
|
|
28238
|
+
};
|
|
27604
28239
|
var BackButton = ({
|
|
27605
28240
|
onClick,
|
|
27606
28241
|
text = "Back",
|
|
@@ -31020,7 +31655,7 @@ function DiagnosisVideoModal({
|
|
|
31020
31655
|
}
|
|
31021
31656
|
loadClip();
|
|
31022
31657
|
}, [clipId, supabase, transformPlaylistUrls]);
|
|
31023
|
-
const
|
|
31658
|
+
const formatTime5 = (seconds) => {
|
|
31024
31659
|
const mins = Math.floor(seconds / 60);
|
|
31025
31660
|
const secs = Math.floor(seconds % 60);
|
|
31026
31661
|
return `${mins}:${secs.toString().padStart(2, "0")}`;
|
|
@@ -31212,9 +31847,9 @@ function DiagnosisVideoModal({
|
|
|
31212
31847
|
}
|
|
31213
31848
|
),
|
|
31214
31849
|
/* @__PURE__ */ jsxs("span", { className: "text-sm font-medium", children: [
|
|
31215
|
-
|
|
31850
|
+
formatTime5(currentTime),
|
|
31216
31851
|
" / ",
|
|
31217
|
-
|
|
31852
|
+
formatTime5(duration)
|
|
31218
31853
|
] }),
|
|
31219
31854
|
/* @__PURE__ */ jsx(
|
|
31220
31855
|
"input",
|
|
@@ -33034,45 +33669,35 @@ var LinePdfGenerator = ({
|
|
|
33034
33669
|
const doc = new jsPDF$1();
|
|
33035
33670
|
const pageHeight = doc.internal.pageSize.height;
|
|
33036
33671
|
const addHeaderPage1 = () => {
|
|
33037
|
-
doc.setFontSize(
|
|
33672
|
+
doc.setFontSize(14);
|
|
33038
33673
|
doc.setFont("helvetica", "bold");
|
|
33039
|
-
doc.setTextColor(
|
|
33674
|
+
doc.setTextColor(50, 50, 50);
|
|
33040
33675
|
doc.text("OPTIFYE.AI", 20, 15);
|
|
33041
|
-
doc.setFontSize(
|
|
33676
|
+
doc.setFontSize(9);
|
|
33042
33677
|
doc.setFont("helvetica", "normal");
|
|
33043
33678
|
doc.setTextColor(100, 100, 100);
|
|
33044
|
-
const
|
|
33045
|
-
const
|
|
33046
|
-
doc.text(
|
|
33047
|
-
doc.setDrawColor(
|
|
33048
|
-
doc.setLineWidth(0.
|
|
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);
|
|
33049
33684
|
doc.line(20, 20, 190, 20);
|
|
33050
33685
|
};
|
|
33051
33686
|
const addHeaderPage2 = () => {
|
|
33052
|
-
doc.setFontSize(
|
|
33687
|
+
doc.setFontSize(14);
|
|
33053
33688
|
doc.setFont("helvetica", "bold");
|
|
33054
|
-
doc.setTextColor(
|
|
33689
|
+
doc.setTextColor(50, 50, 50);
|
|
33055
33690
|
doc.text("OPTIFYE.AI", 20, 15);
|
|
33056
|
-
doc.setFontSize(
|
|
33691
|
+
doc.setFontSize(9);
|
|
33057
33692
|
doc.setFont("helvetica", "normal");
|
|
33058
33693
|
doc.setTextColor(100, 100, 100);
|
|
33059
|
-
const reportText = "REAL-TIME PERFORMANCE REPORT";
|
|
33060
|
-
const reportTextWidth = doc.getStringUnitWidth(reportText) * 10 / doc.internal.scaleFactor;
|
|
33061
|
-
doc.text(reportText, doc.internal.pageSize.width - 20 - reportTextWidth, 15);
|
|
33062
|
-
doc.text("Page 2", doc.internal.pageSize.width - 30, pageHeight - 15);
|
|
33063
|
-
doc.setDrawColor(220, 220, 220);
|
|
33064
|
-
doc.setLineWidth(0.3);
|
|
33065
|
-
doc.line(20, 20, 190, 20);
|
|
33066
|
-
return 35;
|
|
33067
|
-
};
|
|
33068
|
-
const addFooter = (pageNum) => {
|
|
33069
|
-
doc.setFontSize(9);
|
|
33070
|
-
doc.setTextColor(130, 130, 130);
|
|
33071
33694
|
const generatedText = `Generated on ${(/* @__PURE__ */ new Date()).toLocaleString("en-IN", { timeZone: "Asia/Kolkata" })}`;
|
|
33072
|
-
doc.
|
|
33073
|
-
|
|
33074
|
-
|
|
33075
|
-
|
|
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;
|
|
33076
33701
|
};
|
|
33077
33702
|
addHeaderPage1();
|
|
33078
33703
|
doc.setFontSize(26);
|
|
@@ -33118,37 +33743,47 @@ var LinePdfGenerator = ({
|
|
|
33118
33743
|
doc.setDrawColor(200, 200, 200);
|
|
33119
33744
|
doc.setLineWidth(0.5);
|
|
33120
33745
|
doc.line(20, 53, 190, 53);
|
|
33121
|
-
|
|
33746
|
+
const perfOverviewStartY = 58;
|
|
33747
|
+
doc.setFillColor(245, 245, 245);
|
|
33748
|
+
doc.roundedRect(15, perfOverviewStartY, 180, 50, 3, 3, "F");
|
|
33749
|
+
doc.setFontSize(18);
|
|
33122
33750
|
doc.setFont("helvetica", "bold");
|
|
33123
|
-
doc.setTextColor(
|
|
33124
|
-
doc.text("Line Performance Overview", 20,
|
|
33751
|
+
doc.setTextColor(40, 40, 40);
|
|
33752
|
+
doc.text("Line Performance Overview", 20, 68);
|
|
33125
33753
|
doc.setTextColor(0, 0, 0);
|
|
33126
33754
|
const createKPIBox = (y) => {
|
|
33127
|
-
doc.setFillColor(
|
|
33128
|
-
doc.roundedRect(22, y - 7, 165, 12,
|
|
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");
|
|
33129
33760
|
};
|
|
33130
|
-
|
|
33131
|
-
|
|
33761
|
+
const kpiStartY = 80;
|
|
33762
|
+
const kpiSpacing = 10;
|
|
33763
|
+
createKPIBox(kpiStartY);
|
|
33764
|
+
doc.setFontSize(11);
|
|
33132
33765
|
doc.setFont("helvetica", "normal");
|
|
33133
|
-
doc.text("Output:", 25,
|
|
33766
|
+
doc.text("Output:", 25, kpiStartY);
|
|
33134
33767
|
doc.setFont("helvetica", "bold");
|
|
33135
|
-
doc.text(`${lineInfo.metrics.current_output} / ${lineInfo.metrics.line_threshold}`, 120,
|
|
33136
|
-
createKPIBox(
|
|
33768
|
+
doc.text(`${lineInfo.metrics.current_output} / ${lineInfo.metrics.line_threshold}`, 120, kpiStartY);
|
|
33769
|
+
createKPIBox(kpiStartY + kpiSpacing);
|
|
33137
33770
|
doc.setFont("helvetica", "normal");
|
|
33138
|
-
doc.text("Underperforming Workspaces:", 25,
|
|
33771
|
+
doc.text("Underperforming Workspaces:", 25, kpiStartY + kpiSpacing);
|
|
33139
33772
|
doc.setFont("helvetica", "bold");
|
|
33140
|
-
doc.text(`${lineInfo.metrics.underperforming_workspaces} / ${lineInfo.metrics.total_workspaces}`, 120,
|
|
33141
|
-
createKPIBox(
|
|
33773
|
+
doc.text(`${lineInfo.metrics.underperforming_workspaces} / ${lineInfo.metrics.total_workspaces}`, 120, kpiStartY + kpiSpacing);
|
|
33774
|
+
createKPIBox(kpiStartY + kpiSpacing * 2);
|
|
33142
33775
|
doc.setFont("helvetica", "normal");
|
|
33143
|
-
doc.text("Average Efficiency:", 25,
|
|
33776
|
+
doc.text("Average Efficiency:", 25, kpiStartY + kpiSpacing * 2);
|
|
33144
33777
|
doc.setFont("helvetica", "bold");
|
|
33145
|
-
doc.text(`${lineInfo.metrics.avg_efficiency.toFixed(1)}%`, 120,
|
|
33146
|
-
doc.setDrawColor(
|
|
33147
|
-
doc.
|
|
33148
|
-
doc.
|
|
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);
|
|
33149
33784
|
doc.setFont("helvetica", "bold");
|
|
33150
|
-
doc.setTextColor(
|
|
33151
|
-
doc.text("Hourly Output Overview", 20,
|
|
33785
|
+
doc.setTextColor(40, 40, 40);
|
|
33786
|
+
doc.text("Hourly Output Overview", 20, 133);
|
|
33152
33787
|
doc.setTextColor(0, 0, 0);
|
|
33153
33788
|
const getHourlyTimeRanges = (startTimeStr, endTimeStr) => {
|
|
33154
33789
|
const [hours, minutes] = startTimeStr.split(":");
|
|
@@ -33185,7 +33820,7 @@ var LinePdfGenerator = ({
|
|
|
33185
33820
|
}
|
|
33186
33821
|
hourEndTime.setSeconds(0);
|
|
33187
33822
|
hourEndTime.setMilliseconds(0);
|
|
33188
|
-
const
|
|
33823
|
+
const formatTime5 = (date2) => {
|
|
33189
33824
|
return date2.toLocaleTimeString("en-IN", {
|
|
33190
33825
|
hour: "2-digit",
|
|
33191
33826
|
minute: "2-digit",
|
|
@@ -33193,7 +33828,7 @@ var LinePdfGenerator = ({
|
|
|
33193
33828
|
timeZone: "Asia/Kolkata"
|
|
33194
33829
|
});
|
|
33195
33830
|
};
|
|
33196
|
-
return `${
|
|
33831
|
+
return `${formatTime5(hourStartTime)} - ${formatTime5(hourEndTime)}`;
|
|
33197
33832
|
});
|
|
33198
33833
|
};
|
|
33199
33834
|
const hourlyTimeRanges = lineInfo.metrics.shift_start ? getHourlyTimeRanges(lineInfo.metrics.shift_start, lineInfo.metrics.shift_end) : [];
|
|
@@ -33340,86 +33975,94 @@ var LinePdfGenerator = ({
|
|
|
33340
33975
|
return Math.round(lineInfo.metrics.current_output / shiftDuration);
|
|
33341
33976
|
});
|
|
33342
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");
|
|
33343
33986
|
doc.setFontSize(11);
|
|
33344
33987
|
doc.setFont("helvetica", "bold");
|
|
33345
|
-
doc.
|
|
33346
|
-
doc.
|
|
33347
|
-
doc.text("
|
|
33348
|
-
doc.text("
|
|
33349
|
-
doc.
|
|
33350
|
-
doc.
|
|
33351
|
-
doc.
|
|
33352
|
-
doc.setDrawColor(220, 220, 220);
|
|
33353
|
-
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);
|
|
33354
33995
|
doc.setFont("helvetica", "normal");
|
|
33355
|
-
let yPos =
|
|
33356
|
-
const
|
|
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
|
+
}
|
|
33357
34008
|
hourlyTimeRanges.forEach((timeRange, index) => {
|
|
33358
|
-
|
|
33359
|
-
|
|
33360
|
-
|
|
33361
|
-
|
|
33362
|
-
const
|
|
33363
|
-
const
|
|
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();
|
|
33364
34016
|
doc.text(timeRange, 25, yPos);
|
|
33365
|
-
doc.text(
|
|
33366
|
-
doc.text(
|
|
33367
|
-
if (
|
|
33368
|
-
doc.
|
|
33369
|
-
doc.
|
|
33370
|
-
|
|
33371
|
-
|
|
33372
|
-
doc.
|
|
33373
|
-
doc.
|
|
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");
|
|
33374
34027
|
} else {
|
|
33375
|
-
doc.
|
|
33376
|
-
doc.
|
|
33377
|
-
const crossX = 170;
|
|
33378
|
-
const crossY = yPos - 1;
|
|
33379
|
-
doc.line(crossX - 2, crossY - 2, crossX + 2, crossY + 2);
|
|
33380
|
-
doc.line(crossX - 2, crossY + 2, crossX + 2, crossY - 2);
|
|
34028
|
+
doc.setTextColor(227, 67, 41);
|
|
34029
|
+
doc.text("\xD7", 170, yPos);
|
|
33381
34030
|
}
|
|
33382
|
-
doc.
|
|
33383
|
-
doc.setLineWidth(0.2);
|
|
34031
|
+
doc.setTextColor(0, 0, 0);
|
|
33384
34032
|
yPos += rowSpacing;
|
|
33385
34033
|
});
|
|
33386
|
-
doc.roundedRect(20, 140, 170, yPos - 140 - 3, 1, 1, "S");
|
|
33387
|
-
addFooter(1);
|
|
33388
34034
|
doc.addPage();
|
|
33389
34035
|
yPos = addHeaderPage2();
|
|
33390
|
-
|
|
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);
|
|
33391
34043
|
doc.setFont("helvetica", "bold");
|
|
33392
|
-
doc.setTextColor(
|
|
34044
|
+
doc.setTextColor(40, 40, 40);
|
|
33393
34045
|
doc.text("Poorest Performing Workspaces", 20, yPos);
|
|
33394
34046
|
doc.setTextColor(0, 0, 0);
|
|
33395
34047
|
yPos += 10;
|
|
33396
34048
|
doc.setFontSize(11);
|
|
33397
34049
|
doc.setFont("helvetica", "bold");
|
|
33398
|
-
doc.setFillColor(245, 245, 245);
|
|
33399
|
-
doc.roundedRect(20, yPos, 170, 8, 1, 1, "F");
|
|
33400
34050
|
yPos += 5;
|
|
33401
34051
|
doc.text("Workspace", 25, yPos);
|
|
33402
|
-
doc.text("Current/
|
|
34052
|
+
doc.text("Current/Target", 85, yPos);
|
|
33403
34053
|
doc.text("Efficiency", 145, yPos);
|
|
33404
34054
|
yPos += 3;
|
|
33405
|
-
doc.setLineWidth(0.
|
|
33406
|
-
doc.setDrawColor(
|
|
34055
|
+
doc.setLineWidth(0.3);
|
|
34056
|
+
doc.setDrawColor(200, 200, 200);
|
|
33407
34057
|
doc.line(20, yPos, 190, yPos);
|
|
33408
34058
|
doc.setFont("helvetica", "normal");
|
|
33409
|
-
const tableStartY = yPos;
|
|
33410
34059
|
yPos += 7;
|
|
33411
|
-
const sortedWorkspaces = [...workspaceData].sort((a, b) => (a.efficiency || 0) - (b.efficiency || 0)).slice(0, 10);
|
|
33412
34060
|
if (sortedWorkspaces.length === 0) {
|
|
33413
34061
|
doc.text("No workspace data available", 25, yPos);
|
|
33414
34062
|
yPos += 10;
|
|
33415
34063
|
} else {
|
|
33416
34064
|
sortedWorkspaces.forEach((ws, index) => {
|
|
33417
|
-
if (index % 2 === 0) {
|
|
33418
|
-
doc.setFillColor(252, 252, 252);
|
|
33419
|
-
doc.roundedRect(20, yPos - 5, 170, 8, 1, 1, "F");
|
|
33420
|
-
}
|
|
33421
34065
|
const workspaceName = getWorkspaceDisplayName(ws.workspace_name, lineInfo.line_id);
|
|
33422
|
-
const maxWidth = 55;
|
|
33423
34066
|
const truncatedName = workspaceName.length > 25 ? workspaceName.substring(0, 22) + "..." : workspaceName;
|
|
33424
34067
|
doc.text(truncatedName, 25, yPos);
|
|
33425
34068
|
doc.text(`${ws.action_count || 0} / ${ws.action_threshold || 0}`, 85, yPos);
|
|
@@ -33427,9 +34070,6 @@ var LinePdfGenerator = ({
|
|
|
33427
34070
|
yPos += 8;
|
|
33428
34071
|
});
|
|
33429
34072
|
}
|
|
33430
|
-
const wsTableHeight = yPos - tableStartY - 3;
|
|
33431
|
-
doc.roundedRect(20, tableStartY, 170, wsTableHeight, 1, 1, "S");
|
|
33432
|
-
addFooter(2);
|
|
33433
34073
|
doc.save(`${lineInfo.line_name}_${date.split(",")[0]}.pdf`);
|
|
33434
34074
|
} catch (error) {
|
|
33435
34075
|
console.error("PDF generation failed:", error);
|
|
@@ -34603,25 +35243,25 @@ var WorkspacePdfGenerator = ({ workspace, className }) => {
|
|
|
34603
35243
|
doc.setFont("helvetica", "bold");
|
|
34604
35244
|
doc.setTextColor(50, 50, 50);
|
|
34605
35245
|
doc.text("OPTIFYE.AI", 20, 15);
|
|
34606
|
-
doc.setFontSize(
|
|
35246
|
+
doc.setFontSize(9);
|
|
34607
35247
|
doc.setFont("helvetica", "normal");
|
|
34608
|
-
doc.setTextColor(
|
|
34609
|
-
const
|
|
34610
|
-
const
|
|
34611
|
-
doc.text(
|
|
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);
|
|
34612
35252
|
doc.setDrawColor(200, 200, 200);
|
|
34613
35253
|
doc.setLineWidth(0.5);
|
|
34614
35254
|
doc.line(20, 20, 190, 20);
|
|
34615
35255
|
doc.setFillColor(250, 250, 250);
|
|
34616
|
-
doc.roundedRect(15, 25, 180,
|
|
35256
|
+
doc.roundedRect(15, 25, 180, 53, 3, 3, "F");
|
|
34617
35257
|
doc.setFontSize(32);
|
|
34618
35258
|
doc.setFont("helvetica", "bold");
|
|
34619
35259
|
doc.setTextColor(0, 0, 0);
|
|
34620
|
-
doc.text(lineName, 20,
|
|
35260
|
+
doc.text(lineName, 20, 39);
|
|
34621
35261
|
doc.setFontSize(22);
|
|
34622
35262
|
doc.setFont("helvetica", "normal");
|
|
34623
35263
|
doc.setTextColor(40, 40, 40);
|
|
34624
|
-
doc.text(getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id), 20,
|
|
35264
|
+
doc.text(getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id), 20, 51);
|
|
34625
35265
|
doc.setFontSize(13);
|
|
34626
35266
|
doc.setFont("helvetica", "normal");
|
|
34627
35267
|
doc.setTextColor(60, 60, 60);
|
|
@@ -34632,8 +35272,8 @@ var WorkspacePdfGenerator = ({ workspace, className }) => {
|
|
|
34632
35272
|
timeZone: "Asia/Kolkata"
|
|
34633
35273
|
});
|
|
34634
35274
|
const shiftType = "Day Shift";
|
|
34635
|
-
doc.text(`${date}`, 20,
|
|
34636
|
-
doc.text(`${shiftType}`, 20,
|
|
35275
|
+
doc.text(`${date}`, 20, 63);
|
|
35276
|
+
doc.text(`${shiftType}`, 20, 71);
|
|
34637
35277
|
const currentTime = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-IN", {
|
|
34638
35278
|
hour: "2-digit",
|
|
34639
35279
|
minute: "2-digit",
|
|
@@ -34646,11 +35286,11 @@ var WorkspacePdfGenerator = ({ workspace, className }) => {
|
|
|
34646
35286
|
});
|
|
34647
35287
|
doc.setFontSize(12);
|
|
34648
35288
|
doc.setTextColor(80, 80, 80);
|
|
34649
|
-
doc.text(`Report Period: ${shiftStartTime} - ${currentTime}`, 20,
|
|
35289
|
+
doc.text(`Report Period: ${shiftStartTime} - ${currentTime}`, 20, 79);
|
|
34650
35290
|
doc.setTextColor(0, 0, 0);
|
|
34651
35291
|
doc.setDrawColor(180, 180, 180);
|
|
34652
35292
|
doc.setLineWidth(0.8);
|
|
34653
|
-
doc.line(20,
|
|
35293
|
+
doc.line(20, 88, 190, 88);
|
|
34654
35294
|
const createKPIBox = (y) => {
|
|
34655
35295
|
doc.setFillColor(255, 255, 255);
|
|
34656
35296
|
doc.roundedRect(22, y - 7, 165, 12, 2, 2, "F");
|
|
@@ -34658,66 +35298,72 @@ var WorkspacePdfGenerator = ({ workspace, className }) => {
|
|
|
34658
35298
|
doc.setLineWidth(0.2);
|
|
34659
35299
|
doc.roundedRect(22, y - 7, 165, 12, 2, 2, "S");
|
|
34660
35300
|
};
|
|
35301
|
+
const perfOverviewStartY = 93;
|
|
34661
35302
|
doc.setFillColor(245, 245, 245);
|
|
34662
|
-
doc.roundedRect(15,
|
|
35303
|
+
doc.roundedRect(15, perfOverviewStartY, 180, 60, 3, 3, "F");
|
|
34663
35304
|
doc.setFontSize(18);
|
|
34664
35305
|
doc.setFont("helvetica", "bold");
|
|
34665
35306
|
doc.setTextColor(40, 40, 40);
|
|
34666
|
-
doc.text("Performance Overview", 20,
|
|
35307
|
+
doc.text("Performance Overview", 20, 103);
|
|
34667
35308
|
doc.setTextColor(0, 0, 0);
|
|
34668
|
-
const kpiStartY =
|
|
35309
|
+
const kpiStartY = 115;
|
|
34669
35310
|
const kpiSpacing = 10;
|
|
34670
35311
|
createKPIBox(kpiStartY);
|
|
34671
35312
|
doc.setFontSize(11);
|
|
34672
35313
|
doc.setFont("helvetica", "normal");
|
|
34673
|
-
doc.text("
|
|
35314
|
+
doc.text("Current Output/Target Output:", 25, kpiStartY);
|
|
34674
35315
|
doc.setFont("helvetica", "bold");
|
|
34675
|
-
doc.text(`${
|
|
35316
|
+
doc.text(`${workspace.total_actions} / ${workspace.target_output}`, 120, kpiStartY);
|
|
34676
35317
|
createKPIBox(kpiStartY + kpiSpacing);
|
|
34677
35318
|
doc.setFont("helvetica", "normal");
|
|
34678
|
-
doc.text("
|
|
35319
|
+
doc.text("Efficiency:", 25, kpiStartY + kpiSpacing);
|
|
34679
35320
|
doc.setFont("helvetica", "bold");
|
|
34680
|
-
doc.text(`${workspace.
|
|
35321
|
+
doc.text(`${(workspace.avg_efficiency || 0).toFixed(1)}% (Target: 80%)`, 120, kpiStartY + kpiSpacing);
|
|
34681
35322
|
createKPIBox(kpiStartY + kpiSpacing * 2);
|
|
34682
35323
|
doc.setFont("helvetica", "normal");
|
|
34683
|
-
doc.text("
|
|
34684
|
-
doc.setFont("helvetica", "bold");
|
|
34685
|
-
doc.text(`${(workspace.avg_efficiency || 0).toFixed(1)}% (Target: 80%)`, 120, kpiStartY + kpiSpacing * 2);
|
|
34686
|
-
createKPIBox(kpiStartY + kpiSpacing * 3);
|
|
34687
|
-
doc.setFont("helvetica", "normal");
|
|
34688
|
-
doc.text("PPH (Pieces Per Hour):", 25, kpiStartY + kpiSpacing * 3);
|
|
35324
|
+
doc.text("PPH (Pieces Per Hour):", 25, kpiStartY + kpiSpacing * 2);
|
|
34689
35325
|
doc.setFont("helvetica", "bold");
|
|
34690
|
-
doc.text(`${workspace.avg_pph.toFixed(1)} (Standard: ${workspace.pph_threshold.toFixed(1)})`, 120, kpiStartY + kpiSpacing *
|
|
35326
|
+
doc.text(`${workspace.avg_pph.toFixed(1)} (Standard: ${workspace.pph_threshold.toFixed(1)})`, 120, kpiStartY + kpiSpacing * 2);
|
|
34691
35327
|
doc.setDrawColor(180, 180, 180);
|
|
34692
35328
|
doc.setLineWidth(0.8);
|
|
34693
|
-
doc.line(20,
|
|
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;
|
|
34694
35337
|
doc.setFillColor(245, 245, 245);
|
|
34695
|
-
doc.roundedRect(15,
|
|
35338
|
+
doc.roundedRect(15, hourlyPerfStartY, 180, backgroundHeight, 3, 3, "F");
|
|
34696
35339
|
doc.setFontSize(18);
|
|
34697
35340
|
doc.setFont("helvetica", "bold");
|
|
34698
35341
|
doc.setTextColor(40, 40, 40);
|
|
34699
|
-
doc.text("Hourly Performance", 20,
|
|
35342
|
+
doc.text("Hourly Performance", 20, 178);
|
|
34700
35343
|
doc.setTextColor(0, 0, 0);
|
|
34701
35344
|
doc.setFontSize(11);
|
|
34702
35345
|
doc.setFont("helvetica", "bold");
|
|
34703
|
-
doc.
|
|
34704
|
-
doc.
|
|
34705
|
-
doc.text("
|
|
34706
|
-
doc.text("
|
|
34707
|
-
doc.
|
|
34708
|
-
doc.
|
|
34709
|
-
doc.
|
|
34710
|
-
doc.setDrawColor(220, 220, 220);
|
|
34711
|
-
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);
|
|
34712
35353
|
doc.setFont("helvetica", "normal");
|
|
34713
|
-
let yPos =
|
|
34714
|
-
const
|
|
34715
|
-
const
|
|
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
|
+
}
|
|
34716
35366
|
hourlyData.forEach((output, index) => {
|
|
34717
|
-
if (index % 2 === 0) {
|
|
34718
|
-
doc.setFillColor(252, 252, 252);
|
|
34719
|
-
doc.roundedRect(20, yPos - 5, 170, 8, 1, 1, "F");
|
|
34720
|
-
}
|
|
34721
35367
|
const startTime = /* @__PURE__ */ new Date(`2000-01-01 ${workspace.shift_start}`);
|
|
34722
35368
|
startTime.setHours(startTime.getHours() + index);
|
|
34723
35369
|
const endTime = new Date(startTime);
|
|
@@ -34729,14 +35375,17 @@ var WorkspacePdfGenerator = ({ workspace, className }) => {
|
|
|
34729
35375
|
hour: "numeric",
|
|
34730
35376
|
hour12: true
|
|
34731
35377
|
})}`;
|
|
34732
|
-
const
|
|
35378
|
+
const hourNumber = startTime.getHours();
|
|
35379
|
+
const dataCollected = !isToday || hourNumber < currentHour;
|
|
35380
|
+
const outputStr = dataCollected ? output.toString() : "TBD";
|
|
34733
35381
|
const targetStr = hourlyTarget.toString();
|
|
34734
|
-
const outputX = 95 + doc.getStringUnitWidth(outputStr) * doc.getFontSize() / (2 * doc.internal.scaleFactor);
|
|
34735
|
-
const targetX = 135 + doc.getStringUnitWidth(targetStr) * doc.getFontSize() / (2 * doc.internal.scaleFactor);
|
|
34736
35382
|
doc.text(timeRange, 25, yPos);
|
|
34737
|
-
doc.text(outputStr,
|
|
34738
|
-
doc.text(targetStr,
|
|
34739
|
-
if (
|
|
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) {
|
|
34740
35389
|
doc.setTextColor(0, 171, 69);
|
|
34741
35390
|
doc.setFont("ZapfDingbats", "normal");
|
|
34742
35391
|
doc.text("4", 170, yPos);
|
|
@@ -34746,15 +35395,8 @@ var WorkspacePdfGenerator = ({ workspace, className }) => {
|
|
|
34746
35395
|
doc.text("\xD7", 170, yPos);
|
|
34747
35396
|
}
|
|
34748
35397
|
doc.setTextColor(0, 0, 0);
|
|
34749
|
-
yPos +=
|
|
35398
|
+
yPos += rowHeight;
|
|
34750
35399
|
});
|
|
34751
|
-
doc.setLineWidth(0.2);
|
|
34752
|
-
doc.setDrawColor(220, 220, 220);
|
|
34753
|
-
doc.roundedRect(20, 187, 170, yPos - 187 - 3, 1, 1, "S");
|
|
34754
|
-
doc.setFontSize(9);
|
|
34755
|
-
doc.setTextColor(130, 130, 130);
|
|
34756
|
-
const generatedText = `Generated on ${(/* @__PURE__ */ new Date()).toLocaleString("en-IN", { timeZone: "Asia/Kolkata" })}`;
|
|
34757
|
-
doc.text(generatedText, 20, 280);
|
|
34758
35400
|
doc.save(`${workspace.workspace_name}_${date.split(",")[0]}.pdf`);
|
|
34759
35401
|
} catch (error) {
|
|
34760
35402
|
console.error("PDF generation failed:", error);
|
|
@@ -35834,7 +36476,8 @@ var WorkspaceHealthCard = ({
|
|
|
35834
36476
|
workspace,
|
|
35835
36477
|
onClick,
|
|
35836
36478
|
showDetails = true,
|
|
35837
|
-
className = ""
|
|
36479
|
+
className = "",
|
|
36480
|
+
onViewDetails
|
|
35838
36481
|
}) => {
|
|
35839
36482
|
const getStatusConfig = () => {
|
|
35840
36483
|
switch (workspace.status) {
|
|
@@ -35887,38 +36530,101 @@ var WorkspaceHealthCard = ({
|
|
|
35887
36530
|
onClick(workspace);
|
|
35888
36531
|
}
|
|
35889
36532
|
};
|
|
36533
|
+
const handleViewDetails = (event) => {
|
|
36534
|
+
event.stopPropagation();
|
|
36535
|
+
event.preventDefault();
|
|
36536
|
+
if (onViewDetails) {
|
|
36537
|
+
onViewDetails(workspace);
|
|
36538
|
+
}
|
|
36539
|
+
};
|
|
35890
36540
|
const handleKeyDown = (event) => {
|
|
35891
36541
|
if (onClick && (event.key === "Enter" || event.key === " ")) {
|
|
35892
36542
|
event.preventDefault();
|
|
35893
36543
|
onClick(workspace);
|
|
35894
36544
|
}
|
|
35895
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
|
+
};
|
|
35896
36561
|
const formatTimeAgo = (timeString) => {
|
|
35897
|
-
|
|
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`;
|
|
35898
36591
|
};
|
|
35899
|
-
const
|
|
35900
|
-
if (!uptimeDetails)
|
|
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
|
+
}
|
|
35901
36603
|
const downtimeMinutes = Math.max(0, uptimeDetails.expectedMinutes - uptimeDetails.actualMinutes);
|
|
35902
|
-
if (downtimeMinutes === 0)
|
|
35903
|
-
|
|
35904
|
-
|
|
35905
|
-
|
|
35906
|
-
|
|
35907
|
-
|
|
35908
|
-
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
|
+
};
|
|
35909
36610
|
}
|
|
35910
|
-
return
|
|
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
|
+
};
|
|
35911
36616
|
};
|
|
36617
|
+
const downtimeConfig = getDowntimeConfig(workspace.uptimeDetails);
|
|
35912
36618
|
return /* @__PURE__ */ jsx(
|
|
35913
36619
|
Card2,
|
|
35914
36620
|
{
|
|
35915
36621
|
className: clsx(
|
|
35916
|
-
"relative overflow-hidden transition-all duration-300",
|
|
36622
|
+
"relative overflow-hidden transition-all duration-300 h-full flex flex-col",
|
|
35917
36623
|
"bg-gradient-to-br",
|
|
35918
36624
|
config.gradient,
|
|
35919
36625
|
"border",
|
|
35920
36626
|
config.border,
|
|
35921
|
-
"shadow-sm hover:shadow-
|
|
36627
|
+
"shadow-sm hover:shadow-lg hover:border-blue-300 dark:hover:border-blue-700",
|
|
35922
36628
|
onClick && "cursor-pointer hover:scale-[1.01]",
|
|
35923
36629
|
workspace.isStale && "opacity-90",
|
|
35924
36630
|
className
|
|
@@ -35928,7 +36634,7 @@ var WorkspaceHealthCard = ({
|
|
|
35928
36634
|
tabIndex: onClick ? 0 : void 0,
|
|
35929
36635
|
role: onClick ? "button" : void 0,
|
|
35930
36636
|
"aria-label": `Workspace ${workspace.workspace_display_name} status: ${workspace.status}`,
|
|
35931
|
-
children: /* @__PURE__ */ jsxs("div", { className: "p-4", children: [
|
|
36637
|
+
children: /* @__PURE__ */ jsxs("div", { className: "p-4 flex-grow flex flex-col", children: [
|
|
35932
36638
|
/* @__PURE__ */ jsx("div", { className: "mb-3", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-start gap-2 sm:gap-3", children: [
|
|
35933
36639
|
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
35934
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)}` }),
|
|
@@ -35950,21 +36656,20 @@ var WorkspaceHealthCard = ({
|
|
|
35950
36656
|
/* @__PURE__ */ jsx("span", { className: "font-medium", children: formatTimeAgo(workspace.timeSinceLastUpdate) })
|
|
35951
36657
|
] })
|
|
35952
36658
|
] }),
|
|
35953
|
-
|
|
35954
|
-
/* @__PURE__ */ jsx(
|
|
35955
|
-
/* @__PURE__ */
|
|
35956
|
-
|
|
35957
|
-
|
|
35958
|
-
|
|
35959
|
-
|
|
35960
|
-
|
|
35961
|
-
|
|
35962
|
-
|
|
35963
|
-
|
|
35964
|
-
"
|
|
35965
|
-
|
|
35966
|
-
|
|
35967
|
-
] })
|
|
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
|
+
) })
|
|
35968
36673
|
] })
|
|
35969
36674
|
}
|
|
35970
36675
|
);
|
|
@@ -35972,8 +36677,11 @@ var WorkspaceHealthCard = ({
|
|
|
35972
36677
|
var CompactWorkspaceHealthCard = ({
|
|
35973
36678
|
workspace,
|
|
35974
36679
|
onClick,
|
|
35975
|
-
className = ""
|
|
36680
|
+
className = "",
|
|
36681
|
+
onViewDetails
|
|
35976
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`;
|
|
35977
36685
|
const getStatusConfig = () => {
|
|
35978
36686
|
switch (workspace.status) {
|
|
35979
36687
|
case "healthy":
|
|
@@ -36013,6 +36721,13 @@ var CompactWorkspaceHealthCard = ({
|
|
|
36013
36721
|
onClick(workspace);
|
|
36014
36722
|
}
|
|
36015
36723
|
};
|
|
36724
|
+
const handleViewDetails = (event) => {
|
|
36725
|
+
event.stopPropagation();
|
|
36726
|
+
event.preventDefault();
|
|
36727
|
+
if (onViewDetails) {
|
|
36728
|
+
onViewDetails(workspace);
|
|
36729
|
+
}
|
|
36730
|
+
};
|
|
36016
36731
|
return /* @__PURE__ */ jsxs(
|
|
36017
36732
|
"div",
|
|
36018
36733
|
{
|
|
@@ -36040,14 +36755,20 @@ var CompactWorkspaceHealthCard = ({
|
|
|
36040
36755
|
workspace.uptimePercentage.toFixed(1),
|
|
36041
36756
|
"%"
|
|
36042
36757
|
] }),
|
|
36043
|
-
|
|
36044
|
-
|
|
36045
|
-
Math.max(0, workspace.uptimeDetails.expectedMinutes - workspace.uptimeDetails.actualMinutes),
|
|
36046
|
-
"m down"
|
|
36047
|
-
] })
|
|
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 })
|
|
36048
36760
|
] }),
|
|
36049
36761
|
/* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: workspace.timeSinceLastUpdate }),
|
|
36050
|
-
/* @__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
|
+
)
|
|
36051
36772
|
] })
|
|
36052
36773
|
]
|
|
36053
36774
|
}
|
|
@@ -36056,6 +36777,7 @@ var CompactWorkspaceHealthCard = ({
|
|
|
36056
36777
|
var HealthStatusGrid = ({
|
|
36057
36778
|
workspaces,
|
|
36058
36779
|
onWorkspaceClick,
|
|
36780
|
+
onWorkspaceViewDetails,
|
|
36059
36781
|
showFilters = true,
|
|
36060
36782
|
groupBy: initialGroupBy = "none",
|
|
36061
36783
|
className = ""
|
|
@@ -36243,7 +36965,8 @@ var HealthStatusGrid = ({
|
|
|
36243
36965
|
{
|
|
36244
36966
|
workspace,
|
|
36245
36967
|
onClick: onWorkspaceClick,
|
|
36246
|
-
showDetails: true
|
|
36968
|
+
showDetails: true,
|
|
36969
|
+
onViewDetails: onWorkspaceViewDetails
|
|
36247
36970
|
},
|
|
36248
36971
|
workspace.workspace_id
|
|
36249
36972
|
)) })
|
|
@@ -39779,7 +40502,7 @@ var AIAgentView = () => {
|
|
|
39779
40502
|
}
|
|
39780
40503
|
return formattedLines.join("");
|
|
39781
40504
|
};
|
|
39782
|
-
const
|
|
40505
|
+
const formatTime5 = (timestamp) => {
|
|
39783
40506
|
const date = new Date(timestamp);
|
|
39784
40507
|
return date.toLocaleTimeString([], {
|
|
39785
40508
|
hour: "2-digit",
|
|
@@ -41043,7 +41766,7 @@ var AIAgentView = () => {
|
|
|
41043
41766
|
}
|
|
41044
41767
|
),
|
|
41045
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: [
|
|
41046
|
-
/* @__PURE__ */ jsx("span", { children:
|
|
41769
|
+
/* @__PURE__ */ jsx("span", { children: formatTime5(message.created_at) }),
|
|
41047
41770
|
message.role === "assistant" && message.id !== -1 && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
41048
41771
|
/* @__PURE__ */ jsx("div", { className: "w-1 h-1 bg-gray-300 rounded-full" }),
|
|
41049
41772
|
/* @__PURE__ */ jsx("span", { children: "Axel" })
|
|
@@ -43874,8 +44597,7 @@ var KPIsOverviewView = ({
|
|
|
43874
44597
|
" Shift"
|
|
43875
44598
|
] })
|
|
43876
44599
|
] })
|
|
43877
|
-
] }) })
|
|
43878
|
-
/* @__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
|
+
] }) })
|
|
43879
44601
|
] }) }),
|
|
43880
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(
|
|
43881
44603
|
LineCard,
|
|
@@ -48192,18 +48914,41 @@ var useWorkspaceHealth = (options) => {
|
|
|
48192
48914
|
const isFetchingRef = useRef(false);
|
|
48193
48915
|
const refreshIntervalRef = useRef(null);
|
|
48194
48916
|
const healthTable = databaseConfig?.tables?.workspace_health || "workspace_health_status";
|
|
48195
|
-
const
|
|
48196
|
-
|
|
48197
|
-
const
|
|
48198
|
-
const
|
|
48199
|
-
const
|
|
48200
|
-
|
|
48201
|
-
|
|
48202
|
-
|
|
48203
|
-
|
|
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
|
+
}, []);
|
|
48204
48947
|
const fetchWorkspacesHealth = useCallback(async () => {
|
|
48205
|
-
if (
|
|
48948
|
+
if (isFetchingRef.current) return;
|
|
48206
48949
|
if (!options.companyId) {
|
|
48950
|
+
setWorkspaces([]);
|
|
48951
|
+
setSummary(computeSummary([]));
|
|
48207
48952
|
setLoading(false);
|
|
48208
48953
|
return;
|
|
48209
48954
|
}
|
|
@@ -48211,39 +48956,12 @@ var useWorkspaceHealth = (options) => {
|
|
|
48211
48956
|
isFetchingRef.current = true;
|
|
48212
48957
|
setLoading(true);
|
|
48213
48958
|
setError(null);
|
|
48214
|
-
|
|
48215
|
-
|
|
48216
|
-
|
|
48217
|
-
}
|
|
48218
|
-
query = query.order("workspace_display_name", { ascending: true });
|
|
48219
|
-
const { data, error: fetchError } = await query;
|
|
48220
|
-
if (fetchError) throw fetchError;
|
|
48221
|
-
const healthData = data || [];
|
|
48222
|
-
const workspacesWithStatus = healthData.map((ws) => {
|
|
48223
|
-
const status = calculateHealthStatus(ws);
|
|
48224
|
-
const now2 = /* @__PURE__ */ new Date();
|
|
48225
|
-
const lastUpdate = ws.last_heartbeat ? new Date(ws.last_heartbeat) : null;
|
|
48226
|
-
const timeSinceLastUpdate = lastUpdate ? `${Math.floor((now2.getTime() - lastUpdate.getTime()) / 6e4)}m ago` : "Never";
|
|
48227
|
-
return {
|
|
48228
|
-
...ws,
|
|
48229
|
-
status,
|
|
48230
|
-
timeSinceLastUpdate,
|
|
48231
|
-
isStale: !lastUpdate || now2.getTime() - lastUpdate.getTime() > 15 * 6e4
|
|
48232
|
-
};
|
|
48959
|
+
const workspacesWithStatus = await workspaceHealthService.getWorkspaceHealthStatus({
|
|
48960
|
+
lineId: options.lineId,
|
|
48961
|
+
companyId: options.companyId
|
|
48233
48962
|
});
|
|
48234
48963
|
setWorkspaces(workspacesWithStatus);
|
|
48235
|
-
|
|
48236
|
-
const healthy = workspacesWithStatus.filter((w) => w.status === "healthy").length;
|
|
48237
|
-
const unhealthy = workspacesWithStatus.filter((w) => w.status === "unhealthy").length;
|
|
48238
|
-
const warning6 = workspacesWithStatus.filter((w) => w.status === "warning").length;
|
|
48239
|
-
setSummary({
|
|
48240
|
-
totalWorkspaces: total,
|
|
48241
|
-
healthyWorkspaces: healthy,
|
|
48242
|
-
unhealthyWorkspaces: unhealthy,
|
|
48243
|
-
warningWorkspaces: warning6,
|
|
48244
|
-
uptimePercentage: total > 0 ? healthy / total * 100 : 100,
|
|
48245
|
-
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
48246
|
-
});
|
|
48964
|
+
setSummary(computeSummary(workspacesWithStatus));
|
|
48247
48965
|
} catch (err) {
|
|
48248
48966
|
console.error("[useWorkspaceHealth] Error fetching workspace health:", err);
|
|
48249
48967
|
setError({ message: err.message, code: err.code || "FETCH_ERROR" });
|
|
@@ -48252,7 +48970,7 @@ var useWorkspaceHealth = (options) => {
|
|
|
48252
48970
|
setLoading(false);
|
|
48253
48971
|
isFetchingRef.current = false;
|
|
48254
48972
|
}
|
|
48255
|
-
}, [
|
|
48973
|
+
}, [options.companyId, options.lineId, computeSummary]);
|
|
48256
48974
|
useEffect(() => {
|
|
48257
48975
|
fetchWorkspacesHealth();
|
|
48258
48976
|
}, [fetchWorkspacesHealth]);
|
|
@@ -48297,6 +49015,395 @@ var useWorkspaceHealth = (options) => {
|
|
|
48297
49015
|
refetch: fetchWorkspacesHealth
|
|
48298
49016
|
};
|
|
48299
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;
|
|
48300
49407
|
var WorkspaceHealthView = ({
|
|
48301
49408
|
lineId,
|
|
48302
49409
|
companyId,
|
|
@@ -48306,6 +49413,7 @@ var WorkspaceHealthView = ({
|
|
|
48306
49413
|
const router = useRouter();
|
|
48307
49414
|
const [groupBy, setGroupBy] = useState("line");
|
|
48308
49415
|
const timezone = useAppTimezone();
|
|
49416
|
+
const [selectedWorkspace, setSelectedWorkspace] = useState(null);
|
|
48309
49417
|
const operationalDate = getOperationalDate(timezone || "UTC");
|
|
48310
49418
|
const currentHour = (/* @__PURE__ */ new Date()).getHours();
|
|
48311
49419
|
const isNightShift = currentHour >= 18 || currentHour < 6;
|
|
@@ -48346,6 +49454,12 @@ var WorkspaceHealthView = ({
|
|
|
48346
49454
|
},
|
|
48347
49455
|
[router, onNavigate]
|
|
48348
49456
|
);
|
|
49457
|
+
const handleViewDetails = useCallback((workspace) => {
|
|
49458
|
+
setSelectedWorkspace(workspace);
|
|
49459
|
+
}, []);
|
|
49460
|
+
const handleCloseDetails = useCallback(() => {
|
|
49461
|
+
setSelectedWorkspace(null);
|
|
49462
|
+
}, []);
|
|
48349
49463
|
const handleExport = useCallback(() => {
|
|
48350
49464
|
const csv = [
|
|
48351
49465
|
["Workspace", "Line", "Company", "Status", "Last Heartbeat", "Consecutive Misses"],
|
|
@@ -48403,178 +49517,189 @@ var WorkspaceHealthView = ({
|
|
|
48403
49517
|
)
|
|
48404
49518
|
] }) }) }) });
|
|
48405
49519
|
}
|
|
48406
|
-
return /* @__PURE__ */ jsxs(
|
|
48407
|
-
/* @__PURE__ */ jsxs("
|
|
48408
|
-
/* @__PURE__ */ jsxs("
|
|
48409
|
-
/* @__PURE__ */ jsxs("div", { className: "
|
|
48410
|
-
/* @__PURE__ */
|
|
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(
|
|
48411
49567
|
BackButtonMinimal,
|
|
48412
49568
|
{
|
|
48413
49569
|
onClick: () => router.push("/"),
|
|
48414
49570
|
text: "Back",
|
|
48415
|
-
size: "
|
|
49571
|
+
size: "default",
|
|
48416
49572
|
"aria-label": "Navigate back to dashboard"
|
|
48417
49573
|
}
|
|
48418
|
-
),
|
|
48419
|
-
/* @__PURE__ */ jsxs("div", { className: "flex gap-
|
|
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: [
|
|
48420
49583
|
/* @__PURE__ */ jsx(
|
|
48421
49584
|
"button",
|
|
48422
49585
|
{
|
|
48423
49586
|
onClick: () => {
|
|
48424
49587
|
refetch();
|
|
48425
49588
|
},
|
|
48426
|
-
className: "p-
|
|
49589
|
+
className: "p-2 text-gray-600 hover:bg-gray-100 rounded-lg transition-colors",
|
|
48427
49590
|
"aria-label": "Refresh",
|
|
48428
|
-
children: /* @__PURE__ */ jsx(RefreshCw, { className: "h-
|
|
49591
|
+
children: /* @__PURE__ */ jsx(RefreshCw, { className: "h-5 w-5" })
|
|
48429
49592
|
}
|
|
48430
49593
|
),
|
|
48431
49594
|
/* @__PURE__ */ jsx(
|
|
48432
49595
|
"button",
|
|
48433
49596
|
{
|
|
48434
49597
|
onClick: handleExport,
|
|
48435
|
-
className: "p-
|
|
49598
|
+
className: "p-2 text-gray-600 hover:bg-gray-100 rounded-lg transition-colors",
|
|
48436
49599
|
"aria-label": "Export CSV",
|
|
48437
|
-
children: /* @__PURE__ */ jsx(Download, { className: "h-
|
|
49600
|
+
children: /* @__PURE__ */ jsx(Download, { className: "h-5 w-5" })
|
|
48438
49601
|
}
|
|
48439
49602
|
)
|
|
48440
|
-
] })
|
|
48441
|
-
|
|
48442
|
-
|
|
48443
|
-
|
|
48444
|
-
/* @__PURE__ */
|
|
48445
|
-
|
|
48446
|
-
|
|
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
|
+
] })
|
|
48447
49617
|
] })
|
|
48448
49618
|
] }) })
|
|
48449
49619
|
] }),
|
|
48450
|
-
/* @__PURE__ */
|
|
48451
|
-
|
|
48452
|
-
|
|
49620
|
+
/* @__PURE__ */ jsxs("div", { className: "max-w-7xl mx-auto p-4 space-y-6", children: [
|
|
49621
|
+
summary && /* @__PURE__ */ jsxs(
|
|
49622
|
+
motion.div,
|
|
48453
49623
|
{
|
|
48454
|
-
|
|
48455
|
-
|
|
48456
|
-
|
|
48457
|
-
"
|
|
48458
|
-
|
|
48459
|
-
|
|
48460
|
-
|
|
48461
|
-
|
|
48462
|
-
|
|
48463
|
-
|
|
48464
|
-
|
|
48465
|
-
|
|
48466
|
-
|
|
48467
|
-
|
|
48468
|
-
/* @__PURE__ */ jsx(
|
|
48469
|
-
"button",
|
|
48470
|
-
{
|
|
48471
|
-
onClick: () => {
|
|
48472
|
-
refetch();
|
|
48473
|
-
},
|
|
48474
|
-
className: "p-2 text-gray-600 hover:bg-gray-100 rounded-lg transition-colors",
|
|
48475
|
-
"aria-label": "Refresh",
|
|
48476
|
-
children: /* @__PURE__ */ jsx(RefreshCw, { className: "h-5 w-5" })
|
|
48477
|
-
}
|
|
48478
|
-
),
|
|
48479
|
-
/* @__PURE__ */ jsx(
|
|
48480
|
-
"button",
|
|
48481
|
-
{
|
|
48482
|
-
onClick: handleExport,
|
|
48483
|
-
className: "p-2 text-gray-600 hover:bg-gray-100 rounded-lg transition-colors",
|
|
48484
|
-
"aria-label": "Export CSV",
|
|
48485
|
-
children: /* @__PURE__ */ jsx(Download, { className: "h-5 w-5" })
|
|
48486
|
-
}
|
|
48487
|
-
)
|
|
48488
|
-
] }),
|
|
48489
|
-
/* @__PURE__ */ jsx("div", { className: "w-full h-8" })
|
|
48490
|
-
] }) }),
|
|
48491
|
-
/* @__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: [
|
|
48492
|
-
/* @__PURE__ */ jsx("div", { className: "text-sm sm:text-base md:text-lg font-medium text-blue-600", children: /* @__PURE__ */ jsx(LiveTimer, {}) }),
|
|
48493
|
-
/* @__PURE__ */ jsx("div", { className: "hidden sm:block w-px h-4 bg-blue-300" }),
|
|
48494
|
-
/* @__PURE__ */ jsx("span", { className: "text-xs sm:text-sm md:text-base font-medium text-blue-600", children: formatDate(operationalDate) }),
|
|
48495
|
-
/* @__PURE__ */ jsx("div", { className: "hidden sm:block w-px h-4 bg-blue-300" }),
|
|
48496
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 sm:gap-2", children: [
|
|
48497
|
-
/* @__PURE__ */ jsx("div", { className: "text-blue-600", children: getShiftIcon(shiftType) }),
|
|
48498
|
-
/* @__PURE__ */ jsxs("span", { className: "text-xs sm:text-sm md:text-base font-medium text-blue-600", children: [
|
|
48499
|
-
shiftType,
|
|
48500
|
-
" Shift"
|
|
48501
|
-
] })
|
|
48502
|
-
] })
|
|
48503
|
-
] }) })
|
|
48504
|
-
] }),
|
|
48505
|
-
/* @__PURE__ */ jsxs("div", { className: "max-w-7xl mx-auto p-4 space-y-6", children: [
|
|
48506
|
-
summary && /* @__PURE__ */ jsxs(
|
|
48507
|
-
motion.div,
|
|
48508
|
-
{
|
|
48509
|
-
initial: { opacity: 0, y: 20 },
|
|
48510
|
-
animate: { opacity: 1, y: 0 },
|
|
48511
|
-
transition: { duration: 0.3, delay: 0.1 },
|
|
48512
|
-
className: "grid grid-cols-2 sm:grid-cols-2 md:grid-cols-5 gap-2 sm:gap-3 lg:gap-4",
|
|
48513
|
-
children: [
|
|
48514
|
-
/* @__PURE__ */ jsxs(Card2, { className: "col-span-2 sm:col-span-2 md:col-span-2 bg-white", children: [
|
|
48515
|
-
/* @__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" }) }),
|
|
48516
|
-
/* @__PURE__ */ jsxs(CardContent2, { children: [
|
|
48517
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-2", children: [
|
|
48518
|
-
/* @__PURE__ */ jsxs("span", { className: clsx("text-3xl font-bold", getUptimeColor(summary.uptimePercentage)), children: [
|
|
48519
|
-
summary.uptimePercentage.toFixed(1),
|
|
48520
|
-
"%"
|
|
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" })
|
|
48521
49638
|
] }),
|
|
48522
|
-
|
|
48523
|
-
] })
|
|
48524
|
-
|
|
48525
|
-
|
|
48526
|
-
|
|
48527
|
-
|
|
48528
|
-
|
|
48529
|
-
|
|
48530
|
-
|
|
48531
|
-
|
|
48532
|
-
|
|
48533
|
-
|
|
48534
|
-
|
|
48535
|
-
|
|
48536
|
-
|
|
48537
|
-
|
|
48538
|
-
|
|
48539
|
-
|
|
48540
|
-
|
|
48541
|
-
|
|
48542
|
-
|
|
48543
|
-
|
|
48544
|
-
|
|
48545
|
-
|
|
48546
|
-
|
|
48547
|
-
|
|
48548
|
-
|
|
48549
|
-
|
|
48550
|
-
|
|
48551
|
-
|
|
48552
|
-
|
|
48553
|
-
|
|
48554
|
-
/* @__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
|
+
] })
|
|
48555
49671
|
] })
|
|
48556
|
-
]
|
|
48557
|
-
|
|
48558
|
-
|
|
48559
|
-
|
|
48560
|
-
|
|
48561
|
-
|
|
48562
|
-
|
|
48563
|
-
|
|
48564
|
-
|
|
48565
|
-
|
|
48566
|
-
|
|
48567
|
-
|
|
48568
|
-
|
|
48569
|
-
|
|
48570
|
-
|
|
48571
|
-
|
|
48572
|
-
|
|
48573
|
-
|
|
48574
|
-
|
|
48575
|
-
|
|
48576
|
-
|
|
48577
|
-
|
|
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
|
+
)
|
|
48578
49703
|
] });
|
|
48579
49704
|
};
|
|
48580
49705
|
var WorkspaceHealthView_default = withAuth(WorkspaceHealthView, {
|
|
@@ -52143,4 +53268,4 @@ function shuffleArray(array) {
|
|
|
52143
53268
|
return shuffled;
|
|
52144
53269
|
}
|
|
52145
53270
|
|
|
52146
|
-
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 };
|