@optifye/dashboard-core 6.10.5 → 6.10.6
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 +10 -0
- package/dist/index.d.mts +13 -6
- package/dist/index.d.ts +13 -6
- package/dist/index.js +370 -128
- package/dist/index.mjs +370 -128
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -28998,6 +28998,33 @@ var VideoControls = ({
|
|
|
28998
28998
|
}
|
|
28999
28999
|
);
|
|
29000
29000
|
};
|
|
29001
|
+
|
|
29002
|
+
// src/lib/utils/r2Detection.ts
|
|
29003
|
+
function isR2WorkerUrl(url, r2WorkerDomain) {
|
|
29004
|
+
if (!url || !r2WorkerDomain) return false;
|
|
29005
|
+
try {
|
|
29006
|
+
const workerDomain = new URL(r2WorkerDomain).hostname;
|
|
29007
|
+
return url.includes(workerDomain);
|
|
29008
|
+
} catch {
|
|
29009
|
+
return url.includes(r2WorkerDomain.replace(/https?:\/\//, ""));
|
|
29010
|
+
}
|
|
29011
|
+
}
|
|
29012
|
+
|
|
29013
|
+
// src/lib/services/hlsAuthService.ts
|
|
29014
|
+
async function getAuthTokenForHls(supabase) {
|
|
29015
|
+
try {
|
|
29016
|
+
const { data: { session } } = await supabase.auth.getSession();
|
|
29017
|
+
if (!session?.access_token) {
|
|
29018
|
+
console.warn("[HLS Auth] No active session, R2 streaming may fail");
|
|
29019
|
+
return null;
|
|
29020
|
+
}
|
|
29021
|
+
console.log("[HLS Auth] Retrieved token for HLS.js requests");
|
|
29022
|
+
return session.access_token;
|
|
29023
|
+
} catch (error) {
|
|
29024
|
+
console.error("[HLS Auth] Error getting auth token:", error);
|
|
29025
|
+
return null;
|
|
29026
|
+
}
|
|
29027
|
+
}
|
|
29001
29028
|
var ERROR_MAPPING = {
|
|
29002
29029
|
"networkError": {
|
|
29003
29030
|
code: 2,
|
|
@@ -29120,6 +29147,7 @@ var HlsVideoPlayer = React24.forwardRef(({
|
|
|
29120
29147
|
onSeeked,
|
|
29121
29148
|
onClick
|
|
29122
29149
|
}, ref) => {
|
|
29150
|
+
const supabase = useSupabase();
|
|
29123
29151
|
const videoContainerRef = React24.useRef(null);
|
|
29124
29152
|
const videoRef = React24.useRef(null);
|
|
29125
29153
|
const hlsRef = React24.useRef(null);
|
|
@@ -29247,14 +29275,49 @@ var HlsVideoPlayer = React24.forwardRef(({
|
|
|
29247
29275
|
dispose: () => dispose()
|
|
29248
29276
|
};
|
|
29249
29277
|
}, [dispose]);
|
|
29250
|
-
const initializePlayer = React24.useCallback(() => {
|
|
29278
|
+
const initializePlayer = React24.useCallback(async () => {
|
|
29251
29279
|
if (!videoRef.current || !src) return;
|
|
29252
29280
|
const video = videoRef.current;
|
|
29253
29281
|
const player = playerLikeObject();
|
|
29282
|
+
let authToken = null;
|
|
29283
|
+
try {
|
|
29284
|
+
authToken = await getAuthTokenForHls(supabase);
|
|
29285
|
+
if (authToken) {
|
|
29286
|
+
console.log("[HLS Auth] Retrieved token for R2 streaming");
|
|
29287
|
+
} else {
|
|
29288
|
+
console.warn("[HLS Auth] No active session - R2 streaming may fail");
|
|
29289
|
+
}
|
|
29290
|
+
} catch (error) {
|
|
29291
|
+
console.error("[HLS Auth] Error retrieving token:", error);
|
|
29292
|
+
}
|
|
29293
|
+
const r2WorkerDomain = "https://r2-stream-proxy.optifye-r2.workers.dev";
|
|
29254
29294
|
const mergedHlsConfig = {
|
|
29255
29295
|
...BASE_HLS_CONFIG,
|
|
29256
29296
|
...stableHlsConfigRef.current || {},
|
|
29257
|
-
...stableOptionsRef.current || {}
|
|
29297
|
+
...stableOptionsRef.current || {},
|
|
29298
|
+
// Modern HLS.js uses Fetch API - override fetch to add Authorization header
|
|
29299
|
+
fetchSetup: function(context, initParams) {
|
|
29300
|
+
const url = context.url;
|
|
29301
|
+
console.log("[HLS fetchSetup] Request URL:", url);
|
|
29302
|
+
console.log("[HLS fetchSetup] R2 Worker domain:", r2WorkerDomain);
|
|
29303
|
+
console.log("[HLS fetchSetup] Token available:", !!authToken);
|
|
29304
|
+
const isR2 = isR2WorkerUrl(url, r2WorkerDomain);
|
|
29305
|
+
console.log("[HLS fetchSetup] Is R2 Worker URL:", isR2);
|
|
29306
|
+
if (isR2) {
|
|
29307
|
+
if (authToken) {
|
|
29308
|
+
initParams.headers = {
|
|
29309
|
+
...initParams.headers,
|
|
29310
|
+
"Authorization": `Bearer ${authToken}`
|
|
29311
|
+
};
|
|
29312
|
+
console.log("[HLS Auth] \u2705 Injected JWT for R2 request:", url);
|
|
29313
|
+
} else {
|
|
29314
|
+
console.warn("[HLS Auth] \u26A0\uFE0F No token available for R2 request:", url);
|
|
29315
|
+
}
|
|
29316
|
+
} else {
|
|
29317
|
+
console.log("[HLS Auth] CloudFront URL - no auth needed:", url);
|
|
29318
|
+
}
|
|
29319
|
+
return new Request(url, initParams);
|
|
29320
|
+
}
|
|
29258
29321
|
};
|
|
29259
29322
|
cleanupBlobUrl();
|
|
29260
29323
|
const isHLS = src.endsWith(".m3u8") || src.startsWith("#EXTM3U");
|
|
@@ -29293,9 +29356,20 @@ var HlsVideoPlayer = React24.forwardRef(({
|
|
|
29293
29356
|
let errorInfo;
|
|
29294
29357
|
switch (data.type) {
|
|
29295
29358
|
case Hls3.ErrorTypes.NETWORK_ERROR:
|
|
29296
|
-
|
|
29297
|
-
|
|
29298
|
-
|
|
29359
|
+
if (data.response?.code === 401) {
|
|
29360
|
+
errorInfo = {
|
|
29361
|
+
code: 401,
|
|
29362
|
+
type: "recoverable",
|
|
29363
|
+
message: "Authentication expired. Please refresh the page.",
|
|
29364
|
+
canRetry: true,
|
|
29365
|
+
details: "JWT_EXPIRED"
|
|
29366
|
+
};
|
|
29367
|
+
console.error("[HLS Auth] 401 Unauthorized - token may be expired");
|
|
29368
|
+
} else {
|
|
29369
|
+
errorInfo = ERROR_MAPPING.networkError;
|
|
29370
|
+
console.log("[HlsVideoPlayer] Attempting to recover from network error");
|
|
29371
|
+
hls.startLoad();
|
|
29372
|
+
}
|
|
29299
29373
|
break;
|
|
29300
29374
|
case Hls3.ErrorTypes.MEDIA_ERROR:
|
|
29301
29375
|
errorInfo = ERROR_MAPPING.mediaError;
|
|
@@ -29340,8 +29414,19 @@ var HlsVideoPlayer = React24.forwardRef(({
|
|
|
29340
29414
|
let errorInfo;
|
|
29341
29415
|
switch (data.type) {
|
|
29342
29416
|
case Hls3.ErrorTypes.NETWORK_ERROR:
|
|
29343
|
-
|
|
29344
|
-
|
|
29417
|
+
if (data.response?.code === 401) {
|
|
29418
|
+
errorInfo = {
|
|
29419
|
+
code: 401,
|
|
29420
|
+
type: "recoverable",
|
|
29421
|
+
message: "Authentication expired. Please refresh the page.",
|
|
29422
|
+
canRetry: true,
|
|
29423
|
+
details: "JWT_EXPIRED"
|
|
29424
|
+
};
|
|
29425
|
+
console.error("[HLS Auth] 401 Unauthorized - token may be expired");
|
|
29426
|
+
} else {
|
|
29427
|
+
errorInfo = ERROR_MAPPING.networkError;
|
|
29428
|
+
hls.startLoad();
|
|
29429
|
+
}
|
|
29345
29430
|
break;
|
|
29346
29431
|
case Hls3.ErrorTypes.MEDIA_ERROR:
|
|
29347
29432
|
errorInfo = ERROR_MAPPING.mediaError;
|
|
@@ -29492,7 +29577,10 @@ var HlsVideoPlayer = React24.forwardRef(({
|
|
|
29492
29577
|
configVersion
|
|
29493
29578
|
]);
|
|
29494
29579
|
React24.useEffect(() => {
|
|
29495
|
-
|
|
29580
|
+
let cleanup;
|
|
29581
|
+
initializePlayer().then((cleanupFn) => {
|
|
29582
|
+
cleanup = cleanupFn;
|
|
29583
|
+
});
|
|
29496
29584
|
return () => {
|
|
29497
29585
|
cleanup?.();
|
|
29498
29586
|
if (hlsRef.current) {
|
|
@@ -31699,6 +31787,30 @@ var FileManagerFilters = ({
|
|
|
31699
31787
|
if (node.type === "category" || node.type === "percentile-category") {
|
|
31700
31788
|
toggleExpanded(node.id);
|
|
31701
31789
|
onFilterChange(node.id);
|
|
31790
|
+
if (node.id === "fast-cycles") {
|
|
31791
|
+
trackCoreEvent("Fast Clips Clicked", {
|
|
31792
|
+
workspaceId,
|
|
31793
|
+
date,
|
|
31794
|
+
shift,
|
|
31795
|
+
count: node.count || 0,
|
|
31796
|
+
percentile: filterState.percentile
|
|
31797
|
+
});
|
|
31798
|
+
} else if (node.id === "slow-cycles") {
|
|
31799
|
+
trackCoreEvent("Slow Clips Clicked", {
|
|
31800
|
+
workspaceId,
|
|
31801
|
+
date,
|
|
31802
|
+
shift,
|
|
31803
|
+
count: node.count || 0,
|
|
31804
|
+
percentile: filterState.percentile
|
|
31805
|
+
});
|
|
31806
|
+
} else if (node.id === "cycle_completion") {
|
|
31807
|
+
trackCoreEvent("Cycle Completions Clicked", {
|
|
31808
|
+
workspaceId,
|
|
31809
|
+
date,
|
|
31810
|
+
shift,
|
|
31811
|
+
count: node.count || 0
|
|
31812
|
+
});
|
|
31813
|
+
}
|
|
31702
31814
|
if (node.id !== "idle_time" && idleLabelFilter) {
|
|
31703
31815
|
setIdleLabelFilter(null);
|
|
31704
31816
|
}
|
|
@@ -37213,11 +37325,6 @@ var LinePdfGenerator = ({
|
|
|
37213
37325
|
doc.setLineWidth(0.8);
|
|
37214
37326
|
doc.line(20, 118, 190, 118);
|
|
37215
37327
|
const hourlyOverviewStartY = 123;
|
|
37216
|
-
doc.setFontSize(18);
|
|
37217
|
-
doc.setFont("helvetica", "bold");
|
|
37218
|
-
doc.setTextColor(40, 40, 40);
|
|
37219
|
-
doc.text("Hourly Output Overview", 20, 133);
|
|
37220
|
-
doc.setTextColor(0, 0, 0);
|
|
37221
37328
|
const getHourlyTimeRanges = (startTimeStr, endTimeStr) => {
|
|
37222
37329
|
const [hours, minutes] = startTimeStr.split(":");
|
|
37223
37330
|
const startHour = parseInt(hours);
|
|
@@ -37408,23 +37515,43 @@ var LinePdfGenerator = ({
|
|
|
37408
37515
|
return Math.round(lineInfo.metrics.current_output / shiftDuration);
|
|
37409
37516
|
});
|
|
37410
37517
|
}
|
|
37411
|
-
const tableHeaderY =
|
|
37412
|
-
const tableStartY =
|
|
37518
|
+
const tableHeaderY = 146;
|
|
37519
|
+
const tableStartY = 153;
|
|
37413
37520
|
const rowSpacing = 8;
|
|
37414
37521
|
const bottomPadding = 8;
|
|
37415
37522
|
const hourlyTableHeight = hourlyTimeRanges.length * rowSpacing;
|
|
37416
37523
|
const backgroundHeight = tableStartY - hourlyOverviewStartY + hourlyTableHeight + bottomPadding;
|
|
37417
37524
|
doc.setFillColor(245, 245, 245);
|
|
37418
37525
|
doc.roundedRect(15, hourlyOverviewStartY, 180, backgroundHeight, 3, 3, "F");
|
|
37526
|
+
doc.setFontSize(18);
|
|
37527
|
+
doc.setFont("helvetica", "bold");
|
|
37528
|
+
doc.setTextColor(40, 40, 40);
|
|
37529
|
+
doc.text("Hourly Performance", 20, 133);
|
|
37530
|
+
doc.setTextColor(0, 0, 0);
|
|
37531
|
+
const gridTopY = 139;
|
|
37532
|
+
const headerBottomY = 148;
|
|
37533
|
+
const colBoundaries = [20, 70, 100, 130, 155, 190];
|
|
37534
|
+
const totalRows = hourlyTimeRanges.length;
|
|
37535
|
+
const gridBottomY = headerBottomY + totalRows * rowSpacing;
|
|
37536
|
+
const tableHeight = gridBottomY - gridTopY;
|
|
37537
|
+
doc.setFillColor(255, 255, 255);
|
|
37538
|
+
doc.roundedRect(20, gridTopY, 170, tableHeight, 2, 2, "F");
|
|
37539
|
+
doc.setDrawColor(230, 230, 230);
|
|
37540
|
+
doc.setLineWidth(0.2);
|
|
37541
|
+
doc.roundedRect(20, gridTopY, 170, tableHeight, 2, 2, "S");
|
|
37542
|
+
doc.setDrawColor(200, 200, 200);
|
|
37543
|
+
doc.setLineWidth(0.3);
|
|
37544
|
+
doc.line(20, headerBottomY, 190, headerBottomY);
|
|
37545
|
+
colBoundaries.slice(1, -1).forEach((x) => {
|
|
37546
|
+
doc.line(x, gridTopY, x, gridBottomY);
|
|
37547
|
+
});
|
|
37419
37548
|
doc.setFontSize(11);
|
|
37420
37549
|
doc.setFont("helvetica", "bold");
|
|
37421
37550
|
doc.text("Time Range", 25, tableHeaderY);
|
|
37422
|
-
doc.text("Output",
|
|
37423
|
-
doc.text("Target",
|
|
37424
|
-
doc.text("Status",
|
|
37425
|
-
doc.
|
|
37426
|
-
doc.setDrawColor(200, 200, 200);
|
|
37427
|
-
doc.line(20, 146, 190, 146);
|
|
37551
|
+
doc.text("Output", 75, tableHeaderY);
|
|
37552
|
+
doc.text("Target", 105, tableHeaderY);
|
|
37553
|
+
doc.text("Status", 135, tableHeaderY);
|
|
37554
|
+
doc.text("Remarks", 160, tableHeaderY);
|
|
37428
37555
|
doc.setFont("helvetica", "normal");
|
|
37429
37556
|
let yPos = tableStartY;
|
|
37430
37557
|
const lineDateForTable = new Date(lineInfo.date);
|
|
@@ -37446,20 +37573,25 @@ var LinePdfGenerator = ({
|
|
|
37446
37573
|
const dataCollected = !isTodayForTable || hourNumber < currentHour;
|
|
37447
37574
|
const outputStr = dataCollected ? actualOutput.toString() : "TBD";
|
|
37448
37575
|
const targetStr = targetOutputPerHour.toString();
|
|
37576
|
+
if (index < totalRows - 1) {
|
|
37577
|
+
const rowBottomY = headerBottomY + (index + 1) * rowSpacing;
|
|
37578
|
+
doc.setDrawColor(200, 200, 200);
|
|
37579
|
+
doc.line(20, rowBottomY, 190, rowBottomY);
|
|
37580
|
+
}
|
|
37449
37581
|
doc.text(timeRange, 25, yPos);
|
|
37450
|
-
doc.text(outputStr,
|
|
37451
|
-
doc.text(targetStr,
|
|
37582
|
+
doc.text(outputStr, 75, yPos);
|
|
37583
|
+
doc.text(targetStr, 105, yPos);
|
|
37452
37584
|
if (!dataCollected) {
|
|
37453
37585
|
doc.setTextColor(100, 100, 100);
|
|
37454
|
-
doc.text("-",
|
|
37586
|
+
doc.text("-", 135, yPos);
|
|
37455
37587
|
} else if (actualOutput >= targetOutputPerHour) {
|
|
37456
37588
|
doc.setTextColor(0, 171, 69);
|
|
37457
37589
|
doc.setFont("ZapfDingbats", "normal");
|
|
37458
|
-
doc.text("4",
|
|
37590
|
+
doc.text("4", 135, yPos);
|
|
37459
37591
|
doc.setFont("helvetica", "normal");
|
|
37460
37592
|
} else {
|
|
37461
37593
|
doc.setTextColor(227, 67, 41);
|
|
37462
|
-
doc.text("\xD7",
|
|
37594
|
+
doc.text("\xD7", 135, yPos);
|
|
37463
37595
|
}
|
|
37464
37596
|
doc.setTextColor(0, 0, 0);
|
|
37465
37597
|
yPos += rowSpacing;
|
|
@@ -37467,7 +37599,7 @@ var LinePdfGenerator = ({
|
|
|
37467
37599
|
doc.addPage();
|
|
37468
37600
|
yPos = addHeaderPage2();
|
|
37469
37601
|
const workspaceSectionStartY = yPos;
|
|
37470
|
-
const sortedWorkspaces = [...workspaceData].sort((a, b) => (a.efficiency || 0) - (b.efficiency || 0)).slice(0,
|
|
37602
|
+
const sortedWorkspaces = [...workspaceData].sort((a, b) => (a.efficiency || 0) - (b.efficiency || 0)).slice(0, 3);
|
|
37471
37603
|
const wsRowCount = sortedWorkspaces.length > 0 ? sortedWorkspaces.length : 1;
|
|
37472
37604
|
const wsTableHeight = 10 + 8 + 7 + wsRowCount * 8 + 8;
|
|
37473
37605
|
doc.setFillColor(245, 245, 245);
|
|
@@ -37475,28 +37607,45 @@ var LinePdfGenerator = ({
|
|
|
37475
37607
|
doc.setFontSize(18);
|
|
37476
37608
|
doc.setFont("helvetica", "bold");
|
|
37477
37609
|
doc.setTextColor(40, 40, 40);
|
|
37478
|
-
doc.text("Poorest Performing Workspaces", 20, yPos);
|
|
37610
|
+
doc.text("Poorest Performing Workspaces", 20, yPos + 10);
|
|
37479
37611
|
doc.setTextColor(0, 0, 0);
|
|
37480
|
-
yPos +=
|
|
37612
|
+
yPos += 20;
|
|
37613
|
+
const wsGridTopY = yPos - 5;
|
|
37614
|
+
const wsHeaderBottomY = wsGridTopY + 8;
|
|
37615
|
+
const wsColBoundaries = [20, 80, 140, 190];
|
|
37616
|
+
const wsGridBottomY = wsHeaderBottomY + wsRowCount * 8;
|
|
37617
|
+
const wsTableTotalHeight = wsGridBottomY - wsGridTopY;
|
|
37618
|
+
doc.setFillColor(255, 255, 255);
|
|
37619
|
+
doc.roundedRect(20, wsGridTopY, 170, wsTableTotalHeight, 2, 2, "F");
|
|
37620
|
+
doc.setDrawColor(230, 230, 230);
|
|
37621
|
+
doc.setLineWidth(0.2);
|
|
37622
|
+
doc.roundedRect(20, wsGridTopY, 170, wsTableTotalHeight, 2, 2, "S");
|
|
37623
|
+
doc.setDrawColor(200, 200, 200);
|
|
37624
|
+
doc.setLineWidth(0.3);
|
|
37625
|
+
doc.line(20, wsHeaderBottomY, 190, wsHeaderBottomY);
|
|
37626
|
+
wsColBoundaries.slice(1, -1).forEach((x) => {
|
|
37627
|
+
doc.line(x, wsGridTopY, x, wsGridBottomY);
|
|
37628
|
+
});
|
|
37481
37629
|
doc.setFontSize(11);
|
|
37482
37630
|
doc.setFont("helvetica", "bold");
|
|
37483
|
-
yPos
|
|
37631
|
+
yPos = wsGridTopY + 5.5;
|
|
37484
37632
|
doc.text("Workspace", 25, yPos);
|
|
37485
37633
|
doc.text("Current/Target", 85, yPos);
|
|
37486
37634
|
doc.text("Efficiency", 145, yPos);
|
|
37487
|
-
yPos += 3;
|
|
37488
|
-
doc.setLineWidth(0.3);
|
|
37489
|
-
doc.setDrawColor(200, 200, 200);
|
|
37490
|
-
doc.line(20, yPos, 190, yPos);
|
|
37491
37635
|
doc.setFont("helvetica", "normal");
|
|
37492
|
-
yPos
|
|
37636
|
+
yPos = wsHeaderBottomY + 5.5;
|
|
37493
37637
|
if (sortedWorkspaces.length === 0) {
|
|
37494
37638
|
doc.text("No workspace data available", 25, yPos);
|
|
37495
|
-
yPos +=
|
|
37639
|
+
yPos += 8;
|
|
37496
37640
|
} else {
|
|
37497
37641
|
sortedWorkspaces.forEach((ws, index) => {
|
|
37498
37642
|
const workspaceName = getWorkspaceDisplayName(ws.workspace_name, lineInfo.line_id);
|
|
37499
37643
|
const truncatedName = workspaceName.length > 25 ? workspaceName.substring(0, 22) + "..." : workspaceName;
|
|
37644
|
+
if (index < wsRowCount - 1) {
|
|
37645
|
+
const rowBottomY = wsHeaderBottomY + (index + 1) * 8;
|
|
37646
|
+
doc.setDrawColor(200, 200, 200);
|
|
37647
|
+
doc.line(20, rowBottomY, 190, rowBottomY);
|
|
37648
|
+
}
|
|
37500
37649
|
doc.text(truncatedName, 25, yPos);
|
|
37501
37650
|
doc.text(`${ws.action_count || 0} / ${ws.action_threshold || 0}`, 85, yPos);
|
|
37502
37651
|
doc.text(`${(ws.efficiency || 0).toFixed(1)}%`, 145, yPos);
|
|
@@ -38839,18 +38988,27 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons }) => {
|
|
|
38839
38988
|
const hourlyTitleY = hasIdleTimeReason ? 198 : 188;
|
|
38840
38989
|
doc.text("Hourly Performance", 20, hourlyTitleY);
|
|
38841
38990
|
doc.setTextColor(0, 0, 0);
|
|
38842
|
-
doc.setFontSize(headerFontSize);
|
|
38843
|
-
doc.setFont("helvetica", "bold");
|
|
38844
38991
|
const baseHeaderY = hasIdleTimeReason ? 208 : 198;
|
|
38845
38992
|
const headerY = titleFontSize === 16 ? hasIdleTimeReason ? 206 : 196 : baseHeaderY;
|
|
38846
|
-
|
|
38847
|
-
|
|
38848
|
-
|
|
38849
|
-
|
|
38850
|
-
|
|
38993
|
+
const gridTopY = headerY - 5;
|
|
38994
|
+
const headerBottomY = gridTopY + 8;
|
|
38995
|
+
const colBoundaries = [20, 70, 100, 130, 155, 190];
|
|
38996
|
+
const totalRows = hourlyData.length;
|
|
38997
|
+
const gridBottomY = headerBottomY + totalRows * rowHeight;
|
|
38851
38998
|
doc.setDrawColor(200, 200, 200);
|
|
38852
|
-
|
|
38853
|
-
doc.line(20,
|
|
38999
|
+
doc.setLineWidth(0.3);
|
|
39000
|
+
doc.line(20, gridTopY, 190, gridTopY);
|
|
39001
|
+
doc.line(20, headerBottomY, 190, headerBottomY);
|
|
39002
|
+
colBoundaries.forEach((x) => {
|
|
39003
|
+
doc.line(x, gridTopY, x, gridBottomY);
|
|
39004
|
+
});
|
|
39005
|
+
doc.setFontSize(headerFontSize);
|
|
39006
|
+
doc.setFont("helvetica", "bold");
|
|
39007
|
+
doc.text("Time Range", 25, headerY);
|
|
39008
|
+
doc.text("Output", 75, headerY);
|
|
39009
|
+
doc.text("Target", 105, headerY);
|
|
39010
|
+
doc.text("Status", 135, headerY);
|
|
39011
|
+
doc.text("Remarks", 160, headerY);
|
|
38854
39012
|
doc.setFontSize(contentFontSize);
|
|
38855
39013
|
doc.setFont("helvetica", "normal");
|
|
38856
39014
|
let yPos = tableStartY;
|
|
@@ -38881,20 +39039,23 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons }) => {
|
|
|
38881
39039
|
const dataCollected = !isToday2 || hourNumber < currentHour;
|
|
38882
39040
|
const outputStr = dataCollected ? output.toString() : "TBD";
|
|
38883
39041
|
const targetStr = hourlyTarget.toString();
|
|
39042
|
+
const rowBottomY = headerBottomY + (index + 1) * rowHeight;
|
|
39043
|
+
doc.setDrawColor(200, 200, 200);
|
|
39044
|
+
doc.line(20, rowBottomY, 190, rowBottomY);
|
|
38884
39045
|
doc.text(timeRange, 25, yPos);
|
|
38885
|
-
doc.text(outputStr,
|
|
38886
|
-
doc.text(targetStr,
|
|
39046
|
+
doc.text(outputStr, 75, yPos);
|
|
39047
|
+
doc.text(targetStr, 105, yPos);
|
|
38887
39048
|
if (!dataCollected) {
|
|
38888
39049
|
doc.setTextColor(100, 100, 100);
|
|
38889
|
-
doc.text("-",
|
|
39050
|
+
doc.text("-", 135, yPos);
|
|
38890
39051
|
} else if (output >= hourlyTarget) {
|
|
38891
39052
|
doc.setTextColor(0, 171, 69);
|
|
38892
39053
|
doc.setFont("ZapfDingbats", "normal");
|
|
38893
|
-
doc.text("4",
|
|
39054
|
+
doc.text("4", 135, yPos);
|
|
38894
39055
|
doc.setFont("helvetica", "normal");
|
|
38895
39056
|
} else {
|
|
38896
39057
|
doc.setTextColor(227, 67, 41);
|
|
38897
|
-
doc.text("\xD7",
|
|
39058
|
+
doc.text("\xD7", 135, yPos);
|
|
38898
39059
|
}
|
|
38899
39060
|
doc.setTextColor(0, 0, 0);
|
|
38900
39061
|
yPos += rowHeight;
|
|
@@ -39700,14 +39861,16 @@ var KPICard = ({
|
|
|
39700
39861
|
"uppercase tracking-wide sm:tracking-wider"
|
|
39701
39862
|
), children: title }),
|
|
39702
39863
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline gap-0.5 sm:gap-2 flex-wrap", children: [
|
|
39703
|
-
/* @__PURE__ */ jsxRuntime.jsx("
|
|
39704
|
-
"
|
|
39705
|
-
|
|
39706
|
-
|
|
39707
|
-
|
|
39708
|
-
"
|
|
39709
|
-
|
|
39710
|
-
|
|
39864
|
+
isLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-5 sm:h-6 md:h-7 w-12 sm:w-16 md:w-20 bg-gray-200 dark:bg-gray-700 rounded animate-pulse" }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
39865
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: clsx(
|
|
39866
|
+
"font-bold text-gray-900 dark:text-gray-50",
|
|
39867
|
+
"text-base sm:text-xl md:text-2xl"
|
|
39868
|
+
), children: formattedValue }),
|
|
39869
|
+
suffix && /* @__PURE__ */ jsxRuntime.jsx("span", { className: clsx(
|
|
39870
|
+
"font-medium text-gray-600 dark:text-gray-300",
|
|
39871
|
+
style.compact ? "text-base" : "text-lg"
|
|
39872
|
+
), children: suffix })
|
|
39873
|
+
] }),
|
|
39711
39874
|
!isLoading && trendInfo.shouldShowTrend && trendInfo.trendValue !== "neutral" && title !== "Output" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: trendInfo.colorClass, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
39712
39875
|
trendInfo.Icon,
|
|
39713
39876
|
{
|
|
@@ -39866,6 +40029,7 @@ var KPIHeader = ({
|
|
|
39866
40029
|
};
|
|
39867
40030
|
var KPISection = React24.memo(({
|
|
39868
40031
|
kpis,
|
|
40032
|
+
isLoading = false,
|
|
39869
40033
|
className,
|
|
39870
40034
|
layout: layout2 = "row",
|
|
39871
40035
|
gridColumns,
|
|
@@ -39874,7 +40038,8 @@ var KPISection = React24.memo(({
|
|
|
39874
40038
|
compactCards = false,
|
|
39875
40039
|
useSrcLayout = false
|
|
39876
40040
|
}) => {
|
|
39877
|
-
const
|
|
40041
|
+
const showSkeleton = isLoading || !kpis;
|
|
40042
|
+
const outputDifference = kpis ? kpis.outputProgress.current - kpis.outputProgress.idealOutput : 0;
|
|
39878
40043
|
const outputIsOnTarget = outputDifference >= 0;
|
|
39879
40044
|
if (useSrcLayout) {
|
|
39880
40045
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -39891,27 +40056,30 @@ var KPISection = React24.memo(({
|
|
|
39891
40056
|
KPICard,
|
|
39892
40057
|
{
|
|
39893
40058
|
title: "Underperforming",
|
|
39894
|
-
value: "2/3",
|
|
39895
|
-
change: 0
|
|
40059
|
+
value: showSkeleton ? "" : "2/3",
|
|
40060
|
+
change: 0,
|
|
40061
|
+
isLoading: showSkeleton
|
|
39896
40062
|
}
|
|
39897
40063
|
) }),
|
|
39898
40064
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
39899
40065
|
KPICard,
|
|
39900
40066
|
{
|
|
39901
40067
|
title: "Efficiency",
|
|
39902
|
-
value: kpis.efficiency.value,
|
|
39903
|
-
change: kpis.efficiency.change,
|
|
39904
|
-
suffix: "%"
|
|
40068
|
+
value: showSkeleton ? "" : kpis.efficiency.value,
|
|
40069
|
+
change: showSkeleton ? 0 : kpis.efficiency.change,
|
|
40070
|
+
suffix: "%",
|
|
40071
|
+
isLoading: showSkeleton
|
|
39905
40072
|
}
|
|
39906
40073
|
) }),
|
|
39907
40074
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
39908
40075
|
KPICard,
|
|
39909
40076
|
{
|
|
39910
40077
|
title: "Output Progress",
|
|
39911
|
-
value: `${kpis.outputProgress.current}/${kpis.outputProgress.target}`,
|
|
39912
|
-
change: kpis.outputProgress.change,
|
|
40078
|
+
value: showSkeleton ? "" : `${kpis.outputProgress.current}/${kpis.outputProgress.target}`,
|
|
40079
|
+
change: showSkeleton ? 0 : kpis.outputProgress.change,
|
|
39913
40080
|
outputDifference,
|
|
39914
|
-
showOutputDetails:
|
|
40081
|
+
showOutputDetails: !showSkeleton,
|
|
40082
|
+
isLoading: showSkeleton
|
|
39915
40083
|
}
|
|
39916
40084
|
) })
|
|
39917
40085
|
]
|
|
@@ -39922,34 +40090,30 @@ var KPISection = React24.memo(({
|
|
|
39922
40090
|
{
|
|
39923
40091
|
key: "underperforming",
|
|
39924
40092
|
title: "Underperforming",
|
|
39925
|
-
value: `${kpis.underperformingWorkers.current}/${kpis.underperformingWorkers.total}`,
|
|
39926
|
-
change: kpis.underperformingWorkers.change
|
|
40093
|
+
value: showSkeleton ? "" : `${kpis.underperformingWorkers.current}/${kpis.underperformingWorkers.total}`,
|
|
40094
|
+
change: showSkeleton ? 0 : kpis.underperformingWorkers.change,
|
|
40095
|
+
suffix: void 0,
|
|
40096
|
+
status: void 0
|
|
39927
40097
|
},
|
|
39928
40098
|
{
|
|
39929
40099
|
key: "efficiency",
|
|
39930
40100
|
title: "Efficiency",
|
|
39931
|
-
value: kpis.efficiency.value,
|
|
39932
|
-
change: kpis.efficiency.change,
|
|
39933
|
-
suffix: "%"
|
|
40101
|
+
value: showSkeleton ? "" : kpis.efficiency.value,
|
|
40102
|
+
change: showSkeleton ? 0 : kpis.efficiency.change,
|
|
40103
|
+
suffix: "%",
|
|
40104
|
+
status: void 0
|
|
39934
40105
|
},
|
|
39935
40106
|
{
|
|
39936
40107
|
key: "outputProgress",
|
|
39937
40108
|
title: "Output Progress",
|
|
39938
|
-
value: `${kpis.outputProgress.current}/${kpis.outputProgress.target}`,
|
|
39939
|
-
change: kpis.outputProgress.change,
|
|
39940
|
-
|
|
40109
|
+
value: showSkeleton ? "" : `${kpis.outputProgress.current}/${kpis.outputProgress.target}`,
|
|
40110
|
+
change: showSkeleton ? 0 : kpis.outputProgress.change,
|
|
40111
|
+
suffix: void 0,
|
|
40112
|
+
status: showSkeleton ? void 0 : {
|
|
39941
40113
|
tooltipText: outputIsOnTarget ? "On Target" : "Off Target",
|
|
39942
40114
|
positive: outputIsOnTarget
|
|
39943
40115
|
}
|
|
39944
40116
|
}
|
|
39945
|
-
// Only include these additional KPIs if not using the src layout
|
|
39946
|
-
// ...(kpis.qualityCompliance ? [{
|
|
39947
|
-
// key: 'qualityCompliance',
|
|
39948
|
-
// title: 'Quality Compliance',
|
|
39949
|
-
// value: kpis.qualityCompliance.value,
|
|
39950
|
-
// change: kpis.qualityCompliance.change,
|
|
39951
|
-
// suffix: '%',
|
|
39952
|
-
// }] : []),
|
|
39953
40117
|
];
|
|
39954
40118
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
39955
40119
|
KPIGrid,
|
|
@@ -39966,7 +40130,8 @@ var KPISection = React24.memo(({
|
|
|
39966
40130
|
change: kpi.change,
|
|
39967
40131
|
suffix: kpi.suffix,
|
|
39968
40132
|
status: kpi.status,
|
|
39969
|
-
style: { variant: cardVariant, compact: compactCards }
|
|
40133
|
+
style: { variant: cardVariant, compact: compactCards },
|
|
40134
|
+
isLoading: showSkeleton
|
|
39970
40135
|
},
|
|
39971
40136
|
kpi.key
|
|
39972
40137
|
))
|
|
@@ -39975,6 +40140,9 @@ var KPISection = React24.memo(({
|
|
|
39975
40140
|
}, (prevProps, nextProps) => {
|
|
39976
40141
|
const prevKpis = prevProps.kpis;
|
|
39977
40142
|
const nextKpis = nextProps.kpis;
|
|
40143
|
+
if (prevProps.isLoading !== nextProps.isLoading) return false;
|
|
40144
|
+
if (!prevKpis && !nextKpis) return true;
|
|
40145
|
+
if (!prevKpis || !nextKpis) return false;
|
|
39978
40146
|
if (prevKpis === nextKpis) return true;
|
|
39979
40147
|
if (Math.abs(prevKpis.efficiency.value - nextKpis.efficiency.value) >= 0.5) return false;
|
|
39980
40148
|
if (prevKpis.underperformingWorkers.current !== nextKpis.underperformingWorkers.current || prevKpis.underperformingWorkers.total !== nextKpis.underperformingWorkers.total) return false;
|
|
@@ -45727,7 +45895,8 @@ var TeamUsagePdfGenerator = ({
|
|
|
45727
45895
|
usageData,
|
|
45728
45896
|
daysInRange = 1,
|
|
45729
45897
|
title = "Team Usage Report",
|
|
45730
|
-
className
|
|
45898
|
+
className,
|
|
45899
|
+
iconOnly = false
|
|
45731
45900
|
}) => {
|
|
45732
45901
|
const [isGenerating, setIsGenerating] = React24.useState(false);
|
|
45733
45902
|
const generatePDF = async () => {
|
|
@@ -45907,10 +46076,11 @@ var TeamUsagePdfGenerator = ({
|
|
|
45907
46076
|
{
|
|
45908
46077
|
onClick: generatePDF,
|
|
45909
46078
|
disabled: isGenerating || !usageData,
|
|
45910
|
-
className: `inline-flex items-center gap-2 rounded-lg bg-gray-100 px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-200 disabled:opacity-50 disabled:cursor-not-allowed ${className || ""}`,
|
|
46079
|
+
className: `inline-flex items-center gap-2 rounded-lg bg-gray-100 ${iconOnly ? "p-2" : "px-4 py-2"} text-sm font-medium text-gray-700 hover:bg-gray-200 active:bg-gray-300 transition-colors disabled:opacity-50 disabled:cursor-not-allowed ${className || ""}`,
|
|
46080
|
+
"aria-label": iconOnly ? isGenerating ? "Generating PDF..." : "Export PDF" : void 0,
|
|
45911
46081
|
children: [
|
|
45912
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Download, { className: "h-4 w-4" }),
|
|
45913
|
-
isGenerating ? "Generating..." : "Export PDF"
|
|
46082
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Download, { className: iconOnly ? "h-5 w-5" : "h-4 w-4" }),
|
|
46083
|
+
!iconOnly && (isGenerating ? "Generating..." : "Export PDF")
|
|
45914
46084
|
]
|
|
45915
46085
|
}
|
|
45916
46086
|
);
|
|
@@ -48679,10 +48849,19 @@ function HomeView({
|
|
|
48679
48849
|
const [errorMessage, setErrorMessage] = React24.useState(null);
|
|
48680
48850
|
const [displayNamesInitialized, setDisplayNamesInitialized] = React24.useState(false);
|
|
48681
48851
|
const [hasInitialDataLoaded, setHasInitialDataLoaded] = React24.useState(false);
|
|
48852
|
+
const KPI_CACHE_KEY = "optifye_kpi_cache";
|
|
48682
48853
|
const dashboardConfig = useDashboardConfig();
|
|
48683
48854
|
const entityConfig = useEntityConfig();
|
|
48684
48855
|
const supabaseClient = useSupabaseClient();
|
|
48685
48856
|
const { user } = useAuth();
|
|
48857
|
+
const { lines: dbLines } = useLines();
|
|
48858
|
+
const mergedLineNames = React24.useMemo(() => {
|
|
48859
|
+
const merged = { ...lineNames };
|
|
48860
|
+
dbLines.forEach((line) => {
|
|
48861
|
+
merged[line.id] = line.line_name;
|
|
48862
|
+
});
|
|
48863
|
+
return merged;
|
|
48864
|
+
}, [lineNames, dbLines]);
|
|
48686
48865
|
const isSupervisor = user?.role_level === "supervisor";
|
|
48687
48866
|
const hasMultipleLines = allLineIds.length > 1;
|
|
48688
48867
|
const availableLineIds = React24.useMemo(() => {
|
|
@@ -48789,6 +48968,42 @@ function HomeView({
|
|
|
48789
48968
|
}
|
|
48790
48969
|
return buildKPIsFromLineMetricsRow(row);
|
|
48791
48970
|
}, [selectedLineId, factoryViewId, lineMetrics, metricsLoading]);
|
|
48971
|
+
const [cachedKpis, setCachedKpis] = React24.useState(() => {
|
|
48972
|
+
if (typeof window === "undefined") return null;
|
|
48973
|
+
try {
|
|
48974
|
+
const cached = localStorage.getItem(KPI_CACHE_KEY);
|
|
48975
|
+
if (cached) {
|
|
48976
|
+
const parsed = JSON.parse(cached);
|
|
48977
|
+
return parsed[selectedLineId] || parsed[factoryViewId] || null;
|
|
48978
|
+
}
|
|
48979
|
+
} catch (e) {
|
|
48980
|
+
}
|
|
48981
|
+
return null;
|
|
48982
|
+
});
|
|
48983
|
+
React24.useEffect(() => {
|
|
48984
|
+
if (kpis && typeof window !== "undefined") {
|
|
48985
|
+
try {
|
|
48986
|
+
const existing = localStorage.getItem(KPI_CACHE_KEY);
|
|
48987
|
+
const cache = existing ? JSON.parse(existing) : {};
|
|
48988
|
+
cache[selectedLineId] = kpis;
|
|
48989
|
+
localStorage.setItem(KPI_CACHE_KEY, JSON.stringify(cache));
|
|
48990
|
+
setCachedKpis(kpis);
|
|
48991
|
+
} catch (e) {
|
|
48992
|
+
}
|
|
48993
|
+
}
|
|
48994
|
+
}, [kpis, selectedLineId, KPI_CACHE_KEY]);
|
|
48995
|
+
React24.useEffect(() => {
|
|
48996
|
+
if (typeof window === "undefined") return;
|
|
48997
|
+
try {
|
|
48998
|
+
const cached = localStorage.getItem(KPI_CACHE_KEY);
|
|
48999
|
+
if (cached) {
|
|
49000
|
+
const parsed = JSON.parse(cached);
|
|
49001
|
+
setCachedKpis(parsed[selectedLineId] || null);
|
|
49002
|
+
}
|
|
49003
|
+
} catch (e) {
|
|
49004
|
+
}
|
|
49005
|
+
}, [selectedLineId, KPI_CACHE_KEY]);
|
|
49006
|
+
const displayKpis = kpis || cachedKpis;
|
|
48792
49007
|
const {
|
|
48793
49008
|
activeBreaks: allActiveBreaks,
|
|
48794
49009
|
isLoading: breaksLoading,
|
|
@@ -49090,16 +49305,16 @@ function HomeView({
|
|
|
49090
49305
|
// Use stable string representation instead of spreading array
|
|
49091
49306
|
JSON.stringify(workspaceMetrics.map((w) => `${w.workspace_uuid}-${Math.round(w.efficiency)}-${w.trend}`))
|
|
49092
49307
|
]);
|
|
49093
|
-
const memoizedKPIs = React24.useMemo(() =>
|
|
49308
|
+
const memoizedKPIs = React24.useMemo(() => displayKpis, [
|
|
49094
49309
|
// Only update reference when values change by at least 1%
|
|
49095
|
-
|
|
49096
|
-
|
|
49097
|
-
|
|
49098
|
-
|
|
49099
|
-
|
|
49100
|
-
|
|
49310
|
+
displayKpis?.efficiency?.value ? Math.round(displayKpis.efficiency.value) : null,
|
|
49311
|
+
displayKpis?.underperformingWorkers?.current,
|
|
49312
|
+
displayKpis?.underperformingWorkers?.total,
|
|
49313
|
+
displayKpis?.outputProgress?.current,
|
|
49314
|
+
displayKpis?.outputProgress?.target,
|
|
49315
|
+
displayKpis?.avgCycleTime?.value ? Math.round(displayKpis.avgCycleTime.value * 10) / 10 : null,
|
|
49101
49316
|
// Round to 1 decimal
|
|
49102
|
-
|
|
49317
|
+
displayKpis?.qualityCompliance?.value ? Math.round(displayKpis.qualityCompliance.value) : null
|
|
49103
49318
|
]);
|
|
49104
49319
|
React24.useEffect(() => {
|
|
49105
49320
|
setIsHydrated(true);
|
|
@@ -49117,7 +49332,7 @@ function HomeView({
|
|
|
49117
49332
|
trackCoreEvent("Home Line Filter Changed", {
|
|
49118
49333
|
previous_line_id: selectedLineId,
|
|
49119
49334
|
new_line_id: value,
|
|
49120
|
-
line_name:
|
|
49335
|
+
line_name: mergedLineNames[value] || (value === factoryViewId ? "All Lines" : `Line ${value.substring(0, 4)}`)
|
|
49121
49336
|
});
|
|
49122
49337
|
try {
|
|
49123
49338
|
sessionStorage.setItem(LINE_FILTER_STORAGE_KEY, value);
|
|
@@ -49151,9 +49366,9 @@ function HomeView({
|
|
|
49151
49366
|
}
|
|
49152
49367
|
return /* @__PURE__ */ jsxRuntime.jsxs(Select, { onValueChange: handleLineChange, defaultValue: selectedLineId, children: [
|
|
49153
49368
|
/* @__PURE__ */ jsxRuntime.jsx(SelectTrigger, { className: "w-full sm:w-[200px] bg-white border border-gray-200 shadow-sm rounded-md h-9 sm:h-9 text-xs sm:text-sm px-2 sm:px-3", children: /* @__PURE__ */ jsxRuntime.jsx(SelectValue, { placeholder: "Select a line" }) }),
|
|
49154
|
-
/* @__PURE__ */ jsxRuntime.jsx(SelectContent, { className: "z-50 bg-white shadow-lg border border-gray-200 rounded-md text-xs sm:text-sm", children: availableLineIds.map((id3) => /* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: id3, children:
|
|
49369
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectContent, { className: "z-50 bg-white shadow-lg border border-gray-200 rounded-md text-xs sm:text-sm", children: availableLineIds.map((id3) => /* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: id3, children: mergedLineNames[id3] || (id3 === factoryViewId ? "All Lines" : `Line ${id3.substring(0, 4)}`) }, id3)) })
|
|
49155
49370
|
] });
|
|
49156
|
-
}, [availableLineIds, handleLineChange, selectedLineId,
|
|
49371
|
+
}, [availableLineIds, handleLineChange, selectedLineId, mergedLineNames, factoryViewId, allLineIds.length]);
|
|
49157
49372
|
const isInitialLoading = !isHydrated || displayNamesLoading || !displayNamesInitialized;
|
|
49158
49373
|
const isDataLoading = metricsLoading;
|
|
49159
49374
|
if (isInitialLoading) {
|
|
@@ -49182,7 +49397,7 @@ function HomeView({
|
|
|
49182
49397
|
lineTitle,
|
|
49183
49398
|
lineId: selectedLineId === factoryViewId ? allLineIds[0] : selectedLineId,
|
|
49184
49399
|
className: "w-full",
|
|
49185
|
-
headerControls:
|
|
49400
|
+
headerControls: /* @__PURE__ */ jsxRuntime.jsx(KPISection2, { kpis: memoizedKPIs, isLoading: !memoizedKPIs, className: "w-full sm:w-auto" })
|
|
49186
49401
|
}
|
|
49187
49402
|
) }) }),
|
|
49188
49403
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 overflow-y-auto sm:overflow-hidden relative", children: [
|
|
@@ -56471,35 +56686,62 @@ var TeamManagementView = ({
|
|
|
56471
56686
|
) }) });
|
|
56472
56687
|
}
|
|
56473
56688
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("min-h-screen bg-slate-50", className), children: [
|
|
56474
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "sticky top-0 z-10 bg-white border-b border-gray-200 shadow-sm", children: /* @__PURE__ */ jsxRuntime.
|
|
56475
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "
|
|
56476
|
-
|
|
56477
|
-
/* @__PURE__ */ jsxRuntime.jsx("
|
|
56478
|
-
/* @__PURE__ */ jsxRuntime.
|
|
56479
|
-
|
|
56480
|
-
|
|
56481
|
-
|
|
56482
|
-
|
|
56483
|
-
|
|
56484
|
-
|
|
56485
|
-
|
|
56486
|
-
|
|
56487
|
-
|
|
56488
|
-
|
|
56489
|
-
|
|
56490
|
-
|
|
56491
|
-
|
|
56492
|
-
|
|
56493
|
-
|
|
56494
|
-
|
|
56495
|
-
|
|
56496
|
-
|
|
56497
|
-
|
|
56498
|
-
|
|
56499
|
-
|
|
56500
|
-
|
|
56689
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "sticky top-0 z-10 bg-white border-b border-gray-200 shadow-sm", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-3 sm:px-6 lg:px-8 py-3 sm:py-4", children: [
|
|
56690
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "sm:hidden", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
56691
|
+
/* @__PURE__ */ jsxRuntime.jsx(BackButtonMinimal, { onClick: handleBack, text: "Back" }),
|
|
56692
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 flex justify-center", children: /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-base font-semibold text-gray-900 truncate px-2", children: pageTitle }) }),
|
|
56693
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
56694
|
+
canViewUsageStats && /* @__PURE__ */ jsxRuntime.jsx(
|
|
56695
|
+
TeamUsagePdfGenerator,
|
|
56696
|
+
{
|
|
56697
|
+
users,
|
|
56698
|
+
usageData,
|
|
56699
|
+
daysInRange: usageDateRange.daysElapsed,
|
|
56700
|
+
title: "Team Usage Report",
|
|
56701
|
+
iconOnly: true
|
|
56702
|
+
}
|
|
56703
|
+
),
|
|
56704
|
+
canAddUsers && /* @__PURE__ */ jsxRuntime.jsx(
|
|
56705
|
+
"button",
|
|
56706
|
+
{
|
|
56707
|
+
onClick: () => setIsAddUserDialogOpen(true),
|
|
56708
|
+
className: "p-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 active:bg-blue-800 transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500",
|
|
56709
|
+
"aria-label": "Add User",
|
|
56710
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.UserPlus, { className: "w-5 h-5" })
|
|
56711
|
+
}
|
|
56712
|
+
)
|
|
56713
|
+
] })
|
|
56714
|
+
] }) }),
|
|
56715
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "hidden sm:flex items-center justify-between", children: [
|
|
56716
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-4", children: /* @__PURE__ */ jsxRuntime.jsx(BackButtonMinimal, { onClick: handleBack, text: "Back" }) }),
|
|
56717
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 flex flex-col items-center", children: [
|
|
56718
|
+
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl lg:text-3xl font-semibold text-gray-900 text-center", children: pageTitle }),
|
|
56719
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500 mt-1 text-center px-4", children: pageDescription })
|
|
56720
|
+
] }),
|
|
56721
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
56722
|
+
canViewUsageStats && /* @__PURE__ */ jsxRuntime.jsx(
|
|
56723
|
+
TeamUsagePdfGenerator,
|
|
56724
|
+
{
|
|
56725
|
+
users,
|
|
56726
|
+
usageData,
|
|
56727
|
+
daysInRange: usageDateRange.daysElapsed,
|
|
56728
|
+
title: "Team Usage Report"
|
|
56729
|
+
}
|
|
56730
|
+
),
|
|
56731
|
+
canAddUsers && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
56732
|
+
"button",
|
|
56733
|
+
{
|
|
56734
|
+
onClick: () => setIsAddUserDialogOpen(true),
|
|
56735
|
+
className: "inline-flex items-center gap-2 px-4 py-2 bg-blue-600 text-white text-sm font-medium rounded-lg hover:bg-blue-700 shadow-sm transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500",
|
|
56736
|
+
children: [
|
|
56737
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.UserPlus, { className: "w-4 h-4" }),
|
|
56738
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Add User" })
|
|
56739
|
+
]
|
|
56740
|
+
}
|
|
56741
|
+
)
|
|
56742
|
+
] })
|
|
56501
56743
|
] })
|
|
56502
|
-
] }) })
|
|
56744
|
+
] }) }),
|
|
56503
56745
|
/* @__PURE__ */ jsxRuntime.jsx("main", { className: "px-4 sm:px-6 lg:px-8 py-6", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
56504
56746
|
UserManagementTable,
|
|
56505
56747
|
{
|