@optifye/dashboard-core 6.6.6 → 6.6.8
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 +0 -3
- package/dist/index.d.mts +207 -279
- package/dist/index.d.ts +207 -279
- package/dist/index.js +1943 -2339
- package/dist/index.mjs +1024 -1426
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import
|
|
1
|
+
import * as React21 from 'react';
|
|
2
|
+
import React21__default, { createContext, useRef, useCallback, useState, useMemo, useEffect, memo, forwardRef, useImperativeHandle, useContext, useLayoutEffect, 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
5
|
import { toZonedTime, formatInTimeZone } from 'date-fns-tz';
|
|
6
6
|
import { subDays, format, parseISO, isValid, formatDistanceToNow, isFuture, isToday } from 'date-fns';
|
|
7
7
|
import mixpanel from 'mixpanel-browser';
|
|
8
8
|
import { EventEmitter } from 'events';
|
|
9
|
-
import {
|
|
9
|
+
import { createClient, REALTIME_SUBSCRIBE_STATES } from '@supabase/supabase-js';
|
|
10
10
|
import Hls2 from 'hls.js';
|
|
11
11
|
import useSWR from 'swr';
|
|
12
12
|
import { noop, warning, invariant, progress, secondsToMilliseconds, millisecondsToSeconds, memo as memo$1 } from 'motion-utils';
|
|
@@ -153,8 +153,9 @@ var DEFAULT_WORKSPACE_CONFIG = {
|
|
|
153
153
|
totalWorkspaces: 42
|
|
154
154
|
};
|
|
155
155
|
var DEFAULT_DATE_TIME_CONFIG = {
|
|
156
|
-
defaultTimezone: "
|
|
157
|
-
|
|
156
|
+
defaultTimezone: "UTC",
|
|
157
|
+
// Should be overridden by database timezone
|
|
158
|
+
defaultLocale: "en-US",
|
|
158
159
|
dateFormatOptions: {
|
|
159
160
|
day: "2-digit",
|
|
160
161
|
month: "short",
|
|
@@ -268,14 +269,14 @@ var _getDashboardConfigInstance = () => {
|
|
|
268
269
|
}
|
|
269
270
|
return dashboardConfigInstance;
|
|
270
271
|
};
|
|
271
|
-
var DashboardConfigContext =
|
|
272
|
+
var DashboardConfigContext = React21.createContext(void 0);
|
|
272
273
|
var DashboardProvider = ({ config: userProvidedConfig, children }) => {
|
|
273
|
-
const fullConfig =
|
|
274
|
+
const fullConfig = React21.useMemo(() => mergeWithDefaultConfig(userProvidedConfig), [userProvidedConfig]);
|
|
274
275
|
_setDashboardConfigInstance(fullConfig);
|
|
275
|
-
|
|
276
|
+
React21.useEffect(() => {
|
|
276
277
|
_setDashboardConfigInstance(fullConfig);
|
|
277
278
|
}, [fullConfig]);
|
|
278
|
-
|
|
279
|
+
React21.useEffect(() => {
|
|
279
280
|
if (!fullConfig.theme) return;
|
|
280
281
|
const styleId = "dashboard-core-theme-vars";
|
|
281
282
|
let styleEl = document.getElementById(styleId);
|
|
@@ -301,7 +302,7 @@ var DashboardProvider = ({ config: userProvidedConfig, children }) => {
|
|
|
301
302
|
return /* @__PURE__ */ jsx(DashboardConfigContext.Provider, { value: fullConfig, children });
|
|
302
303
|
};
|
|
303
304
|
var useDashboardConfig = () => {
|
|
304
|
-
const ctx =
|
|
305
|
+
const ctx = React21.useContext(DashboardConfigContext);
|
|
305
306
|
if (!ctx) throw new Error("useDashboardConfig must be used within a DashboardProvider");
|
|
306
307
|
return ctx;
|
|
307
308
|
};
|
|
@@ -426,7 +427,7 @@ var memoizedOutputArrayAggregation = createMemoizedFunction(
|
|
|
426
427
|
},
|
|
427
428
|
(arrays) => arrays.map((arr) => arr.length).join("-")
|
|
428
429
|
);
|
|
429
|
-
var getOperationalDate = (timezone
|
|
430
|
+
var getOperationalDate = (timezone, date = /* @__PURE__ */ new Date(), shiftStartTime = "06:00") => {
|
|
430
431
|
const zonedDate = toZonedTime(date, timezone);
|
|
431
432
|
const hours = zonedDate.getHours();
|
|
432
433
|
const [startHour = 6] = shiftStartTime.split(":").map(Number);
|
|
@@ -1888,7 +1889,7 @@ var workspaceService = {
|
|
|
1888
1889
|
}
|
|
1889
1890
|
const totalDayOutput = outputWorkspaces.reduce((sum, ws) => sum + (ws.action_total_day_output || 0), 0);
|
|
1890
1891
|
const totalPPH = outputWorkspaces.reduce((sum, ws) => sum + (ws.action_pph_threshold || 0), 0);
|
|
1891
|
-
const operationalDate = getOperationalDate(defaultTimezone);
|
|
1892
|
+
const operationalDate = getOperationalDate(defaultTimezone || "UTC");
|
|
1892
1893
|
const thresholdData = {
|
|
1893
1894
|
factory_id: configuredFactoryId,
|
|
1894
1895
|
line_id: lineId,
|
|
@@ -3568,329 +3569,10 @@ var useAudioService = () => {
|
|
|
3568
3569
|
};
|
|
3569
3570
|
|
|
3570
3571
|
// src/lib/utils/dateShiftUtils.ts
|
|
3571
|
-
function isValidDateFormat(date) {
|
|
3572
|
-
return /^\d{4}-\d{2}-\d{2}$/.test(date);
|
|
3573
|
-
}
|
|
3574
3572
|
function isValidShiftId(shiftId) {
|
|
3575
3573
|
const id3 = typeof shiftId === "string" ? parseInt(shiftId, 10) : shiftId;
|
|
3576
3574
|
return id3 === 0 || id3 === 1;
|
|
3577
3575
|
}
|
|
3578
|
-
|
|
3579
|
-
// src/lib/api/s3-clips-parser.ts
|
|
3580
|
-
function parseS3Uri(s3Uri, sopCategories) {
|
|
3581
|
-
const path = new URL(s3Uri).pathname;
|
|
3582
|
-
const parts = path.split("/").filter((p) => p);
|
|
3583
|
-
if (s3Uri.includes("missed_qchecks")) {
|
|
3584
|
-
console.warn(`Skipping missed_qchecks URI in parseS3Uri: ${s3Uri}`);
|
|
3585
|
-
return null;
|
|
3586
|
-
}
|
|
3587
|
-
if (parts.length < 8) {
|
|
3588
|
-
console.warn(`Invalid S3 path structure: ${s3Uri} - Too few parts: ${parts.length}, expected at least 8`);
|
|
3589
|
-
return null;
|
|
3590
|
-
}
|
|
3591
|
-
try {
|
|
3592
|
-
const datePart = parts[2];
|
|
3593
|
-
const shiftPart = parts[3];
|
|
3594
|
-
const violationType = parts[4];
|
|
3595
|
-
let folderName = "";
|
|
3596
|
-
let timestamp = "";
|
|
3597
|
-
for (let i = 5; i < parts.length; i++) {
|
|
3598
|
-
const part = parts[i];
|
|
3599
|
-
if (part && part.includes("_") && /\d{8}_\d{6}/.test(part)) {
|
|
3600
|
-
folderName = part;
|
|
3601
|
-
const timeMatch = folderName.match(/_(\d{8})_(\d{6})_/);
|
|
3602
|
-
if (timeMatch) {
|
|
3603
|
-
timestamp = `${timeMatch[2].substring(0, 2)}:${timeMatch[2].substring(2, 4)}:${timeMatch[2].substring(4, 6)}`;
|
|
3604
|
-
break;
|
|
3605
|
-
}
|
|
3606
|
-
}
|
|
3607
|
-
}
|
|
3608
|
-
if (!timestamp) {
|
|
3609
|
-
console.warn(`Couldn't extract timestamp from any part: ${parts.join("/")}`);
|
|
3610
|
-
timestamp = "00:00:00";
|
|
3611
|
-
}
|
|
3612
|
-
let severity = "low";
|
|
3613
|
-
let type = "bottleneck";
|
|
3614
|
-
let description = "Analysis Clip";
|
|
3615
|
-
const normalizedViolationType = violationType.toLowerCase().trim();
|
|
3616
|
-
if (sopCategories && sopCategories.length > 0) {
|
|
3617
|
-
const matchedCategory = sopCategories.find((category) => {
|
|
3618
|
-
const categoryId = category.id.toLowerCase();
|
|
3619
|
-
const s3FolderName = (category.s3FolderName || category.id).toLowerCase();
|
|
3620
|
-
return categoryId === normalizedViolationType || s3FolderName === normalizedViolationType || // Also check for partial matches for flexibility
|
|
3621
|
-
normalizedViolationType.includes(categoryId) || normalizedViolationType.includes(s3FolderName);
|
|
3622
|
-
});
|
|
3623
|
-
if (matchedCategory) {
|
|
3624
|
-
type = matchedCategory.id;
|
|
3625
|
-
description = matchedCategory.description || matchedCategory.label;
|
|
3626
|
-
if (matchedCategory.color.includes("red")) {
|
|
3627
|
-
severity = "high";
|
|
3628
|
-
} else if (matchedCategory.color.includes("yellow") || matchedCategory.color.includes("orange")) {
|
|
3629
|
-
severity = "medium";
|
|
3630
|
-
} else {
|
|
3631
|
-
severity = "low";
|
|
3632
|
-
}
|
|
3633
|
-
console.log(`Matched SOP category: ${matchedCategory.id} for violation type: ${violationType}`);
|
|
3634
|
-
return { timestamp, severity, description, type, originalUri: s3Uri };
|
|
3635
|
-
}
|
|
3636
|
-
}
|
|
3637
|
-
switch (normalizedViolationType) {
|
|
3638
|
-
case "idle_time":
|
|
3639
|
-
case "idle":
|
|
3640
|
-
case "low_value":
|
|
3641
|
-
case "low value":
|
|
3642
|
-
case "low_value_moment":
|
|
3643
|
-
case "low_value_moments":
|
|
3644
|
-
case "low value moment":
|
|
3645
|
-
case "low value moments":
|
|
3646
|
-
type = "low_value";
|
|
3647
|
-
severity = "low";
|
|
3648
|
-
description = "Idle Time Detected";
|
|
3649
|
-
break;
|
|
3650
|
-
case "sop_deviation":
|
|
3651
|
-
type = "missing_quality_check";
|
|
3652
|
-
severity = "high";
|
|
3653
|
-
description = "SOP Deviations";
|
|
3654
|
-
break;
|
|
3655
|
-
case "long_cycle_time":
|
|
3656
|
-
severity = "high";
|
|
3657
|
-
type = "long_cycle_time";
|
|
3658
|
-
description = "Long Cycle Time Detected";
|
|
3659
|
-
break;
|
|
3660
|
-
case "best_cycle_time":
|
|
3661
|
-
type = "best_cycle_time";
|
|
3662
|
-
severity = "low";
|
|
3663
|
-
description = "Best Cycle Time Performance";
|
|
3664
|
-
break;
|
|
3665
|
-
case "worst_cycle_time":
|
|
3666
|
-
type = "worst_cycle_time";
|
|
3667
|
-
severity = "high";
|
|
3668
|
-
description = "Worst Cycle Time Performance";
|
|
3669
|
-
break;
|
|
3670
|
-
case "cycle_completion":
|
|
3671
|
-
case "completed_cycles":
|
|
3672
|
-
case "completed_cycle":
|
|
3673
|
-
type = "cycle_completion";
|
|
3674
|
-
severity = "low";
|
|
3675
|
-
description = "Cycle Completion";
|
|
3676
|
-
break;
|
|
3677
|
-
case "running_cycle":
|
|
3678
|
-
case "active_cycle":
|
|
3679
|
-
case "production_cycle":
|
|
3680
|
-
type = "running_cycle";
|
|
3681
|
-
severity = "low";
|
|
3682
|
-
description = "Active Production Cycle";
|
|
3683
|
-
break;
|
|
3684
|
-
case "setup_state":
|
|
3685
|
-
case "machine_setup":
|
|
3686
|
-
case "line_setup":
|
|
3687
|
-
type = "setup_state";
|
|
3688
|
-
severity = "medium";
|
|
3689
|
-
description = "Machine Setup Activity";
|
|
3690
|
-
break;
|
|
3691
|
-
case "medium_bottleneck":
|
|
3692
|
-
severity = "medium";
|
|
3693
|
-
description = "Medium Bottleneck Identified";
|
|
3694
|
-
break;
|
|
3695
|
-
case "minor_bottleneck":
|
|
3696
|
-
case "mild_bottleneck":
|
|
3697
|
-
severity = "low";
|
|
3698
|
-
description = "Minor Bottleneck Identified";
|
|
3699
|
-
break;
|
|
3700
|
-
default:
|
|
3701
|
-
if (normalizedViolationType.includes("sop") && normalizedViolationType.includes("deviation")) {
|
|
3702
|
-
type = "missing_quality_check";
|
|
3703
|
-
severity = "high";
|
|
3704
|
-
description = "SOP Deviations";
|
|
3705
|
-
} else if (normalizedViolationType.includes("worst") && normalizedViolationType.includes("cycle")) {
|
|
3706
|
-
type = "worst_cycle_time";
|
|
3707
|
-
severity = "high";
|
|
3708
|
-
description = "Worst Cycle Time Performance";
|
|
3709
|
-
} else if (normalizedViolationType.includes("best") && normalizedViolationType.includes("cycle")) {
|
|
3710
|
-
type = "best_cycle_time";
|
|
3711
|
-
severity = "low";
|
|
3712
|
-
description = "Best Cycle Time Performance";
|
|
3713
|
-
} else if (normalizedViolationType.includes("long") && normalizedViolationType.includes("cycle")) {
|
|
3714
|
-
type = "long_cycle_time";
|
|
3715
|
-
severity = "high";
|
|
3716
|
-
description = "Long Cycle Time Detected";
|
|
3717
|
-
} else if (normalizedViolationType.includes("cycle") && (normalizedViolationType.includes("completion") || normalizedViolationType.includes("complete"))) {
|
|
3718
|
-
type = "cycle_completion";
|
|
3719
|
-
severity = "low";
|
|
3720
|
-
description = "Cycle Completion";
|
|
3721
|
-
} else if (normalizedViolationType.includes("running") && normalizedViolationType.includes("cycle")) {
|
|
3722
|
-
type = "running_cycle";
|
|
3723
|
-
severity = "low";
|
|
3724
|
-
description = "Active Production Cycle";
|
|
3725
|
-
} else if (normalizedViolationType.includes("setup") || normalizedViolationType.includes("machine") && normalizedViolationType.includes("setup")) {
|
|
3726
|
-
type = "setup_state";
|
|
3727
|
-
severity = "medium";
|
|
3728
|
-
description = "Machine Setup Activity";
|
|
3729
|
-
} else {
|
|
3730
|
-
description = `Clip type: ${violationType.replace(/_/g, " ")}`;
|
|
3731
|
-
console.log(`Detected unknown violation type: ${violationType} in URI: ${s3Uri}`);
|
|
3732
|
-
}
|
|
3733
|
-
break;
|
|
3734
|
-
}
|
|
3735
|
-
return { timestamp, severity, description, type, originalUri: s3Uri };
|
|
3736
|
-
} catch (error) {
|
|
3737
|
-
console.error(`Error parsing S3 URI: ${s3Uri}`, error);
|
|
3738
|
-
return null;
|
|
3739
|
-
}
|
|
3740
|
-
}
|
|
3741
|
-
function shuffleArray(array) {
|
|
3742
|
-
const shuffled = [...array];
|
|
3743
|
-
for (let i = shuffled.length - 1; i > 0; i--) {
|
|
3744
|
-
const j = Math.floor(Math.random() * (i + 1));
|
|
3745
|
-
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
|
|
3746
|
-
}
|
|
3747
|
-
return shuffled;
|
|
3748
|
-
}
|
|
3749
|
-
var SmartVideoCache = class extends EventEmitter {
|
|
3750
|
-
constructor() {
|
|
3751
|
-
super();
|
|
3752
|
-
this.metrics = {
|
|
3753
|
-
hits: 0,
|
|
3754
|
-
misses: 0,
|
|
3755
|
-
evictions: 0,
|
|
3756
|
-
totalSize: 0,
|
|
3757
|
-
entryCount: 0,
|
|
3758
|
-
hitRate: 0
|
|
3759
|
-
};
|
|
3760
|
-
this.setMaxListeners(50);
|
|
3761
|
-
}
|
|
3762
|
-
/**
|
|
3763
|
-
* DISABLED - Always returns null
|
|
3764
|
-
*/
|
|
3765
|
-
async getSummary(key) {
|
|
3766
|
-
this.metrics.misses++;
|
|
3767
|
-
this.updateHitRate();
|
|
3768
|
-
return null;
|
|
3769
|
-
}
|
|
3770
|
-
/**
|
|
3771
|
-
* DISABLED - Does nothing
|
|
3772
|
-
*/
|
|
3773
|
-
async setSummary(key, summary) {
|
|
3774
|
-
}
|
|
3775
|
-
/**
|
|
3776
|
-
* DISABLED - Always returns null
|
|
3777
|
-
*/
|
|
3778
|
-
async getVideos(key) {
|
|
3779
|
-
this.metrics.misses++;
|
|
3780
|
-
this.updateHitRate();
|
|
3781
|
-
return null;
|
|
3782
|
-
}
|
|
3783
|
-
/**
|
|
3784
|
-
* DISABLED - Does nothing
|
|
3785
|
-
*/
|
|
3786
|
-
async setVideos(key, videos) {
|
|
3787
|
-
}
|
|
3788
|
-
/**
|
|
3789
|
-
* DISABLED - Always returns null
|
|
3790
|
-
*/
|
|
3791
|
-
async getClipCounts(key) {
|
|
3792
|
-
console.log("[SmartVideoCache] DISABLED - Returning null for clip counts");
|
|
3793
|
-
this.metrics.misses++;
|
|
3794
|
-
this.updateHitRate();
|
|
3795
|
-
return null;
|
|
3796
|
-
}
|
|
3797
|
-
/**
|
|
3798
|
-
* DISABLED - Does nothing
|
|
3799
|
-
*/
|
|
3800
|
-
async setClipCounts(key, clipCountsWithIndex, ttlMinutes) {
|
|
3801
|
-
console.log("[SmartVideoCache] DISABLED - Not caching clip counts");
|
|
3802
|
-
}
|
|
3803
|
-
/**
|
|
3804
|
-
* DISABLED - Does nothing
|
|
3805
|
-
*/
|
|
3806
|
-
setPrefetchStatus(key, status) {
|
|
3807
|
-
}
|
|
3808
|
-
/**
|
|
3809
|
-
* DISABLED - Always returns NONE
|
|
3810
|
-
*/
|
|
3811
|
-
getPrefetchStatus(key) {
|
|
3812
|
-
return "none" /* NONE */;
|
|
3813
|
-
}
|
|
3814
|
-
/**
|
|
3815
|
-
* DISABLED - Always returns false
|
|
3816
|
-
*/
|
|
3817
|
-
isPrefetchReady(key) {
|
|
3818
|
-
return false;
|
|
3819
|
-
}
|
|
3820
|
-
/**
|
|
3821
|
-
* DISABLED - Returns empty cleanup function
|
|
3822
|
-
*/
|
|
3823
|
-
subscribeToPrefetchStatus(key, callback) {
|
|
3824
|
-
return () => {
|
|
3825
|
-
};
|
|
3826
|
-
}
|
|
3827
|
-
/**
|
|
3828
|
-
* DISABLED - Always returns null
|
|
3829
|
-
*/
|
|
3830
|
-
getSignedUrl(s3Uri) {
|
|
3831
|
-
this.metrics.misses++;
|
|
3832
|
-
this.updateHitRate();
|
|
3833
|
-
return null;
|
|
3834
|
-
}
|
|
3835
|
-
/**
|
|
3836
|
-
* DISABLED - Does nothing
|
|
3837
|
-
*/
|
|
3838
|
-
setSignedUrl(s3Uri, url, ttlSeconds = 3600) {
|
|
3839
|
-
}
|
|
3840
|
-
/**
|
|
3841
|
-
* DISABLED - Always returns empty array
|
|
3842
|
-
*/
|
|
3843
|
-
async getVideosByWorkspace(workspaceId) {
|
|
3844
|
-
return [];
|
|
3845
|
-
}
|
|
3846
|
-
/**
|
|
3847
|
-
* DISABLED - Does nothing
|
|
3848
|
-
*/
|
|
3849
|
-
async warmup(entries) {
|
|
3850
|
-
}
|
|
3851
|
-
/**
|
|
3852
|
-
* Get cache statistics (mostly zeros since cache is disabled)
|
|
3853
|
-
*/
|
|
3854
|
-
getStats() {
|
|
3855
|
-
return {
|
|
3856
|
-
...this.metrics,
|
|
3857
|
-
summaryStats: { hits: 0, misses: this.metrics.misses, evictions: 0, totalSize: 0 },
|
|
3858
|
-
videoStats: { hits: 0, misses: this.metrics.misses, evictions: 0, totalSize: 0 },
|
|
3859
|
-
clipCountsStats: { hits: 0, misses: this.metrics.misses, evictions: 0, totalSize: 0 },
|
|
3860
|
-
urlCacheSize: 0
|
|
3861
|
-
};
|
|
3862
|
-
}
|
|
3863
|
-
/**
|
|
3864
|
-
* DISABLED - Does nothing
|
|
3865
|
-
*/
|
|
3866
|
-
clear(type) {
|
|
3867
|
-
this.removeAllListeners();
|
|
3868
|
-
}
|
|
3869
|
-
/**
|
|
3870
|
-
* Update hit rate metric
|
|
3871
|
-
*/
|
|
3872
|
-
updateHitRate() {
|
|
3873
|
-
const total = this.metrics.hits + this.metrics.misses;
|
|
3874
|
-
this.metrics.hitRate = total > 0 ? this.metrics.hits / total : 0;
|
|
3875
|
-
}
|
|
3876
|
-
/**
|
|
3877
|
-
* Export cache state for debugging (returns empty state)
|
|
3878
|
-
*/
|
|
3879
|
-
exportState() {
|
|
3880
|
-
return {
|
|
3881
|
-
summaries: [],
|
|
3882
|
-
videos: [],
|
|
3883
|
-
urls: [],
|
|
3884
|
-
metrics: this.getStats()
|
|
3885
|
-
};
|
|
3886
|
-
}
|
|
3887
|
-
};
|
|
3888
|
-
var smartVideoCache = new SmartVideoCache();
|
|
3889
|
-
if (typeof window !== "undefined") {
|
|
3890
|
-
window.addEventListener("beforeunload", () => {
|
|
3891
|
-
console.log("[SmartVideoCache] Cache disabled - no cleanup needed");
|
|
3892
|
-
});
|
|
3893
|
-
}
|
|
3894
3576
|
var getSupabaseClient = () => {
|
|
3895
3577
|
const url = process.env.NEXT_PUBLIC_SUPABASE_URL;
|
|
3896
3578
|
const key = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
|
|
@@ -3903,616 +3585,6 @@ var getAuthToken = async () => {
|
|
|
3903
3585
|
try {
|
|
3904
3586
|
const supabase = getSupabaseClient();
|
|
3905
3587
|
const { data: { session } } = await supabase.auth.getSession();
|
|
3906
|
-
return session?.access_token || null;
|
|
3907
|
-
} catch (error) {
|
|
3908
|
-
console.error("[S3ClipsAPIClient] Error getting auth token:", error);
|
|
3909
|
-
return null;
|
|
3910
|
-
}
|
|
3911
|
-
};
|
|
3912
|
-
var S3ClipsAPIClient = class {
|
|
3913
|
-
constructor(sopCategories) {
|
|
3914
|
-
this.baseUrl = "/api/clips";
|
|
3915
|
-
this.requestCache = /* @__PURE__ */ new Map();
|
|
3916
|
-
this.sopCategories = sopCategories;
|
|
3917
|
-
console.log("[S3ClipsAPIClient] \u2705 Initialized - Using secure API routes (no direct S3 access)");
|
|
3918
|
-
}
|
|
3919
|
-
/**
|
|
3920
|
-
* Fetch with authentication and error handling
|
|
3921
|
-
*/
|
|
3922
|
-
async fetchWithAuth(endpoint, body) {
|
|
3923
|
-
const token = await getAuthToken();
|
|
3924
|
-
if (!token) {
|
|
3925
|
-
throw new Error("Authentication required");
|
|
3926
|
-
}
|
|
3927
|
-
const response = await fetch(endpoint, {
|
|
3928
|
-
method: "POST",
|
|
3929
|
-
headers: {
|
|
3930
|
-
"Authorization": `Bearer ${token}`,
|
|
3931
|
-
"Content-Type": "application/json"
|
|
3932
|
-
},
|
|
3933
|
-
body: JSON.stringify(body)
|
|
3934
|
-
});
|
|
3935
|
-
if (!response.ok) {
|
|
3936
|
-
const error = await response.json().catch(() => ({ error: "Request failed" }));
|
|
3937
|
-
throw new Error(error.error || `API error: ${response.status}`);
|
|
3938
|
-
}
|
|
3939
|
-
return response.json();
|
|
3940
|
-
}
|
|
3941
|
-
/**
|
|
3942
|
-
* Deduplicate requests to prevent multiple API calls
|
|
3943
|
-
*/
|
|
3944
|
-
async deduplicate(key, factory) {
|
|
3945
|
-
if (this.requestCache.has(key)) {
|
|
3946
|
-
console.log(`[S3ClipsAPIClient] Deduplicating request: ${key}`);
|
|
3947
|
-
return this.requestCache.get(key);
|
|
3948
|
-
}
|
|
3949
|
-
const promise = factory().finally(() => {
|
|
3950
|
-
this.requestCache.delete(key);
|
|
3951
|
-
});
|
|
3952
|
-
this.requestCache.set(key, promise);
|
|
3953
|
-
return promise;
|
|
3954
|
-
}
|
|
3955
|
-
/**
|
|
3956
|
-
* List S3 clips
|
|
3957
|
-
*/
|
|
3958
|
-
async listS3Clips(params) {
|
|
3959
|
-
const cacheKey = `list:${JSON.stringify(params)}`;
|
|
3960
|
-
return this.deduplicate(cacheKey, async () => {
|
|
3961
|
-
const response = await this.fetchWithAuth(this.baseUrl, {
|
|
3962
|
-
action: "list",
|
|
3963
|
-
workspaceId: params.workspaceId,
|
|
3964
|
-
date: params.date,
|
|
3965
|
-
shift: params.shiftId,
|
|
3966
|
-
maxKeys: params.maxKeys
|
|
3967
|
-
});
|
|
3968
|
-
return response.clips.map((clip) => clip.originalUri);
|
|
3969
|
-
});
|
|
3970
|
-
}
|
|
3971
|
-
/**
|
|
3972
|
-
* Get clip counts
|
|
3973
|
-
*/
|
|
3974
|
-
async getClipCounts(workspaceId, date, shiftId) {
|
|
3975
|
-
const cacheKey = `counts:${workspaceId}:${date}:${shiftId}`;
|
|
3976
|
-
return this.deduplicate(cacheKey, async () => {
|
|
3977
|
-
const response = await this.fetchWithAuth(this.baseUrl, {
|
|
3978
|
-
action: "count",
|
|
3979
|
-
workspaceId,
|
|
3980
|
-
date,
|
|
3981
|
-
shift: shiftId.toString()
|
|
3982
|
-
});
|
|
3983
|
-
return response.counts;
|
|
3984
|
-
});
|
|
3985
|
-
}
|
|
3986
|
-
/**
|
|
3987
|
-
* Get clip counts with index (for compatibility)
|
|
3988
|
-
*/
|
|
3989
|
-
async getClipCountsWithIndex(workspaceId, date, shiftId) {
|
|
3990
|
-
const counts = await this.getClipCounts(workspaceId, date, shiftId.toString());
|
|
3991
|
-
const videoIndex = {
|
|
3992
|
-
byCategory: /* @__PURE__ */ new Map(),
|
|
3993
|
-
allVideos: [],
|
|
3994
|
-
counts,
|
|
3995
|
-
workspaceId,
|
|
3996
|
-
date,
|
|
3997
|
-
shiftId: shiftId.toString(),
|
|
3998
|
-
lastUpdated: /* @__PURE__ */ new Date()
|
|
3999
|
-
};
|
|
4000
|
-
return { counts, videoIndex };
|
|
4001
|
-
}
|
|
4002
|
-
/**
|
|
4003
|
-
* Get metadata for a video
|
|
4004
|
-
*/
|
|
4005
|
-
async getMetadata(playlistUri) {
|
|
4006
|
-
const cacheKey = `metadata:${playlistUri}`;
|
|
4007
|
-
return this.deduplicate(cacheKey, async () => {
|
|
4008
|
-
const response = await this.fetchWithAuth(this.baseUrl, {
|
|
4009
|
-
action: "metadata",
|
|
4010
|
-
playlistUri
|
|
4011
|
-
});
|
|
4012
|
-
return response.metadata;
|
|
4013
|
-
});
|
|
4014
|
-
}
|
|
4015
|
-
/**
|
|
4016
|
-
* Get metadata cycle time
|
|
4017
|
-
*/
|
|
4018
|
-
async getMetadataCycleTime(playlistUri) {
|
|
4019
|
-
const metadata = await this.getMetadata(playlistUri);
|
|
4020
|
-
return metadata?.cycle_time_seconds || null;
|
|
4021
|
-
}
|
|
4022
|
-
/**
|
|
4023
|
-
* Get first clip for category
|
|
4024
|
-
*/
|
|
4025
|
-
async getFirstClipForCategory(workspaceId, date, shiftId, category) {
|
|
4026
|
-
const cacheKey = `first:${workspaceId}:${date}:${shiftId}:${category}`;
|
|
4027
|
-
return this.deduplicate(cacheKey, async () => {
|
|
4028
|
-
const response = await this.fetchWithAuth(this.baseUrl, {
|
|
4029
|
-
action: "first",
|
|
4030
|
-
workspaceId,
|
|
4031
|
-
date,
|
|
4032
|
-
shift: shiftId.toString(),
|
|
4033
|
-
category,
|
|
4034
|
-
sopCategories: this.sopCategories
|
|
4035
|
-
});
|
|
4036
|
-
return response.video;
|
|
4037
|
-
});
|
|
4038
|
-
}
|
|
4039
|
-
/**
|
|
4040
|
-
* Get clip by index
|
|
4041
|
-
*/
|
|
4042
|
-
async getClipByIndex(workspaceId, date, shiftId, category, index, includeCycleTime = true, includeMetadata = false) {
|
|
4043
|
-
const cacheKey = `by-index:${workspaceId}:${date}:${shiftId}:${category}:${index}`;
|
|
4044
|
-
return this.deduplicate(cacheKey, async () => {
|
|
4045
|
-
const response = await this.fetchWithAuth(this.baseUrl, {
|
|
4046
|
-
action: "by-index",
|
|
4047
|
-
workspaceId,
|
|
4048
|
-
date,
|
|
4049
|
-
shift: shiftId.toString(),
|
|
4050
|
-
category,
|
|
4051
|
-
index,
|
|
4052
|
-
sopCategories: this.sopCategories
|
|
4053
|
-
});
|
|
4054
|
-
const video = response.video;
|
|
4055
|
-
if (video && includeMetadata && video.originalUri) {
|
|
4056
|
-
try {
|
|
4057
|
-
const metadata = await this.getMetadata(video.originalUri);
|
|
4058
|
-
if (metadata) {
|
|
4059
|
-
video.cycle_time_seconds = metadata.cycle_time_seconds;
|
|
4060
|
-
video.creation_timestamp = metadata.creation_timestamp;
|
|
4061
|
-
}
|
|
4062
|
-
} catch (error) {
|
|
4063
|
-
console.warn("[S3ClipsAPIClient] Failed to fetch metadata:", error);
|
|
4064
|
-
}
|
|
4065
|
-
}
|
|
4066
|
-
return video;
|
|
4067
|
-
});
|
|
4068
|
-
}
|
|
4069
|
-
/**
|
|
4070
|
-
* Get videos page with pagination
|
|
4071
|
-
*/
|
|
4072
|
-
async getVideosPage(workspaceId, date, shiftId, category, pageSize = 5, startAfter) {
|
|
4073
|
-
const cacheKey = `page:${workspaceId}:${date}:${shiftId}:${category}:${pageSize}:${startAfter || "first"}`;
|
|
4074
|
-
return this.deduplicate(cacheKey, async () => {
|
|
4075
|
-
const response = await this.fetchWithAuth(this.baseUrl, {
|
|
4076
|
-
action: "page",
|
|
4077
|
-
workspaceId,
|
|
4078
|
-
date,
|
|
4079
|
-
shift: shiftId.toString(),
|
|
4080
|
-
category,
|
|
4081
|
-
pageSize,
|
|
4082
|
-
startAfter,
|
|
4083
|
-
sopCategories: this.sopCategories
|
|
4084
|
-
});
|
|
4085
|
-
return {
|
|
4086
|
-
videos: response.videos,
|
|
4087
|
-
nextToken: response.nextToken,
|
|
4088
|
-
hasMore: response.hasMore
|
|
4089
|
-
};
|
|
4090
|
-
});
|
|
4091
|
-
}
|
|
4092
|
-
/**
|
|
4093
|
-
* Batch fetch multiple videos in parallel
|
|
4094
|
-
*/
|
|
4095
|
-
async batchFetchVideos(workspaceId, date, shiftId, requests) {
|
|
4096
|
-
const batchKey = `batch:${workspaceId}:${date}:${shiftId}:${requests.length}`;
|
|
4097
|
-
return this.deduplicate(batchKey, async () => {
|
|
4098
|
-
const response = await this.fetchWithAuth("/api/clips/batch", {
|
|
4099
|
-
workspaceId,
|
|
4100
|
-
date,
|
|
4101
|
-
shift: shiftId.toString(),
|
|
4102
|
-
requests,
|
|
4103
|
-
sopCategories: this.sopCategories
|
|
4104
|
-
});
|
|
4105
|
-
console.log(`[S3ClipsAPIClient] Batch fetched ${response.videos.length} videos in ${response.performance.duration}ms`);
|
|
4106
|
-
return response.videos;
|
|
4107
|
-
});
|
|
4108
|
-
}
|
|
4109
|
-
/**
|
|
4110
|
-
* Convert S3 URI to CloudFront URL
|
|
4111
|
-
* In the API client, URLs are already signed from the server
|
|
4112
|
-
*/
|
|
4113
|
-
s3UriToCloudfront(s3Uri) {
|
|
4114
|
-
return s3Uri;
|
|
4115
|
-
}
|
|
4116
|
-
/**
|
|
4117
|
-
* Clean up resources
|
|
4118
|
-
*/
|
|
4119
|
-
dispose() {
|
|
4120
|
-
this.requestCache.clear();
|
|
4121
|
-
}
|
|
4122
|
-
/**
|
|
4123
|
-
* Get service statistics
|
|
4124
|
-
*/
|
|
4125
|
-
getStats() {
|
|
4126
|
-
return {
|
|
4127
|
-
requestCache: {
|
|
4128
|
-
pendingCount: this.requestCache.size,
|
|
4129
|
-
maxSize: 1e3
|
|
4130
|
-
}
|
|
4131
|
-
};
|
|
4132
|
-
}
|
|
4133
|
-
};
|
|
4134
|
-
|
|
4135
|
-
// src/lib/api/s3-clips-secure.ts
|
|
4136
|
-
var S3ClipsService = class {
|
|
4137
|
-
constructor(config) {
|
|
4138
|
-
// Flags for compatibility
|
|
4139
|
-
this.isIndexBuilding = false;
|
|
4140
|
-
this.isPrefetching = false;
|
|
4141
|
-
this.currentMetadataFetches = 0;
|
|
4142
|
-
this.MAX_CONCURRENT_METADATA = 3;
|
|
4143
|
-
this.config = config;
|
|
4144
|
-
if (!config.s3Config) {
|
|
4145
|
-
throw new Error("S3 configuration is required");
|
|
4146
|
-
}
|
|
4147
|
-
const sopCategories = config.s3Config.sopCategories?.default;
|
|
4148
|
-
this.apiClient = new S3ClipsAPIClient(sopCategories);
|
|
4149
|
-
const processing = config.s3Config.processing || {};
|
|
4150
|
-
this.defaultLimitPerCategory = processing.defaultLimitPerCategory || 30;
|
|
4151
|
-
this.maxLimitPerCategory = processing.maxLimitPerCategory || 1e3;
|
|
4152
|
-
this.concurrencyLimit = processing.concurrencyLimit || 10;
|
|
4153
|
-
this.maxInitialFetch = processing.maxInitialFetch || 60;
|
|
4154
|
-
console.log("[S3ClipsService] \u2705 Initialized with secure API client - AWS credentials are now server-side only!");
|
|
4155
|
-
}
|
|
4156
|
-
/**
|
|
4157
|
-
* Lists S3 clips using API
|
|
4158
|
-
*/
|
|
4159
|
-
async listS3Clips(params) {
|
|
4160
|
-
const { workspaceId, date, shiftId } = params;
|
|
4161
|
-
if (!isValidShiftId(shiftId)) {
|
|
4162
|
-
console.error(`[S3ClipsService] Invalid shift ID: ${shiftId}`);
|
|
4163
|
-
return [];
|
|
4164
|
-
}
|
|
4165
|
-
console.log(`[S3ClipsService] Listing clips via API for workspace: ${workspaceId}`);
|
|
4166
|
-
try {
|
|
4167
|
-
return await this.apiClient.listS3Clips(params);
|
|
4168
|
-
} catch (error) {
|
|
4169
|
-
console.error("[S3ClipsService] Error listing clips:", error);
|
|
4170
|
-
return [];
|
|
4171
|
-
}
|
|
4172
|
-
}
|
|
4173
|
-
/**
|
|
4174
|
-
* Get metadata cycle time
|
|
4175
|
-
*/
|
|
4176
|
-
async getMetadataCycleTime(playlistUri) {
|
|
4177
|
-
try {
|
|
4178
|
-
return await this.apiClient.getMetadataCycleTime(playlistUri);
|
|
4179
|
-
} catch (error) {
|
|
4180
|
-
console.error("[S3ClipsService] Error fetching metadata cycle time:", error);
|
|
4181
|
-
return null;
|
|
4182
|
-
}
|
|
4183
|
-
}
|
|
4184
|
-
/**
|
|
4185
|
-
* Control prefetch mode
|
|
4186
|
-
*/
|
|
4187
|
-
setPrefetchMode(enabled) {
|
|
4188
|
-
this.isPrefetching = enabled;
|
|
4189
|
-
console.log(`[S3ClipsService] Prefetch mode ${enabled ? "enabled" : "disabled"}`);
|
|
4190
|
-
}
|
|
4191
|
-
/**
|
|
4192
|
-
* Get full metadata
|
|
4193
|
-
*/
|
|
4194
|
-
async getFullMetadata(playlistUri) {
|
|
4195
|
-
if (this.isIndexBuilding || this.isPrefetching) {
|
|
4196
|
-
console.warn("[S3ClipsService] Skipping metadata - operation in progress");
|
|
4197
|
-
return null;
|
|
4198
|
-
}
|
|
4199
|
-
if (this.currentMetadataFetches >= this.MAX_CONCURRENT_METADATA) {
|
|
4200
|
-
console.warn("[S3ClipsService] Skipping metadata - max concurrent fetches reached");
|
|
4201
|
-
return null;
|
|
4202
|
-
}
|
|
4203
|
-
this.currentMetadataFetches++;
|
|
4204
|
-
try {
|
|
4205
|
-
return await this.apiClient.getMetadata(playlistUri);
|
|
4206
|
-
} catch (error) {
|
|
4207
|
-
console.error("[S3ClipsService] Error fetching metadata:", error);
|
|
4208
|
-
return null;
|
|
4209
|
-
} finally {
|
|
4210
|
-
this.currentMetadataFetches--;
|
|
4211
|
-
}
|
|
4212
|
-
}
|
|
4213
|
-
/**
|
|
4214
|
-
* Convert S3 URI to CloudFront URL
|
|
4215
|
-
* URLs from API are already signed
|
|
4216
|
-
*/
|
|
4217
|
-
s3UriToCloudfront(s3Uri) {
|
|
4218
|
-
if (s3Uri.startsWith("http")) {
|
|
4219
|
-
return s3Uri;
|
|
4220
|
-
}
|
|
4221
|
-
console.warn("[S3ClipsService] Unexpected S3 URI in secure mode:", s3Uri);
|
|
4222
|
-
return s3Uri;
|
|
4223
|
-
}
|
|
4224
|
-
/**
|
|
4225
|
-
* Get SOP categories for workspace
|
|
4226
|
-
*/
|
|
4227
|
-
getSOPCategories(workspaceId) {
|
|
4228
|
-
const sopConfig = this.config.s3Config?.sopCategories;
|
|
4229
|
-
if (!sopConfig) return void 0;
|
|
4230
|
-
if (sopConfig.workspaceOverrides && sopConfig.workspaceOverrides[workspaceId]) {
|
|
4231
|
-
return sopConfig.workspaceOverrides[workspaceId];
|
|
4232
|
-
}
|
|
4233
|
-
return sopConfig.default;
|
|
4234
|
-
}
|
|
4235
|
-
async getClipCounts(workspaceId, date, shiftId, buildIndex) {
|
|
4236
|
-
if (!isValidShiftId(shiftId)) {
|
|
4237
|
-
console.error(`[S3ClipsService] Invalid shift ID: ${shiftId}`);
|
|
4238
|
-
return buildIndex ? { counts: {}, videoIndex: this.createEmptyVideoIndex(workspaceId, date, shiftId.toString()) } : {};
|
|
4239
|
-
}
|
|
4240
|
-
try {
|
|
4241
|
-
if (buildIndex) {
|
|
4242
|
-
const result = await this.apiClient.getClipCountsWithIndex(workspaceId, date, shiftId);
|
|
4243
|
-
const cacheKey = `clip-counts:${workspaceId}:${date}:${shiftId}`;
|
|
4244
|
-
await smartVideoCache.setClipCounts(cacheKey, result);
|
|
4245
|
-
return result;
|
|
4246
|
-
} else {
|
|
4247
|
-
const counts = await this.apiClient.getClipCounts(workspaceId, date, shiftId);
|
|
4248
|
-
return counts;
|
|
4249
|
-
}
|
|
4250
|
-
} catch (error) {
|
|
4251
|
-
console.error("[S3ClipsService] Error fetching clip counts:", error);
|
|
4252
|
-
return buildIndex ? { counts: {}, videoIndex: this.createEmptyVideoIndex(workspaceId, date, shiftId.toString()) } : {};
|
|
4253
|
-
}
|
|
4254
|
-
}
|
|
4255
|
-
async getClipCountsCacheFirst(workspaceId, date, shiftId, buildIndex) {
|
|
4256
|
-
const cacheKey = `clip-counts:${workspaceId}:${date}:${shiftId}`;
|
|
4257
|
-
const cachedResult = await smartVideoCache.getClipCounts(cacheKey);
|
|
4258
|
-
if (cachedResult) {
|
|
4259
|
-
console.log("[S3ClipsService] Using cached clip counts");
|
|
4260
|
-
return buildIndex ? cachedResult : cachedResult.counts;
|
|
4261
|
-
}
|
|
4262
|
-
console.log("[S3ClipsService] Cache miss - fetching from API");
|
|
4263
|
-
return buildIndex ? this.getClipCounts(workspaceId, date, shiftId, true) : this.getClipCounts(workspaceId, date, shiftId);
|
|
4264
|
-
}
|
|
4265
|
-
/**
|
|
4266
|
-
* Get first clip for category
|
|
4267
|
-
*/
|
|
4268
|
-
async getFirstClipForCategory(workspaceId, date, shiftId, category) {
|
|
4269
|
-
if (!isValidShiftId(shiftId)) {
|
|
4270
|
-
console.error(`[S3ClipsService] Invalid shift ID: ${shiftId}`);
|
|
4271
|
-
return null;
|
|
4272
|
-
}
|
|
4273
|
-
try {
|
|
4274
|
-
return await this.apiClient.getFirstClipForCategory(workspaceId, date, shiftId, category);
|
|
4275
|
-
} catch (error) {
|
|
4276
|
-
console.error("[S3ClipsService] Error fetching first clip:", error);
|
|
4277
|
-
return null;
|
|
4278
|
-
}
|
|
4279
|
-
}
|
|
4280
|
-
/**
|
|
4281
|
-
* Get video from index (for compatibility)
|
|
4282
|
-
*/
|
|
4283
|
-
async getVideoFromIndex(videoIndex, category, index, includeCycleTime = true, includeMetadata = true) {
|
|
4284
|
-
const { workspaceId, date, shiftId } = videoIndex;
|
|
4285
|
-
return this.getClipByIndex(
|
|
4286
|
-
workspaceId,
|
|
4287
|
-
date,
|
|
4288
|
-
shiftId,
|
|
4289
|
-
category,
|
|
4290
|
-
index,
|
|
4291
|
-
includeCycleTime,
|
|
4292
|
-
includeMetadata
|
|
4293
|
-
);
|
|
4294
|
-
}
|
|
4295
|
-
/**
|
|
4296
|
-
* Get clip by index
|
|
4297
|
-
*/
|
|
4298
|
-
async getClipByIndex(workspaceId, date, shiftId, category, index, includeCycleTime = true, includeMetadata = false) {
|
|
4299
|
-
try {
|
|
4300
|
-
return await this.apiClient.getClipByIndex(
|
|
4301
|
-
workspaceId,
|
|
4302
|
-
date,
|
|
4303
|
-
shiftId,
|
|
4304
|
-
category,
|
|
4305
|
-
index,
|
|
4306
|
-
includeCycleTime,
|
|
4307
|
-
includeMetadata
|
|
4308
|
-
);
|
|
4309
|
-
} catch (error) {
|
|
4310
|
-
console.error("[S3ClipsService] Error fetching clip by index:", error);
|
|
4311
|
-
return null;
|
|
4312
|
-
}
|
|
4313
|
-
}
|
|
4314
|
-
/**
|
|
4315
|
-
* Process full video (for compatibility)
|
|
4316
|
-
*/
|
|
4317
|
-
async processFullVideo(uri, index, workspaceId, date, shiftId, includeCycleTime, includeMetadata = false) {
|
|
4318
|
-
const sopCategories = this.getSOPCategories(workspaceId);
|
|
4319
|
-
const parsedInfo = parseS3Uri(uri, sopCategories);
|
|
4320
|
-
if (!parsedInfo) {
|
|
4321
|
-
console.warn(`Skipping URI due to parsing failure: ${uri}`);
|
|
4322
|
-
return null;
|
|
4323
|
-
}
|
|
4324
|
-
const video = {
|
|
4325
|
-
id: `${workspaceId}-${date}-${shiftId}-${index}`,
|
|
4326
|
-
src: uri,
|
|
4327
|
-
// Already signed from API
|
|
4328
|
-
...parsedInfo,
|
|
4329
|
-
originalUri: uri
|
|
4330
|
-
};
|
|
4331
|
-
if (includeMetadata) {
|
|
4332
|
-
const metadata = await this.getFullMetadata(uri);
|
|
4333
|
-
if (metadata) {
|
|
4334
|
-
video.cycle_time_seconds = metadata.original_task_metadata?.cycle_time;
|
|
4335
|
-
video.creation_timestamp = metadata.upload_timestamp || metadata.original_task_metadata?.timestamp || metadata.creation_timestamp;
|
|
4336
|
-
}
|
|
4337
|
-
}
|
|
4338
|
-
return video;
|
|
4339
|
-
}
|
|
4340
|
-
/**
|
|
4341
|
-
* Fetch clips (main method for compatibility)
|
|
4342
|
-
*/
|
|
4343
|
-
async fetchClips(params) {
|
|
4344
|
-
const {
|
|
4345
|
-
workspaceId,
|
|
4346
|
-
date: inputDate,
|
|
4347
|
-
shift,
|
|
4348
|
-
category,
|
|
4349
|
-
limit,
|
|
4350
|
-
offset = 0,
|
|
4351
|
-
mode
|
|
4352
|
-
} = params;
|
|
4353
|
-
if (!workspaceId) {
|
|
4354
|
-
throw new Error("Valid Workspace ID is required");
|
|
4355
|
-
}
|
|
4356
|
-
const date = inputDate || getOperationalDate(
|
|
4357
|
-
this.config.dateTimeConfig?.defaultTimezone || "Asia/Kolkata",
|
|
4358
|
-
/* @__PURE__ */ new Date(),
|
|
4359
|
-
this.config.shiftConfig?.dayShift?.startTime || "06:00"
|
|
4360
|
-
);
|
|
4361
|
-
if (!isValidDateFormat(date)) {
|
|
4362
|
-
throw new Error("Invalid date format. Use YYYY-MM-DD.");
|
|
4363
|
-
}
|
|
4364
|
-
let shiftId;
|
|
4365
|
-
if (shift !== void 0) {
|
|
4366
|
-
if (!isValidShiftId(shift)) {
|
|
4367
|
-
throw new Error("Invalid shift value. Must be 0 (day) or 1 (night).");
|
|
4368
|
-
}
|
|
4369
|
-
shiftId = parseInt(shift, 10);
|
|
4370
|
-
} else {
|
|
4371
|
-
const { shiftId: currentShiftId } = getCurrentShift(
|
|
4372
|
-
this.config.dateTimeConfig?.defaultTimezone || "Asia/Kolkata",
|
|
4373
|
-
this.config.shiftConfig
|
|
4374
|
-
);
|
|
4375
|
-
shiftId = currentShiftId;
|
|
4376
|
-
}
|
|
4377
|
-
console.log(`[S3ClipsService] Fetching clips for workspace ${workspaceId}`);
|
|
4378
|
-
if (mode === "summary") {
|
|
4379
|
-
const counts = await this.getClipCounts(workspaceId, date, shiftId.toString());
|
|
4380
|
-
return { counts, samples: {} };
|
|
4381
|
-
}
|
|
4382
|
-
const effectiveLimit = limit || this.defaultLimitPerCategory;
|
|
4383
|
-
const result = await this.getVideosPage(
|
|
4384
|
-
workspaceId,
|
|
4385
|
-
date,
|
|
4386
|
-
shiftId,
|
|
4387
|
-
category || "all",
|
|
4388
|
-
effectiveLimit
|
|
4389
|
-
);
|
|
4390
|
-
return result.videos;
|
|
4391
|
-
}
|
|
4392
|
-
/**
|
|
4393
|
-
* Batch fetch multiple videos in parallel
|
|
4394
|
-
*/
|
|
4395
|
-
async batchFetchVideos(workspaceId, date, shiftId, requests) {
|
|
4396
|
-
try {
|
|
4397
|
-
const results = await this.apiClient.batchFetchVideos(
|
|
4398
|
-
workspaceId,
|
|
4399
|
-
date,
|
|
4400
|
-
shiftId,
|
|
4401
|
-
requests
|
|
4402
|
-
);
|
|
4403
|
-
return results.map((r2) => r2.video).filter((v) => v !== null);
|
|
4404
|
-
} catch (error) {
|
|
4405
|
-
console.error("[S3ClipsService] Error batch fetching videos:", error);
|
|
4406
|
-
return [];
|
|
4407
|
-
}
|
|
4408
|
-
}
|
|
4409
|
-
/**
|
|
4410
|
-
* Get videos page using pagination API
|
|
4411
|
-
*/
|
|
4412
|
-
async getVideosPage(workspaceId, date, shiftId, category, pageSize = 5, startAfter) {
|
|
4413
|
-
try {
|
|
4414
|
-
return await this.apiClient.getVideosPage(
|
|
4415
|
-
workspaceId,
|
|
4416
|
-
date,
|
|
4417
|
-
shiftId,
|
|
4418
|
-
category,
|
|
4419
|
-
pageSize,
|
|
4420
|
-
startAfter
|
|
4421
|
-
);
|
|
4422
|
-
} catch (error) {
|
|
4423
|
-
console.error("[S3ClipsService] Error fetching videos page:", error);
|
|
4424
|
-
return { videos: [], hasMore: false };
|
|
4425
|
-
}
|
|
4426
|
-
}
|
|
4427
|
-
/**
|
|
4428
|
-
* Create empty video index for compatibility
|
|
4429
|
-
*/
|
|
4430
|
-
createEmptyVideoIndex(workspaceId, date, shiftId) {
|
|
4431
|
-
return {
|
|
4432
|
-
byCategory: /* @__PURE__ */ new Map(),
|
|
4433
|
-
allVideos: [],
|
|
4434
|
-
counts: {},
|
|
4435
|
-
workspaceId,
|
|
4436
|
-
date,
|
|
4437
|
-
shiftId,
|
|
4438
|
-
lastUpdated: /* @__PURE__ */ new Date(),
|
|
4439
|
-
_debugId: `empty_${Date.now()}`
|
|
4440
|
-
};
|
|
4441
|
-
}
|
|
4442
|
-
/**
|
|
4443
|
-
* Get clip by ID
|
|
4444
|
-
*/
|
|
4445
|
-
async getClipById(clipId, sopCategories) {
|
|
4446
|
-
console.log(`[S3ClipsService] Getting clip by ID: ${clipId} (Note: This is a fallback implementation)`);
|
|
4447
|
-
try {
|
|
4448
|
-
const parts = clipId.split("-");
|
|
4449
|
-
if (parts.length >= 5) {
|
|
4450
|
-
const workspaceId = parts[0];
|
|
4451
|
-
const date = parts[1];
|
|
4452
|
-
const shift = parts[2];
|
|
4453
|
-
const category = parts[3];
|
|
4454
|
-
const index = parseInt(parts[4], 10);
|
|
4455
|
-
if (!isNaN(index)) {
|
|
4456
|
-
return await this.getClipByIndex(workspaceId, date, shift, category, index, true, false);
|
|
4457
|
-
}
|
|
4458
|
-
}
|
|
4459
|
-
console.warn(`[S3ClipsService] Could not parse clipId: ${clipId}`);
|
|
4460
|
-
return null;
|
|
4461
|
-
} catch (error) {
|
|
4462
|
-
console.error("[S3ClipsService] Error getting clip by ID:", error);
|
|
4463
|
-
return null;
|
|
4464
|
-
}
|
|
4465
|
-
}
|
|
4466
|
-
/**
|
|
4467
|
-
* Get neighboring clips
|
|
4468
|
-
*/
|
|
4469
|
-
async getNeighboringClips(workspaceId, date, shiftId, category, currentClipId, sopCategories) {
|
|
4470
|
-
console.log(`[S3ClipsService] Getting neighboring clips for ID: ${currentClipId} (Note: This is a fallback implementation)`);
|
|
4471
|
-
try {
|
|
4472
|
-
const parts = currentClipId.split("-");
|
|
4473
|
-
if (parts.length >= 5) {
|
|
4474
|
-
const index = parseInt(parts[4], 10);
|
|
4475
|
-
if (!isNaN(index)) {
|
|
4476
|
-
const [previous, next] = await Promise.all([
|
|
4477
|
-
index > 0 ? this.getClipByIndex(workspaceId, date, shiftId, category, index - 1, true, false) : null,
|
|
4478
|
-
this.getClipByIndex(workspaceId, date, shiftId, category, index + 1, true, false)
|
|
4479
|
-
]);
|
|
4480
|
-
return { previous, next };
|
|
4481
|
-
}
|
|
4482
|
-
}
|
|
4483
|
-
console.warn(`[S3ClipsService] Could not parse currentClipId: ${currentClipId}`);
|
|
4484
|
-
return { previous: null, next: null };
|
|
4485
|
-
} catch (error) {
|
|
4486
|
-
console.error("[S3ClipsService] Error getting neighboring clips:", error);
|
|
4487
|
-
return { previous: null, next: null };
|
|
4488
|
-
}
|
|
4489
|
-
}
|
|
4490
|
-
/**
|
|
4491
|
-
* Cleanup
|
|
4492
|
-
*/
|
|
4493
|
-
dispose() {
|
|
4494
|
-
console.log("[S3ClipsService] Disposing service");
|
|
4495
|
-
this.apiClient.dispose();
|
|
4496
|
-
}
|
|
4497
|
-
/**
|
|
4498
|
-
* Get statistics
|
|
4499
|
-
*/
|
|
4500
|
-
getStats() {
|
|
4501
|
-
return this.apiClient.getStats();
|
|
4502
|
-
}
|
|
4503
|
-
};
|
|
4504
|
-
var getSupabaseClient2 = () => {
|
|
4505
|
-
const url = process.env.NEXT_PUBLIC_SUPABASE_URL;
|
|
4506
|
-
const key = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
|
|
4507
|
-
if (!url || !key) {
|
|
4508
|
-
throw new Error("Supabase configuration missing");
|
|
4509
|
-
}
|
|
4510
|
-
return createClient(url, key);
|
|
4511
|
-
};
|
|
4512
|
-
var getAuthToken2 = async () => {
|
|
4513
|
-
try {
|
|
4514
|
-
const supabase = getSupabaseClient2();
|
|
4515
|
-
const { data: { session } } = await supabase.auth.getSession();
|
|
4516
3588
|
console.log("[S3ClipsSupabase] Auth session exists:", !!session, "has token:", !!session?.access_token);
|
|
4517
3589
|
return session?.access_token || null;
|
|
4518
3590
|
} catch (error) {
|
|
@@ -4543,7 +3615,7 @@ var S3ClipsSupabaseService = class {
|
|
|
4543
3615
|
* Fetch with authentication and error handling
|
|
4544
3616
|
*/
|
|
4545
3617
|
async fetchWithAuth(endpoint, body) {
|
|
4546
|
-
const token = await
|
|
3618
|
+
const token = await getAuthToken();
|
|
4547
3619
|
if (!token) {
|
|
4548
3620
|
throw new Error("Authentication required");
|
|
4549
3621
|
}
|
|
@@ -5006,7 +4078,8 @@ var S3ClipsSupabaseService = class {
|
|
|
5006
4078
|
return descriptions[clipType] || "Analysis Clip";
|
|
5007
4079
|
}
|
|
5008
4080
|
};
|
|
5009
|
-
|
|
4081
|
+
|
|
4082
|
+
// src/lib/services/videoPrefetchManager.ts
|
|
5010
4083
|
var VideoPrefetchManager = class extends EventEmitter {
|
|
5011
4084
|
constructor() {
|
|
5012
4085
|
super();
|
|
@@ -5020,7 +4093,7 @@ var VideoPrefetchManager = class extends EventEmitter {
|
|
|
5020
4093
|
getS3Service(dashboardConfig) {
|
|
5021
4094
|
const configKey = JSON.stringify(dashboardConfig.s3Config);
|
|
5022
4095
|
if (!this.s3Services.has(configKey)) {
|
|
5023
|
-
this.s3Services.set(configKey, new
|
|
4096
|
+
this.s3Services.set(configKey, new S3ClipsSupabaseService(dashboardConfig));
|
|
5024
4097
|
}
|
|
5025
4098
|
return this.s3Services.get(configKey);
|
|
5026
4099
|
}
|
|
@@ -5456,6 +4529,154 @@ var createSupervisorService = (supabase) => {
|
|
|
5456
4529
|
var simulateApiDelay = (ms = 1e3) => {
|
|
5457
4530
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
5458
4531
|
};
|
|
4532
|
+
var TimezoneService = class _TimezoneService {
|
|
4533
|
+
constructor() {
|
|
4534
|
+
this.timezoneCache = /* @__PURE__ */ new Map();
|
|
4535
|
+
this.supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL || "";
|
|
4536
|
+
this.supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || "";
|
|
4537
|
+
}
|
|
4538
|
+
static getInstance() {
|
|
4539
|
+
if (!_TimezoneService.instance) {
|
|
4540
|
+
_TimezoneService.instance = new _TimezoneService();
|
|
4541
|
+
}
|
|
4542
|
+
return _TimezoneService.instance;
|
|
4543
|
+
}
|
|
4544
|
+
/**
|
|
4545
|
+
* Fetch timezone for a specific line from Supabase
|
|
4546
|
+
*/
|
|
4547
|
+
async getTimezoneForLine(lineId, defaultTimezone = "Asia/Kolkata") {
|
|
4548
|
+
if (this.timezoneCache.has(lineId)) {
|
|
4549
|
+
return this.timezoneCache.get(lineId);
|
|
4550
|
+
}
|
|
4551
|
+
try {
|
|
4552
|
+
const supabase = createClient(this.supabaseUrl, this.supabaseAnonKey);
|
|
4553
|
+
const { data, error } = await supabase.from("line_operating_hours").select("timezone").eq("line_id", lineId).order("created_at", { ascending: false }).limit(1).single();
|
|
4554
|
+
if (error) {
|
|
4555
|
+
console.warn(`Failed to fetch timezone for line ${lineId}:`, error);
|
|
4556
|
+
return defaultTimezone;
|
|
4557
|
+
}
|
|
4558
|
+
if (data?.timezone) {
|
|
4559
|
+
this.timezoneCache.set(lineId, data.timezone);
|
|
4560
|
+
return data.timezone;
|
|
4561
|
+
}
|
|
4562
|
+
return defaultTimezone;
|
|
4563
|
+
} catch (error) {
|
|
4564
|
+
console.error("Error fetching timezone:", error);
|
|
4565
|
+
return defaultTimezone;
|
|
4566
|
+
}
|
|
4567
|
+
}
|
|
4568
|
+
/**
|
|
4569
|
+
* Fetch timezone for a company's factory
|
|
4570
|
+
*/
|
|
4571
|
+
async getTimezoneForCompany(companyId, defaultTimezone = "Asia/Kolkata") {
|
|
4572
|
+
const cacheKey = `company-${companyId}`;
|
|
4573
|
+
if (this.timezoneCache.has(cacheKey)) {
|
|
4574
|
+
return this.timezoneCache.get(cacheKey);
|
|
4575
|
+
}
|
|
4576
|
+
try {
|
|
4577
|
+
const supabase = createClient(this.supabaseUrl, this.supabaseAnonKey);
|
|
4578
|
+
const { data, error } = await supabase.from("line_operating_hours").select(`
|
|
4579
|
+
timezone,
|
|
4580
|
+
line:lines!inner(
|
|
4581
|
+
company_id
|
|
4582
|
+
)
|
|
4583
|
+
`).eq("lines.company_id", companyId).order("created_at", { ascending: false }).limit(1);
|
|
4584
|
+
if (error) {
|
|
4585
|
+
console.warn(`Failed to fetch timezone for company ${companyId}:`, error);
|
|
4586
|
+
return defaultTimezone;
|
|
4587
|
+
}
|
|
4588
|
+
if (data?.[0]?.timezone) {
|
|
4589
|
+
this.timezoneCache.set(cacheKey, data[0].timezone);
|
|
4590
|
+
return data[0].timezone;
|
|
4591
|
+
}
|
|
4592
|
+
return defaultTimezone;
|
|
4593
|
+
} catch (error) {
|
|
4594
|
+
console.error("Error fetching company timezone:", error);
|
|
4595
|
+
return defaultTimezone;
|
|
4596
|
+
}
|
|
4597
|
+
}
|
|
4598
|
+
/**
|
|
4599
|
+
* Fetch timezone for a workspace
|
|
4600
|
+
*/
|
|
4601
|
+
async getTimezoneForWorkspace(workspaceId, defaultTimezone = "Asia/Kolkata") {
|
|
4602
|
+
const cacheKey = `workspace-${workspaceId}`;
|
|
4603
|
+
if (this.timezoneCache.has(cacheKey)) {
|
|
4604
|
+
return this.timezoneCache.get(cacheKey);
|
|
4605
|
+
}
|
|
4606
|
+
try {
|
|
4607
|
+
const supabase = createClient(this.supabaseUrl, this.supabaseAnonKey);
|
|
4608
|
+
const { data: workspaceData, error: workspaceError } = await supabase.from("workspaces").select("line_id").eq("id", workspaceId).single();
|
|
4609
|
+
if (workspaceError) {
|
|
4610
|
+
console.warn(`Failed to fetch workspace ${workspaceId}:`, workspaceError);
|
|
4611
|
+
return defaultTimezone;
|
|
4612
|
+
}
|
|
4613
|
+
if (workspaceData?.line_id) {
|
|
4614
|
+
return this.getTimezoneForLine(workspaceData.line_id, defaultTimezone);
|
|
4615
|
+
}
|
|
4616
|
+
const { data: cameraData, error: cameraError } = await supabase.from("cameras").select("line_id").eq("workspace_id", workspaceId).limit(1).single();
|
|
4617
|
+
if (!cameraError && cameraData?.line_id) {
|
|
4618
|
+
return this.getTimezoneForLine(cameraData.line_id, defaultTimezone);
|
|
4619
|
+
}
|
|
4620
|
+
return defaultTimezone;
|
|
4621
|
+
} catch (error) {
|
|
4622
|
+
console.error("Error fetching workspace timezone:", error);
|
|
4623
|
+
return defaultTimezone;
|
|
4624
|
+
}
|
|
4625
|
+
}
|
|
4626
|
+
/**
|
|
4627
|
+
* Batch fetch timezones for multiple lines
|
|
4628
|
+
*/
|
|
4629
|
+
async getTimezonesForLines(lineIds, defaultTimezone = "Asia/Kolkata") {
|
|
4630
|
+
const result = /* @__PURE__ */ new Map();
|
|
4631
|
+
const uncachedLineIds = lineIds.filter((id3) => !this.timezoneCache.has(id3));
|
|
4632
|
+
if (uncachedLineIds.length === 0) {
|
|
4633
|
+
lineIds.forEach((id3) => {
|
|
4634
|
+
result.set(id3, this.timezoneCache.get(id3) || defaultTimezone);
|
|
4635
|
+
});
|
|
4636
|
+
return result;
|
|
4637
|
+
}
|
|
4638
|
+
try {
|
|
4639
|
+
const supabase = createClient(this.supabaseUrl, this.supabaseAnonKey);
|
|
4640
|
+
const { data, error } = await supabase.from("line_operating_hours").select("line_id, timezone").in("line_id", uncachedLineIds).order("created_at", { ascending: false });
|
|
4641
|
+
if (error) {
|
|
4642
|
+
console.warn("Failed to fetch timezones for lines:", error);
|
|
4643
|
+
lineIds.forEach((id3) => result.set(id3, defaultTimezone));
|
|
4644
|
+
return result;
|
|
4645
|
+
}
|
|
4646
|
+
const timezoneMap = /* @__PURE__ */ new Map();
|
|
4647
|
+
data?.forEach((item) => {
|
|
4648
|
+
if (!timezoneMap.has(item.line_id)) {
|
|
4649
|
+
timezoneMap.set(item.line_id, item.timezone);
|
|
4650
|
+
this.timezoneCache.set(item.line_id, item.timezone);
|
|
4651
|
+
}
|
|
4652
|
+
});
|
|
4653
|
+
lineIds.forEach((id3) => {
|
|
4654
|
+
const timezone = this.timezoneCache.get(id3) || timezoneMap.get(id3) || defaultTimezone;
|
|
4655
|
+
result.set(id3, timezone);
|
|
4656
|
+
});
|
|
4657
|
+
return result;
|
|
4658
|
+
} catch (error) {
|
|
4659
|
+
console.error("Error batch fetching timezones:", error);
|
|
4660
|
+
lineIds.forEach((id3) => result.set(id3, defaultTimezone));
|
|
4661
|
+
return result;
|
|
4662
|
+
}
|
|
4663
|
+
}
|
|
4664
|
+
/**
|
|
4665
|
+
* Clear timezone cache
|
|
4666
|
+
*/
|
|
4667
|
+
clearCache() {
|
|
4668
|
+
this.timezoneCache.clear();
|
|
4669
|
+
}
|
|
4670
|
+
/**
|
|
4671
|
+
* Clear specific entry from cache
|
|
4672
|
+
*/
|
|
4673
|
+
clearCacheEntry(key) {
|
|
4674
|
+
this.timezoneCache.delete(key);
|
|
4675
|
+
this.timezoneCache.delete(`company-${key}`);
|
|
4676
|
+
this.timezoneCache.delete(`workspace-${key}`);
|
|
4677
|
+
}
|
|
4678
|
+
};
|
|
4679
|
+
var timezoneService = TimezoneService.getInstance();
|
|
5459
4680
|
var AuthContext = createContext({
|
|
5460
4681
|
session: null,
|
|
5461
4682
|
user: null,
|
|
@@ -5929,18 +5150,96 @@ var useClipFilter = () => {
|
|
|
5929
5150
|
}
|
|
5930
5151
|
return context;
|
|
5931
5152
|
};
|
|
5153
|
+
var TimezoneContext = createContext(void 0);
|
|
5154
|
+
function TimezoneProvider({
|
|
5155
|
+
children,
|
|
5156
|
+
workspaceId: propWorkspaceId,
|
|
5157
|
+
lineId: propLineId,
|
|
5158
|
+
companyId: propCompanyId,
|
|
5159
|
+
fallbackTimezone = "Asia/Kolkata"
|
|
5160
|
+
// Last resort fallback ONLY
|
|
5161
|
+
}) {
|
|
5162
|
+
const dashboardConfig = useDashboardConfig();
|
|
5163
|
+
const workspaceConfig = useWorkspaceConfig();
|
|
5164
|
+
const [timezone, setTimezone] = useState(fallbackTimezone);
|
|
5165
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
5166
|
+
const [error, setError] = useState(null);
|
|
5167
|
+
const workspaceId = propWorkspaceId || (workspaceConfig && typeof workspaceConfig === "object" && "id" in workspaceConfig ? workspaceConfig.id : void 0);
|
|
5168
|
+
const lineId = propLineId;
|
|
5169
|
+
const companyId = propCompanyId || (dashboardConfig && typeof dashboardConfig === "object" && "company" in dashboardConfig ? dashboardConfig.company?.id : void 0);
|
|
5170
|
+
const fetchTimezone = async () => {
|
|
5171
|
+
setIsLoading(true);
|
|
5172
|
+
setError(null);
|
|
5173
|
+
try {
|
|
5174
|
+
let fetchedTimezone = null;
|
|
5175
|
+
if (lineId) {
|
|
5176
|
+
console.log(`[TimezoneContext] Fetching timezone for line: ${lineId}`);
|
|
5177
|
+
fetchedTimezone = await timezoneService.getTimezoneForLine(lineId, fallbackTimezone);
|
|
5178
|
+
} else if (workspaceId) {
|
|
5179
|
+
console.log(`[TimezoneContext] Fetching timezone for workspace: ${workspaceId}`);
|
|
5180
|
+
fetchedTimezone = await timezoneService.getTimezoneForWorkspace(workspaceId, fallbackTimezone);
|
|
5181
|
+
} else if (companyId) {
|
|
5182
|
+
console.log(`[TimezoneContext] Fetching timezone for company: ${companyId}`);
|
|
5183
|
+
fetchedTimezone = await timezoneService.getTimezoneForCompany(companyId, fallbackTimezone);
|
|
5184
|
+
} else {
|
|
5185
|
+
console.warn("[TimezoneContext] No ID available to fetch timezone, using fallback");
|
|
5186
|
+
fetchedTimezone = fallbackTimezone;
|
|
5187
|
+
}
|
|
5188
|
+
console.log(`[TimezoneContext] Timezone resolved to: ${fetchedTimezone}`);
|
|
5189
|
+
if (fetchedTimezone === fallbackTimezone) {
|
|
5190
|
+
console.warn(`[TimezoneContext] WARNING: Using fallback timezone ${fallbackTimezone}. This should not happen in production!`);
|
|
5191
|
+
}
|
|
5192
|
+
setTimezone(fetchedTimezone);
|
|
5193
|
+
} catch (err) {
|
|
5194
|
+
console.error("[TimezoneContext] Error fetching timezone:", err);
|
|
5195
|
+
setError(err instanceof Error ? err : new Error("Failed to fetch timezone"));
|
|
5196
|
+
console.error(`[TimezoneContext] CRITICAL: Failed to fetch timezone from database, falling back to ${fallbackTimezone}`);
|
|
5197
|
+
setTimezone(fallbackTimezone);
|
|
5198
|
+
} finally {
|
|
5199
|
+
setIsLoading(false);
|
|
5200
|
+
}
|
|
5201
|
+
};
|
|
5202
|
+
useEffect(() => {
|
|
5203
|
+
fetchTimezone();
|
|
5204
|
+
}, [lineId, workspaceId, companyId]);
|
|
5205
|
+
const value = {
|
|
5206
|
+
timezone,
|
|
5207
|
+
isLoading,
|
|
5208
|
+
error,
|
|
5209
|
+
refetch: fetchTimezone
|
|
5210
|
+
};
|
|
5211
|
+
return /* @__PURE__ */ jsx(TimezoneContext.Provider, { value, children });
|
|
5212
|
+
}
|
|
5213
|
+
function useTimezoneContext() {
|
|
5214
|
+
const context = useContext(TimezoneContext);
|
|
5215
|
+
if (!context) {
|
|
5216
|
+
throw new Error("useTimezoneContext must be used within TimezoneProvider");
|
|
5217
|
+
}
|
|
5218
|
+
return context;
|
|
5219
|
+
}
|
|
5220
|
+
function useAppTimezone() {
|
|
5221
|
+
const { timezone } = useTimezoneContext();
|
|
5222
|
+
return timezone;
|
|
5223
|
+
}
|
|
5224
|
+
function withTimezone(Component3) {
|
|
5225
|
+
return function WithTimezoneComponent(props) {
|
|
5226
|
+
const timezone = useAppTimezone();
|
|
5227
|
+
return /* @__PURE__ */ jsx(Component3, { ...props, timezone });
|
|
5228
|
+
};
|
|
5229
|
+
}
|
|
5932
5230
|
var DEFAULT_COMPANY_ID = "default-company-id";
|
|
5933
5231
|
var useWorkspaceMetrics = (workspaceId) => {
|
|
5934
5232
|
const supabase = useSupabase();
|
|
5935
5233
|
const entityConfig = useEntityConfig();
|
|
5936
5234
|
useDatabaseConfig();
|
|
5937
5235
|
const dateTimeConfig = useDateTimeConfig();
|
|
5236
|
+
const timezone = useAppTimezone();
|
|
5938
5237
|
const [workspaceMetrics, setWorkspaceMetrics] = useState(null);
|
|
5939
5238
|
const [isLoading, setIsLoading] = useState(true);
|
|
5940
5239
|
const [error, setError] = useState(null);
|
|
5941
5240
|
const fetchWorkspaceMetrics = useCallback(async () => {
|
|
5942
5241
|
try {
|
|
5943
|
-
const operationalDate = getOperationalDate(dateTimeConfig.defaultTimezone);
|
|
5242
|
+
const operationalDate = getOperationalDate(timezone || dateTimeConfig.defaultTimezone || "UTC");
|
|
5944
5243
|
const { data, error: fetchError } = await supabase.from("overview_workspace_metrics").select("*").eq("workspace_id", workspaceId).eq("date", operationalDate).single();
|
|
5945
5244
|
if (fetchError) throw fetchError;
|
|
5946
5245
|
setWorkspaceMetrics(data);
|
|
@@ -5953,7 +5252,7 @@ var useWorkspaceMetrics = (workspaceId) => {
|
|
|
5953
5252
|
}, [supabase, workspaceId, dateTimeConfig.defaultTimezone]);
|
|
5954
5253
|
useEffect(() => {
|
|
5955
5254
|
let channels = [];
|
|
5956
|
-
const operationalDate = getOperationalDate(dateTimeConfig.defaultTimezone);
|
|
5255
|
+
const operationalDate = getOperationalDate(timezone || dateTimeConfig.defaultTimezone || "UTC");
|
|
5957
5256
|
const setupSubscriptions = () => {
|
|
5958
5257
|
const companyId = entityConfig.companyId || DEFAULT_COMPANY_ID;
|
|
5959
5258
|
const metricsTablePrefix = getMetricsTablePrefix();
|
|
@@ -6004,12 +5303,13 @@ var useWorkspaceMetrics = (workspaceId) => {
|
|
|
6004
5303
|
var useLineMetrics = (lineId) => {
|
|
6005
5304
|
const supabase = useSupabase();
|
|
6006
5305
|
const dateTimeConfig = useDateTimeConfig();
|
|
5306
|
+
const timezone = useAppTimezone();
|
|
6007
5307
|
const [lineMetrics, setLineMetrics] = useState(null);
|
|
6008
5308
|
const [isLoading, setIsLoading] = useState(true);
|
|
6009
5309
|
const [error, setError] = useState(null);
|
|
6010
5310
|
const fetchLineMetrics = useCallback(async () => {
|
|
6011
5311
|
try {
|
|
6012
|
-
const operationalDate = getOperationalDate(dateTimeConfig.defaultTimezone);
|
|
5312
|
+
const operationalDate = getOperationalDate(timezone || dateTimeConfig.defaultTimezone || "UTC");
|
|
6013
5313
|
const { data, error: fetchError } = await supabase.from("overview_line_metrics").select("*").eq("line_id", lineId).eq("date", operationalDate).single();
|
|
6014
5314
|
if (fetchError) throw fetchError;
|
|
6015
5315
|
setLineMetrics(data);
|
|
@@ -6022,7 +5322,7 @@ var useLineMetrics = (lineId) => {
|
|
|
6022
5322
|
}, [supabase, lineId, dateTimeConfig.defaultTimezone]);
|
|
6023
5323
|
useEffect(() => {
|
|
6024
5324
|
let channels = [];
|
|
6025
|
-
const operationalDate = getOperationalDate(dateTimeConfig.defaultTimezone);
|
|
5325
|
+
const operationalDate = getOperationalDate(timezone || dateTimeConfig.defaultTimezone || "UTC");
|
|
6026
5326
|
const setupSubscriptions = () => {
|
|
6027
5327
|
const lineMetricsChannel = supabase.channel("line-base-metrics").on(
|
|
6028
5328
|
"postgres_changes",
|
|
@@ -6657,7 +5957,7 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
|
|
|
6657
5957
|
return;
|
|
6658
5958
|
}
|
|
6659
5959
|
const channels = [];
|
|
6660
|
-
const operationalDate = date || getOperationalDate();
|
|
5960
|
+
const operationalDate = date || getOperationalDate(defaultTimezone || "UTC");
|
|
6661
5961
|
const currentShift = getCurrentShift(defaultTimezone, shiftConfig);
|
|
6662
5962
|
const queryShiftId = shiftId ?? currentShift.shiftId;
|
|
6663
5963
|
const metricsChannel = supabase.channel(`workspace-metrics-${workspaceId}`).on(
|
|
@@ -6761,6 +6061,7 @@ var useLineWorkspaceMetrics = (lineId, options) => {
|
|
|
6761
6061
|
const databaseConfig = useDatabaseConfig();
|
|
6762
6062
|
const dateTimeConfig = useDateTimeConfig();
|
|
6763
6063
|
const shiftConfig = useShiftConfig();
|
|
6064
|
+
const timezone = useAppTimezone();
|
|
6764
6065
|
const supabase = useSupabase();
|
|
6765
6066
|
const [workspaces, setWorkspaces] = useState([]);
|
|
6766
6067
|
const [loading, setLoading] = useState(true);
|
|
@@ -6774,8 +6075,8 @@ var useLineWorkspaceMetrics = (lineId, options) => {
|
|
|
6774
6075
|
return options?.initialShiftId !== void 0 ? options.initialShiftId : currentShift.shiftId;
|
|
6775
6076
|
}, [options?.initialShiftId, dateTimeConfig.defaultTimezone, shiftConfig]);
|
|
6776
6077
|
const queryDate = useMemo(() => {
|
|
6777
|
-
return options?.initialDate || getOperationalDate(dateTimeConfig.defaultTimezone);
|
|
6778
|
-
}, [options?.initialDate, dateTimeConfig.defaultTimezone]);
|
|
6078
|
+
return options?.initialDate || getOperationalDate(timezone || dateTimeConfig.defaultTimezone || "UTC");
|
|
6079
|
+
}, [options?.initialDate, timezone, dateTimeConfig.defaultTimezone]);
|
|
6779
6080
|
const metricsTable = useMemo(() => {
|
|
6780
6081
|
const companyId = entityConfig.companyId;
|
|
6781
6082
|
if (!companyId) return "";
|
|
@@ -7562,7 +6863,8 @@ var useRealtimeLineMetrics = ({
|
|
|
7562
6863
|
() => urlShiftId !== void 0 ? urlShiftId : currentShift.shiftId,
|
|
7563
6864
|
[urlShiftId, currentShift.shiftId]
|
|
7564
6865
|
);
|
|
7565
|
-
const
|
|
6866
|
+
const timezone = useAppTimezone();
|
|
6867
|
+
const date = useMemo(() => urlDate || getOperationalDate(timezone || dateTimeConfig.defaultTimezone || "UTC"), [urlDate, timezone, dateTimeConfig.defaultTimezone]);
|
|
7566
6868
|
const fetchData = useCallback(async () => {
|
|
7567
6869
|
try {
|
|
7568
6870
|
if (!lineIdRef.current || isFetchingRef.current) return;
|
|
@@ -7802,7 +7104,7 @@ var useRealtimeLineMetrics = ({
|
|
|
7802
7104
|
if (process.env.NODE_ENV === "development") {
|
|
7803
7105
|
console.log("Line metrics update received:", payloadData);
|
|
7804
7106
|
}
|
|
7805
|
-
const currentDate = urlDate || getOperationalDate(dateTimeConfig.defaultTimezone);
|
|
7107
|
+
const currentDate = urlDate || getOperationalDate(timezone || dateTimeConfig.defaultTimezone || "UTC");
|
|
7806
7108
|
if (payloadData?.date === currentDate && payloadData?.shift_id === shiftId) {
|
|
7807
7109
|
queueUpdate();
|
|
7808
7110
|
}
|
|
@@ -7825,7 +7127,7 @@ var useRealtimeLineMetrics = ({
|
|
|
7825
7127
|
if (process.env.NODE_ENV === "development") {
|
|
7826
7128
|
console.log(`${metricsTablePrefix} update received:`, payloadData);
|
|
7827
7129
|
}
|
|
7828
|
-
const currentDate = urlDate || getOperationalDate(dateTimeConfig.defaultTimezone);
|
|
7130
|
+
const currentDate = urlDate || getOperationalDate(timezone || dateTimeConfig.defaultTimezone || "UTC");
|
|
7829
7131
|
if (payloadData?.date === currentDate && payloadData?.shift_id === shiftId) {
|
|
7830
7132
|
queueUpdate();
|
|
7831
7133
|
}
|
|
@@ -9140,6 +8442,7 @@ var useAllWorkspaceMetrics = (options) => {
|
|
|
9140
8442
|
const databaseConfig = useDatabaseConfig();
|
|
9141
8443
|
const dateTimeConfig = useDateTimeConfig();
|
|
9142
8444
|
const shiftConfig = useShiftConfig();
|
|
8445
|
+
const timezone = useAppTimezone();
|
|
9143
8446
|
const supabase = useSupabase();
|
|
9144
8447
|
const [workspaces, setWorkspaces] = useState([]);
|
|
9145
8448
|
const [loading, setLoading] = useState(true);
|
|
@@ -9153,8 +8456,8 @@ var useAllWorkspaceMetrics = (options) => {
|
|
|
9153
8456
|
return options?.initialShiftId !== void 0 ? options.initialShiftId : currentShift.shiftId;
|
|
9154
8457
|
}, [options?.initialShiftId, dateTimeConfig.defaultTimezone, shiftConfig]);
|
|
9155
8458
|
const queryDate = useMemo(() => {
|
|
9156
|
-
return options?.initialDate || getOperationalDate(dateTimeConfig.defaultTimezone);
|
|
9157
|
-
}, [options?.initialDate, dateTimeConfig.defaultTimezone]);
|
|
8459
|
+
return options?.initialDate || getOperationalDate(timezone || dateTimeConfig.defaultTimezone || "UTC");
|
|
8460
|
+
}, [options?.initialDate, timezone, dateTimeConfig.defaultTimezone]);
|
|
9158
8461
|
const metricsTable = useMemo(() => {
|
|
9159
8462
|
const companyId = entityConfig.companyId;
|
|
9160
8463
|
if (!companyId) return "";
|
|
@@ -13159,7 +12462,7 @@ var MotionConfigContext = createContext({
|
|
|
13159
12462
|
});
|
|
13160
12463
|
|
|
13161
12464
|
// ../../node_modules/framer-motion/dist/es/components/AnimatePresence/PopChild.mjs
|
|
13162
|
-
var PopChildMeasure = class extends
|
|
12465
|
+
var PopChildMeasure = class extends React21.Component {
|
|
13163
12466
|
getSnapshotBeforeUpdate(prevProps) {
|
|
13164
12467
|
const element = this.props.childRef.current;
|
|
13165
12468
|
if (element && prevProps.isPresent && !this.props.isPresent) {
|
|
@@ -13214,7 +12517,7 @@ function PopChild({ children, isPresent }) {
|
|
|
13214
12517
|
document.head.removeChild(style);
|
|
13215
12518
|
};
|
|
13216
12519
|
}, [isPresent]);
|
|
13217
|
-
return jsx(PopChildMeasure, { isPresent, childRef: ref, sizeRef: size, children:
|
|
12520
|
+
return jsx(PopChildMeasure, { isPresent, childRef: ref, sizeRef: size, children: React21.cloneElement(children, { ref }) });
|
|
13218
12521
|
}
|
|
13219
12522
|
|
|
13220
12523
|
// ../../node_modules/framer-motion/dist/es/components/AnimatePresence/PresenceChild.mjs
|
|
@@ -13251,7 +12554,7 @@ var PresenceChild = ({ children, initial, isPresent, onExitComplete, custom, pre
|
|
|
13251
12554
|
useMemo(() => {
|
|
13252
12555
|
presenceChildren.forEach((_, key) => presenceChildren.set(key, false));
|
|
13253
12556
|
}, [isPresent]);
|
|
13254
|
-
|
|
12557
|
+
React21.useEffect(() => {
|
|
13255
12558
|
!isPresent && !presenceChildren.size && onExitComplete && onExitComplete();
|
|
13256
12559
|
}, [isPresent]);
|
|
13257
12560
|
if (mode === "popLayout") {
|
|
@@ -20535,7 +19838,7 @@ var LoadingPage = ({
|
|
|
20535
19838
|
subMessage = "Please wait while we prepare your data",
|
|
20536
19839
|
className
|
|
20537
19840
|
}) => {
|
|
20538
|
-
|
|
19841
|
+
React21__default.useEffect(() => {
|
|
20539
19842
|
console.log("LoadingPage rendered with message:", message);
|
|
20540
19843
|
const timeout = setTimeout(() => {
|
|
20541
19844
|
console.warn("LoadingPage has been visible for more than 8 seconds. This might indicate an issue.");
|
|
@@ -20576,15 +19879,15 @@ var withAuth = (WrappedComponent2, options) => {
|
|
|
20576
19879
|
requireAuth: true,
|
|
20577
19880
|
...options
|
|
20578
19881
|
};
|
|
20579
|
-
const WithAuthComponent =
|
|
19882
|
+
const WithAuthComponent = React21.memo(function WithAuthComponent2(props) {
|
|
20580
19883
|
const { session, loading, error } = useAuth();
|
|
20581
19884
|
const router = useRouter();
|
|
20582
|
-
|
|
19885
|
+
React21.useEffect(() => {
|
|
20583
19886
|
if (process.env.NODE_ENV === "development" && process.env.DEBUG_AUTH === "true") {
|
|
20584
19887
|
console.log("withAuth state:", { loading, hasSession: !!session, requireAuth: defaultOptions.requireAuth });
|
|
20585
19888
|
}
|
|
20586
19889
|
}, [session, loading]);
|
|
20587
|
-
|
|
19890
|
+
React21.useEffect(() => {
|
|
20588
19891
|
if (!loading && defaultOptions.requireAuth && !session && !error) {
|
|
20589
19892
|
console.log("Redirecting to login from withAuth");
|
|
20590
19893
|
router.replace(defaultOptions.redirectTo);
|
|
@@ -20960,11 +20263,11 @@ var BarChartComponent = ({
|
|
|
20960
20263
|
aspect = 2,
|
|
20961
20264
|
...restOfChartProps
|
|
20962
20265
|
}) => {
|
|
20963
|
-
const containerRef =
|
|
20964
|
-
const [containerReady, setContainerReady] =
|
|
20266
|
+
const containerRef = React21__default.useRef(null);
|
|
20267
|
+
const [containerReady, setContainerReady] = React21__default.useState(false);
|
|
20965
20268
|
const themeConfig = useThemeConfig();
|
|
20966
20269
|
const { formatNumber } = useFormatNumber();
|
|
20967
|
-
|
|
20270
|
+
React21__default.useEffect(() => {
|
|
20968
20271
|
const checkContainerDimensions = () => {
|
|
20969
20272
|
if (containerRef.current) {
|
|
20970
20273
|
const rect = containerRef.current.getBoundingClientRect();
|
|
@@ -21062,7 +20365,7 @@ var BarChartComponent = ({
|
|
|
21062
20365
|
}
|
|
21063
20366
|
return /* @__PURE__ */ jsx("div", { className: clsx("w-full", className), children: chartContent });
|
|
21064
20367
|
};
|
|
21065
|
-
var BarChart =
|
|
20368
|
+
var BarChart = React21__default.memo(BarChartComponent, (prevProps, nextProps) => {
|
|
21066
20369
|
if (prevProps.xAxisDataKey !== nextProps.xAxisDataKey || prevProps.xAxisLabel !== nextProps.xAxisLabel || prevProps.yAxisLabel !== nextProps.yAxisLabel || prevProps.yAxisUnit !== nextProps.yAxisUnit || prevProps.layout !== nextProps.layout || prevProps.className !== nextProps.className || prevProps.showGrid !== nextProps.showGrid || prevProps.showLegend !== nextProps.showLegend || prevProps.showTooltip !== nextProps.showTooltip || prevProps.responsive !== nextProps.responsive || prevProps.aspect !== nextProps.aspect) {
|
|
21067
20370
|
return false;
|
|
21068
20371
|
}
|
|
@@ -21110,11 +20413,11 @@ var LineChartComponent = ({
|
|
|
21110
20413
|
aspect = 2,
|
|
21111
20414
|
...restOfChartProps
|
|
21112
20415
|
}) => {
|
|
21113
|
-
const containerRef =
|
|
21114
|
-
const [containerReady, setContainerReady] =
|
|
20416
|
+
const containerRef = React21__default.useRef(null);
|
|
20417
|
+
const [containerReady, setContainerReady] = React21__default.useState(false);
|
|
21115
20418
|
const themeConfig = useThemeConfig();
|
|
21116
20419
|
const { formatNumber } = useFormatNumber();
|
|
21117
|
-
|
|
20420
|
+
React21__default.useEffect(() => {
|
|
21118
20421
|
const checkContainerDimensions = () => {
|
|
21119
20422
|
if (containerRef.current) {
|
|
21120
20423
|
const rect = containerRef.current.getBoundingClientRect();
|
|
@@ -21213,7 +20516,7 @@ var LineChartComponent = ({
|
|
|
21213
20516
|
}
|
|
21214
20517
|
return /* @__PURE__ */ jsx("div", { className: clsx("w-full", className), children: chartContent });
|
|
21215
20518
|
};
|
|
21216
|
-
var LineChart =
|
|
20519
|
+
var LineChart = React21__default.memo(LineChartComponent, (prevProps, nextProps) => {
|
|
21217
20520
|
if (prevProps.xAxisDataKey !== nextProps.xAxisDataKey || prevProps.xAxisLabel !== nextProps.xAxisLabel || prevProps.yAxisLabel !== nextProps.yAxisLabel || prevProps.yAxisUnit !== nextProps.yAxisUnit || prevProps.className !== nextProps.className || prevProps.showGrid !== nextProps.showGrid || prevProps.showLegend !== nextProps.showLegend || prevProps.showTooltip !== nextProps.showTooltip || prevProps.responsive !== nextProps.responsive || prevProps.aspect !== nextProps.aspect || JSON.stringify(prevProps.yAxisDomain) !== JSON.stringify(nextProps.yAxisDomain)) {
|
|
21218
20521
|
return false;
|
|
21219
20522
|
}
|
|
@@ -21290,7 +20593,7 @@ var OutputProgressChartComponent = ({
|
|
|
21290
20593
|
] }) })
|
|
21291
20594
|
] }) });
|
|
21292
20595
|
};
|
|
21293
|
-
var OutputProgressChart =
|
|
20596
|
+
var OutputProgressChart = React21__default.memo(OutputProgressChartComponent);
|
|
21294
20597
|
OutputProgressChart.displayName = "OutputProgressChart";
|
|
21295
20598
|
var LargeOutputProgressChart = ({
|
|
21296
20599
|
currentOutput,
|
|
@@ -21406,7 +20709,7 @@ var CycleTimeChartComponent = ({
|
|
|
21406
20709
|
}
|
|
21407
20710
|
) }) });
|
|
21408
20711
|
};
|
|
21409
|
-
var CycleTimeChart =
|
|
20712
|
+
var CycleTimeChart = React21__default.memo(CycleTimeChartComponent, (prevProps, nextProps) => {
|
|
21410
20713
|
if (prevProps.className !== nextProps.className) {
|
|
21411
20714
|
return false;
|
|
21412
20715
|
}
|
|
@@ -21432,8 +20735,8 @@ var CycleTimeOverTimeChart = ({
|
|
|
21432
20735
|
className = ""
|
|
21433
20736
|
}) => {
|
|
21434
20737
|
const MAX_DATA_POINTS = 40;
|
|
21435
|
-
const containerRef =
|
|
21436
|
-
const [containerReady, setContainerReady] =
|
|
20738
|
+
const containerRef = React21__default.useRef(null);
|
|
20739
|
+
const [containerReady, setContainerReady] = React21__default.useState(false);
|
|
21437
20740
|
const getHourFromTimeString = (timeStr) => {
|
|
21438
20741
|
const [hours, minutes] = timeStr.split(":");
|
|
21439
20742
|
return parseInt(hours);
|
|
@@ -21444,10 +20747,10 @@ var CycleTimeOverTimeChart = ({
|
|
|
21444
20747
|
};
|
|
21445
20748
|
const displayData = getDisplayData(data);
|
|
21446
20749
|
const DURATION = displayData.length;
|
|
21447
|
-
const [animatedData, setAnimatedData] =
|
|
21448
|
-
const prevDataRef =
|
|
21449
|
-
const animationFrameRef =
|
|
21450
|
-
const animateToNewData =
|
|
20750
|
+
const [animatedData, setAnimatedData] = React21__default.useState(Array(DURATION).fill(0));
|
|
20751
|
+
const prevDataRef = React21__default.useRef(Array(DURATION).fill(0));
|
|
20752
|
+
const animationFrameRef = React21__default.useRef(null);
|
|
20753
|
+
const animateToNewData = React21__default.useCallback((targetData) => {
|
|
21451
20754
|
const startData = [...prevDataRef.current];
|
|
21452
20755
|
const startTime = performance.now();
|
|
21453
20756
|
const duration = 1200;
|
|
@@ -21477,7 +20780,7 @@ var CycleTimeOverTimeChart = ({
|
|
|
21477
20780
|
}
|
|
21478
20781
|
animationFrameRef.current = requestAnimationFrame(animate);
|
|
21479
20782
|
}, []);
|
|
21480
|
-
|
|
20783
|
+
React21__default.useEffect(() => {
|
|
21481
20784
|
if (JSON.stringify(data) !== JSON.stringify(prevDataRef.current)) {
|
|
21482
20785
|
const processedData = getDisplayData(data);
|
|
21483
20786
|
animateToNewData(processedData);
|
|
@@ -21488,7 +20791,7 @@ var CycleTimeOverTimeChart = ({
|
|
|
21488
20791
|
}
|
|
21489
20792
|
};
|
|
21490
20793
|
}, [data, animateToNewData]);
|
|
21491
|
-
|
|
20794
|
+
React21__default.useEffect(() => {
|
|
21492
20795
|
const checkContainerDimensions = () => {
|
|
21493
20796
|
if (containerRef.current) {
|
|
21494
20797
|
const rect = containerRef.current.getBoundingClientRect();
|
|
@@ -21731,7 +21034,7 @@ var CycleTimeOverTimeChart = ({
|
|
|
21731
21034
|
}
|
|
21732
21035
|
);
|
|
21733
21036
|
};
|
|
21734
|
-
var Card =
|
|
21037
|
+
var Card = React21.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
21735
21038
|
"div",
|
|
21736
21039
|
{
|
|
21737
21040
|
ref,
|
|
@@ -21743,7 +21046,7 @@ var Card = React20.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */
|
|
|
21743
21046
|
}
|
|
21744
21047
|
));
|
|
21745
21048
|
Card.displayName = "Card";
|
|
21746
|
-
var CardHeader =
|
|
21049
|
+
var CardHeader = React21.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
21747
21050
|
"div",
|
|
21748
21051
|
{
|
|
21749
21052
|
ref,
|
|
@@ -21752,7 +21055,7 @@ var CardHeader = React20.forwardRef(({ className, ...props }, ref) => /* @__PURE
|
|
|
21752
21055
|
}
|
|
21753
21056
|
));
|
|
21754
21057
|
CardHeader.displayName = "CardHeader";
|
|
21755
|
-
var CardTitle =
|
|
21058
|
+
var CardTitle = React21.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
21756
21059
|
"h3",
|
|
21757
21060
|
{
|
|
21758
21061
|
ref,
|
|
@@ -21764,7 +21067,7 @@ var CardTitle = React20.forwardRef(({ className, ...props }, ref) => /* @__PURE_
|
|
|
21764
21067
|
}
|
|
21765
21068
|
));
|
|
21766
21069
|
CardTitle.displayName = "CardTitle";
|
|
21767
|
-
var CardDescription =
|
|
21070
|
+
var CardDescription = React21.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
21768
21071
|
"p",
|
|
21769
21072
|
{
|
|
21770
21073
|
ref,
|
|
@@ -21773,9 +21076,9 @@ var CardDescription = React20.forwardRef(({ className, ...props }, ref) => /* @_
|
|
|
21773
21076
|
}
|
|
21774
21077
|
));
|
|
21775
21078
|
CardDescription.displayName = "CardDescription";
|
|
21776
|
-
var CardContent =
|
|
21079
|
+
var CardContent = React21.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("p-6 pt-0", className), ...props }));
|
|
21777
21080
|
CardContent.displayName = "CardContent";
|
|
21778
|
-
var CardFooter =
|
|
21081
|
+
var CardFooter = React21.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
21779
21082
|
"div",
|
|
21780
21083
|
{
|
|
21781
21084
|
ref,
|
|
@@ -21851,7 +21154,7 @@ var buttonVariants = cva(
|
|
|
21851
21154
|
}
|
|
21852
21155
|
}
|
|
21853
21156
|
);
|
|
21854
|
-
var Button =
|
|
21157
|
+
var Button = React21.forwardRef(
|
|
21855
21158
|
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
21856
21159
|
const Comp = asChild ? Slot : "button";
|
|
21857
21160
|
return /* @__PURE__ */ jsx(
|
|
@@ -21874,8 +21177,8 @@ var HourlyOutputChartComponent = ({
|
|
|
21874
21177
|
idleTimeHourly,
|
|
21875
21178
|
className = ""
|
|
21876
21179
|
}) => {
|
|
21877
|
-
const containerRef =
|
|
21878
|
-
const [containerReady, setContainerReady] =
|
|
21180
|
+
const containerRef = React21__default.useRef(null);
|
|
21181
|
+
const [containerReady, setContainerReady] = React21__default.useState(false);
|
|
21879
21182
|
const getTimeFromTimeString = (timeStr) => {
|
|
21880
21183
|
const [hours, minutes] = timeStr.split(":");
|
|
21881
21184
|
const hour = parseInt(hours);
|
|
@@ -21884,7 +21187,7 @@ var HourlyOutputChartComponent = ({
|
|
|
21884
21187
|
return { hour, minute, decimalHour };
|
|
21885
21188
|
};
|
|
21886
21189
|
const shiftStartTime = getTimeFromTimeString(shiftStart);
|
|
21887
|
-
const { shiftDuration, shiftEndTime, hasPartialLastHour } =
|
|
21190
|
+
const { shiftDuration, shiftEndTime, hasPartialLastHour } = React21__default.useMemo(() => {
|
|
21888
21191
|
console.log("[HourlyOutputChart] Calculating shift duration with:", {
|
|
21889
21192
|
shiftStart,
|
|
21890
21193
|
shiftEnd,
|
|
@@ -21919,12 +21222,12 @@ var HourlyOutputChartComponent = ({
|
|
|
21919
21222
|
}, [shiftEnd, shiftStartTime.decimalHour]);
|
|
21920
21223
|
const SHIFT_DURATION = shiftDuration;
|
|
21921
21224
|
shiftEndTime ? shiftEndTime.hour : (shiftStartTime.hour + SHIFT_DURATION) % 24;
|
|
21922
|
-
const [animatedData, setAnimatedData] =
|
|
21225
|
+
const [animatedData, setAnimatedData] = React21__default.useState(
|
|
21923
21226
|
() => Array(SHIFT_DURATION).fill(0)
|
|
21924
21227
|
);
|
|
21925
|
-
const prevDataRef =
|
|
21926
|
-
const animationFrameRef =
|
|
21927
|
-
|
|
21228
|
+
const prevDataRef = React21__default.useRef(Array(SHIFT_DURATION).fill(0));
|
|
21229
|
+
const animationFrameRef = React21__default.useRef(null);
|
|
21230
|
+
React21__default.useEffect(() => {
|
|
21928
21231
|
setAnimatedData((prev) => {
|
|
21929
21232
|
if (prev.length !== SHIFT_DURATION) {
|
|
21930
21233
|
return Array(SHIFT_DURATION).fill(0);
|
|
@@ -21933,14 +21236,14 @@ var HourlyOutputChartComponent = ({
|
|
|
21933
21236
|
});
|
|
21934
21237
|
prevDataRef.current = Array(SHIFT_DURATION).fill(0);
|
|
21935
21238
|
}, [SHIFT_DURATION]);
|
|
21936
|
-
const [idleBarState, setIdleBarState] =
|
|
21239
|
+
const [idleBarState, setIdleBarState] = React21__default.useState({
|
|
21937
21240
|
visible: showIdleTime,
|
|
21938
21241
|
key: 0,
|
|
21939
21242
|
shouldAnimate: false
|
|
21940
21243
|
});
|
|
21941
|
-
const prevShowIdleTimeRef =
|
|
21942
|
-
const stateUpdateTimeoutRef =
|
|
21943
|
-
|
|
21244
|
+
const prevShowIdleTimeRef = React21__default.useRef(showIdleTime);
|
|
21245
|
+
const stateUpdateTimeoutRef = React21__default.useRef(null);
|
|
21246
|
+
React21__default.useEffect(() => {
|
|
21944
21247
|
if (stateUpdateTimeoutRef.current) {
|
|
21945
21248
|
clearTimeout(stateUpdateTimeoutRef.current);
|
|
21946
21249
|
}
|
|
@@ -21965,7 +21268,7 @@ var HourlyOutputChartComponent = ({
|
|
|
21965
21268
|
}
|
|
21966
21269
|
};
|
|
21967
21270
|
}, [showIdleTime]);
|
|
21968
|
-
const animateToNewData =
|
|
21271
|
+
const animateToNewData = React21__default.useCallback((targetData) => {
|
|
21969
21272
|
const startData = [...prevDataRef.current];
|
|
21970
21273
|
const startTime = performance.now();
|
|
21971
21274
|
const duration = 1200;
|
|
@@ -21995,7 +21298,7 @@ var HourlyOutputChartComponent = ({
|
|
|
21995
21298
|
}
|
|
21996
21299
|
animationFrameRef.current = requestAnimationFrame(animate);
|
|
21997
21300
|
}, []);
|
|
21998
|
-
|
|
21301
|
+
React21__default.useEffect(() => {
|
|
21999
21302
|
if (JSON.stringify(data) !== JSON.stringify(prevDataRef.current)) {
|
|
22000
21303
|
const shiftData = data.slice(0, SHIFT_DURATION);
|
|
22001
21304
|
animateToNewData(shiftData);
|
|
@@ -22006,7 +21309,7 @@ var HourlyOutputChartComponent = ({
|
|
|
22006
21309
|
}
|
|
22007
21310
|
};
|
|
22008
21311
|
}, [data, animateToNewData]);
|
|
22009
|
-
|
|
21312
|
+
React21__default.useEffect(() => {
|
|
22010
21313
|
const checkContainerDimensions = () => {
|
|
22011
21314
|
if (containerRef.current) {
|
|
22012
21315
|
const rect = containerRef.current.getBoundingClientRect();
|
|
@@ -22028,7 +21331,7 @@ var HourlyOutputChartComponent = ({
|
|
|
22028
21331
|
clearTimeout(fallbackTimeout);
|
|
22029
21332
|
};
|
|
22030
21333
|
}, []);
|
|
22031
|
-
const formatHour =
|
|
21334
|
+
const formatHour = React21__default.useCallback((hourIndex) => {
|
|
22032
21335
|
const isLastHour = hourIndex === SHIFT_DURATION - 1;
|
|
22033
21336
|
const startDecimalHour = shiftStartTime.decimalHour + hourIndex;
|
|
22034
21337
|
const startHour = Math.floor(startDecimalHour) % 24;
|
|
@@ -22052,7 +21355,7 @@ var HourlyOutputChartComponent = ({
|
|
|
22052
21355
|
};
|
|
22053
21356
|
return `${formatTime3(startHour, startMinute)}-${formatTime3(endHour, endMinute)}`;
|
|
22054
21357
|
}, [shiftStartTime.decimalHour, SHIFT_DURATION, shiftEndTime]);
|
|
22055
|
-
const formatTimeRange =
|
|
21358
|
+
const formatTimeRange = React21__default.useCallback((hourIndex) => {
|
|
22056
21359
|
const isLastHour = hourIndex === SHIFT_DURATION - 1;
|
|
22057
21360
|
const startDecimalHour = shiftStartTime.decimalHour + hourIndex;
|
|
22058
21361
|
const startHour = Math.floor(startDecimalHour) % 24;
|
|
@@ -22073,7 +21376,7 @@ var HourlyOutputChartComponent = ({
|
|
|
22073
21376
|
};
|
|
22074
21377
|
return `${formatTime3(startHour, startMinute)} - ${formatTime3(endHour, endMinute)}`;
|
|
22075
21378
|
}, [shiftStartTime.decimalHour, SHIFT_DURATION, shiftEndTime]);
|
|
22076
|
-
const chartData =
|
|
21379
|
+
const chartData = React21__default.useMemo(() => {
|
|
22077
21380
|
return Array.from({ length: SHIFT_DURATION }, (_, i) => {
|
|
22078
21381
|
const actualHour = (shiftStartTime.hour + i) % 24;
|
|
22079
21382
|
const startMinute = shiftStartTime.minute;
|
|
@@ -22142,7 +21445,7 @@ var HourlyOutputChartComponent = ({
|
|
|
22142
21445
|
};
|
|
22143
21446
|
});
|
|
22144
21447
|
}, [animatedData, data, pphThreshold, idleTimeHourly, shiftStartTime.hour, shiftStartTime.minute, shiftEndTime, formatHour, formatTimeRange, SHIFT_DURATION]);
|
|
22145
|
-
const IdleBar =
|
|
21448
|
+
const IdleBar = React21__default.useMemo(() => {
|
|
22146
21449
|
if (!idleBarState.visible) return null;
|
|
22147
21450
|
return /* @__PURE__ */ jsx(
|
|
22148
21451
|
Bar,
|
|
@@ -22467,7 +21770,7 @@ var HourlyOutputChartComponent = ({
|
|
|
22467
21770
|
}
|
|
22468
21771
|
);
|
|
22469
21772
|
};
|
|
22470
|
-
var HourlyOutputChart =
|
|
21773
|
+
var HourlyOutputChart = React21__default.memo(HourlyOutputChartComponent, (prevProps, nextProps) => {
|
|
22471
21774
|
if (prevProps.pphThreshold !== nextProps.pphThreshold || prevProps.shiftStart !== nextProps.shiftStart || prevProps.showIdleTime !== nextProps.showIdleTime || prevProps.className !== nextProps.className) {
|
|
22472
21775
|
return false;
|
|
22473
21776
|
}
|
|
@@ -22503,7 +21806,7 @@ function getTrendArrowAndColor(trend) {
|
|
|
22503
21806
|
return { arrow: "\u2192", color: "text-gray-400" };
|
|
22504
21807
|
}
|
|
22505
21808
|
}
|
|
22506
|
-
var VideoCard =
|
|
21809
|
+
var VideoCard = React21__default.memo(({
|
|
22507
21810
|
workspace,
|
|
22508
21811
|
hlsUrl,
|
|
22509
21812
|
shouldPlay,
|
|
@@ -22652,7 +21955,7 @@ var VideoCard = React20__default.memo(({
|
|
|
22652
21955
|
});
|
|
22653
21956
|
VideoCard.displayName = "VideoCard";
|
|
22654
21957
|
var DEFAULT_HLS_URL = "https://192.168.5.9:8443/cam1.m3u8";
|
|
22655
|
-
var VideoGridView =
|
|
21958
|
+
var VideoGridView = React21__default.memo(({
|
|
22656
21959
|
workspaces,
|
|
22657
21960
|
selectedLine,
|
|
22658
21961
|
className = "",
|
|
@@ -22805,32 +22108,10 @@ var VideoGridView = React20__default.memo(({
|
|
|
22805
22108
|
observerRef.current?.disconnect();
|
|
22806
22109
|
};
|
|
22807
22110
|
}, [filteredWorkspaces]);
|
|
22808
|
-
|
|
22111
|
+
useRef({});
|
|
22809
22112
|
const handleWorkspaceClick = useCallback((workspace) => {
|
|
22810
22113
|
const workspaceId = workspace.workspace_uuid || workspace.workspace_name;
|
|
22811
|
-
|
|
22812
|
-
const operationalDate = getOperationalDate(dashboardConfig.dateTimeConfig?.defaultTimezone);
|
|
22813
|
-
const fullKey = `${workspaceId}-${operationalDate}-all-all-meta-1000--`;
|
|
22814
|
-
if (!prefetchCacheRef.current[fullKey]?.status) {
|
|
22815
|
-
const clipsService = new S3ClipsService(dashboardConfig);
|
|
22816
|
-
const fullPromise = clipsService.fetchClips({
|
|
22817
|
-
workspaceId,
|
|
22818
|
-
date: operationalDate,
|
|
22819
|
-
mode: "full",
|
|
22820
|
-
includeCycleTime: true,
|
|
22821
|
-
includeMetadata: true,
|
|
22822
|
-
limit: 1e3
|
|
22823
|
-
});
|
|
22824
|
-
prefetchCacheRef.current[fullKey] = { status: "pending", promise: fullPromise };
|
|
22825
|
-
fullPromise.then((data) => {
|
|
22826
|
-
prefetchCacheRef.current[fullKey] = { status: "resolved", data };
|
|
22827
|
-
console.log(`Prefetched full clips data for workspace ${workspaceId}`);
|
|
22828
|
-
}).catch((error) => {
|
|
22829
|
-
prefetchCacheRef.current[fullKey] = { status: "rejected", error };
|
|
22830
|
-
console.warn(`Failed to prefetch full clips for workspace ${workspaceId}:`, error);
|
|
22831
|
-
});
|
|
22832
|
-
}
|
|
22833
|
-
}
|
|
22114
|
+
console.log(`[VideoGridView] Prefetching disabled for workspace ${workspaceId}`);
|
|
22834
22115
|
trackCoreEvent("Workspace Detail Clicked", {
|
|
22835
22116
|
workspace_name: workspace.workspace_name,
|
|
22836
22117
|
workspace_id: workspaceId,
|
|
@@ -23589,7 +22870,7 @@ var EmptyStateMessage = ({
|
|
|
23589
22870
|
iconClassName
|
|
23590
22871
|
}) => {
|
|
23591
22872
|
let IconContent = null;
|
|
23592
|
-
if (
|
|
22873
|
+
if (React21__default.isValidElement(iconType)) {
|
|
23593
22874
|
IconContent = iconType;
|
|
23594
22875
|
} else if (typeof iconType === "string") {
|
|
23595
22876
|
const MappedIcon = IconMap[iconType];
|
|
@@ -24408,7 +23689,8 @@ var ShiftDisplay_default = ShiftDisplay;
|
|
|
24408
23689
|
var TimeDisplay = ({ className, variant = "default" }) => {
|
|
24409
23690
|
const { dateTimeConfig } = useDashboardConfig();
|
|
24410
23691
|
const [time2, setTime] = useState("");
|
|
24411
|
-
const
|
|
23692
|
+
const dbTimezone = useAppTimezone();
|
|
23693
|
+
const timezoneToDisplay = dbTimezone || dateTimeConfig?.defaultTimezone || "UTC";
|
|
24412
23694
|
const localeToUse = dateTimeConfig?.defaultLocale || "en-US";
|
|
24413
23695
|
const timeSuffix = "";
|
|
24414
23696
|
useEffect(() => {
|
|
@@ -24472,6 +23754,17 @@ var ISTTimer = memo(() => {
|
|
|
24472
23754
|
});
|
|
24473
23755
|
ISTTimer.displayName = "ISTTimer";
|
|
24474
23756
|
var ISTTimer_default = ISTTimer;
|
|
23757
|
+
var TimeDisplay3 = TimeDisplay_default;
|
|
23758
|
+
var Timer = memo(() => {
|
|
23759
|
+
return /* @__PURE__ */ jsx(
|
|
23760
|
+
TimeDisplay3,
|
|
23761
|
+
{
|
|
23762
|
+
variant: "minimal"
|
|
23763
|
+
}
|
|
23764
|
+
);
|
|
23765
|
+
});
|
|
23766
|
+
Timer.displayName = "Timer";
|
|
23767
|
+
var Timer_default = Timer;
|
|
24475
23768
|
var TicketHistory = ({ companyId }) => {
|
|
24476
23769
|
const { tickets, loading, error } = useTicketHistory(companyId);
|
|
24477
23770
|
const [expandedTickets, setExpandedTickets] = useState(/* @__PURE__ */ new Set());
|
|
@@ -27569,8 +26862,9 @@ var LiveTimer = () => {
|
|
|
27569
26862
|
}, 1e3);
|
|
27570
26863
|
return () => clearInterval(timer);
|
|
27571
26864
|
}, []);
|
|
27572
|
-
const
|
|
27573
|
-
|
|
26865
|
+
const timezone = useAppTimezone();
|
|
26866
|
+
const formatter = new Intl.DateTimeFormat("en-US", {
|
|
26867
|
+
timeZone: timezone,
|
|
27574
26868
|
hour: "2-digit",
|
|
27575
26869
|
minute: "2-digit",
|
|
27576
26870
|
second: "2-digit",
|
|
@@ -27604,7 +26898,7 @@ function Skeleton({ className, ...props }) {
|
|
|
27604
26898
|
var Select = SelectPrimitive.Root;
|
|
27605
26899
|
var SelectGroup = SelectPrimitive.Group;
|
|
27606
26900
|
var SelectValue = SelectPrimitive.Value;
|
|
27607
|
-
var SelectTrigger =
|
|
26901
|
+
var SelectTrigger = React21.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
27608
26902
|
SelectPrimitive.Trigger,
|
|
27609
26903
|
{
|
|
27610
26904
|
ref,
|
|
@@ -27620,7 +26914,7 @@ var SelectTrigger = React20.forwardRef(({ className, children, ...props }, ref)
|
|
|
27620
26914
|
}
|
|
27621
26915
|
));
|
|
27622
26916
|
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
|
|
27623
|
-
var SelectScrollUpButton =
|
|
26917
|
+
var SelectScrollUpButton = React21.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
27624
26918
|
SelectPrimitive.ScrollUpButton,
|
|
27625
26919
|
{
|
|
27626
26920
|
ref,
|
|
@@ -27630,7 +26924,7 @@ var SelectScrollUpButton = React20.forwardRef(({ className, ...props }, ref) =>
|
|
|
27630
26924
|
}
|
|
27631
26925
|
));
|
|
27632
26926
|
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
|
|
27633
|
-
var SelectScrollDownButton =
|
|
26927
|
+
var SelectScrollDownButton = React21.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
27634
26928
|
SelectPrimitive.ScrollDownButton,
|
|
27635
26929
|
{
|
|
27636
26930
|
ref,
|
|
@@ -27640,7 +26934,7 @@ var SelectScrollDownButton = React20.forwardRef(({ className, ...props }, ref) =
|
|
|
27640
26934
|
}
|
|
27641
26935
|
));
|
|
27642
26936
|
SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
|
|
27643
|
-
var SelectContent =
|
|
26937
|
+
var SelectContent = React21.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
|
|
27644
26938
|
SelectPrimitive.Content,
|
|
27645
26939
|
{
|
|
27646
26940
|
ref,
|
|
@@ -27668,7 +26962,7 @@ var SelectContent = React20.forwardRef(({ className, children, position = "poppe
|
|
|
27668
26962
|
}
|
|
27669
26963
|
) }));
|
|
27670
26964
|
SelectContent.displayName = SelectPrimitive.Content.displayName;
|
|
27671
|
-
var SelectLabel =
|
|
26965
|
+
var SelectLabel = React21.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
27672
26966
|
SelectPrimitive.Label,
|
|
27673
26967
|
{
|
|
27674
26968
|
ref,
|
|
@@ -27677,7 +26971,7 @@ var SelectLabel = React20.forwardRef(({ className, ...props }, ref) => /* @__PUR
|
|
|
27677
26971
|
}
|
|
27678
26972
|
));
|
|
27679
26973
|
SelectLabel.displayName = SelectPrimitive.Label.displayName;
|
|
27680
|
-
var SelectItem =
|
|
26974
|
+
var SelectItem = React21.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
27681
26975
|
SelectPrimitive.Item,
|
|
27682
26976
|
{
|
|
27683
26977
|
ref,
|
|
@@ -27693,7 +26987,7 @@ var SelectItem = React20.forwardRef(({ className, children, ...props }, ref) =>
|
|
|
27693
26987
|
}
|
|
27694
26988
|
));
|
|
27695
26989
|
SelectItem.displayName = SelectPrimitive.Item.displayName;
|
|
27696
|
-
var SelectSeparator =
|
|
26990
|
+
var SelectSeparator = React21.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
27697
26991
|
SelectPrimitive.Separator,
|
|
27698
26992
|
{
|
|
27699
26993
|
ref,
|
|
@@ -27986,7 +27280,7 @@ if (typeof document !== "undefined") {
|
|
|
27986
27280
|
document.head.appendChild(style);
|
|
27987
27281
|
}
|
|
27988
27282
|
}
|
|
27989
|
-
var VideoPlayer =
|
|
27283
|
+
var VideoPlayer = React21__default.forwardRef(({
|
|
27990
27284
|
src,
|
|
27991
27285
|
poster,
|
|
27992
27286
|
autoplay = false,
|
|
@@ -27996,15 +27290,19 @@ var VideoPlayer = React20__default.forwardRef(({
|
|
|
27996
27290
|
playsInline = true,
|
|
27997
27291
|
className = "",
|
|
27998
27292
|
options = {},
|
|
27293
|
+
externalLoadingControl = false,
|
|
27294
|
+
onLoadingChange,
|
|
27999
27295
|
onReady,
|
|
28000
27296
|
onPlay,
|
|
28001
27297
|
onPause,
|
|
27298
|
+
onPlaying,
|
|
28002
27299
|
onTimeUpdate,
|
|
28003
27300
|
onDurationChange,
|
|
28004
27301
|
onEnded,
|
|
28005
27302
|
onError,
|
|
28006
27303
|
onLoadStart,
|
|
28007
27304
|
onLoadedMetadata,
|
|
27305
|
+
onLoadedData,
|
|
28008
27306
|
onSeeking,
|
|
28009
27307
|
onSeeked
|
|
28010
27308
|
}, ref) => {
|
|
@@ -28126,6 +27424,7 @@ var VideoPlayer = React20__default.forwardRef(({
|
|
|
28126
27424
|
});
|
|
28127
27425
|
player.on("play", () => onPlay?.(player));
|
|
28128
27426
|
player.on("pause", () => onPause?.(player));
|
|
27427
|
+
player.on("playing", () => onPlaying?.(player));
|
|
28129
27428
|
player.on("timeupdate", () => {
|
|
28130
27429
|
const currentTime2 = player.currentTime() || 0;
|
|
28131
27430
|
onTimeUpdate?.(player, currentTime2);
|
|
@@ -28137,16 +27436,21 @@ var VideoPlayer = React20__default.forwardRef(({
|
|
|
28137
27436
|
player.on("ended", () => onEnded?.(player));
|
|
28138
27437
|
player.on("loadstart", () => {
|
|
28139
27438
|
setIsLoading(true);
|
|
27439
|
+
onLoadingChange?.(true);
|
|
28140
27440
|
onLoadStart?.(player);
|
|
28141
27441
|
});
|
|
28142
27442
|
player.on("loadeddata", () => {
|
|
28143
27443
|
setIsLoading(false);
|
|
27444
|
+
onLoadingChange?.(false);
|
|
27445
|
+
onLoadedData?.(player);
|
|
28144
27446
|
});
|
|
28145
27447
|
player.on("waiting", () => {
|
|
28146
27448
|
setIsLoading(true);
|
|
27449
|
+
onLoadingChange?.(true);
|
|
28147
27450
|
});
|
|
28148
27451
|
player.on("playing", () => {
|
|
28149
27452
|
setIsLoading(false);
|
|
27453
|
+
onLoadingChange?.(false);
|
|
28150
27454
|
});
|
|
28151
27455
|
player.on("loadedmetadata", () => {
|
|
28152
27456
|
onLoadedMetadata?.(player);
|
|
@@ -28271,7 +27575,7 @@ var VideoPlayer = React20__default.forwardRef(({
|
|
|
28271
27575
|
setIsReady(false);
|
|
28272
27576
|
}
|
|
28273
27577
|
}, []);
|
|
28274
|
-
|
|
27578
|
+
React21__default.useImperativeHandle(ref, () => ({
|
|
28275
27579
|
player: playerRef.current,
|
|
28276
27580
|
play,
|
|
28277
27581
|
pause,
|
|
@@ -28292,7 +27596,7 @@ var VideoPlayer = React20__default.forwardRef(({
|
|
|
28292
27596
|
"data-vjs-player": true
|
|
28293
27597
|
}
|
|
28294
27598
|
),
|
|
28295
|
-
isLoading && /* @__PURE__ */ jsx("div", { className: "video-player-loading", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) })
|
|
27599
|
+
isLoading && !externalLoadingControl && /* @__PURE__ */ jsx("div", { className: "video-player-loading", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) })
|
|
28296
27600
|
] });
|
|
28297
27601
|
});
|
|
28298
27602
|
VideoPlayer.displayName = "VideoPlayer";
|
|
@@ -28474,7 +27778,10 @@ var CroppedVideoPlayer = forwardRef(({
|
|
|
28474
27778
|
onEnded: handleVideoEnded,
|
|
28475
27779
|
onSeeking: handleSeeking,
|
|
28476
27780
|
onSeeked: handleSeeked,
|
|
28477
|
-
onLoadedMetadata: handleLoadedMetadata
|
|
27781
|
+
onLoadedMetadata: handleLoadedMetadata,
|
|
27782
|
+
onLoadedData: videoProps.onLoadedData,
|
|
27783
|
+
onPlaying: videoProps.onPlaying,
|
|
27784
|
+
onLoadingChange: videoProps.onLoadingChange
|
|
28478
27785
|
}
|
|
28479
27786
|
) }),
|
|
28480
27787
|
/* @__PURE__ */ jsx(
|
|
@@ -28887,7 +28194,7 @@ var NewClipsNotification = ({
|
|
|
28887
28194
|
}
|
|
28888
28195
|
);
|
|
28889
28196
|
};
|
|
28890
|
-
var
|
|
28197
|
+
var getSupabaseClient2 = () => {
|
|
28891
28198
|
const url = process.env.NEXT_PUBLIC_SUPABASE_URL;
|
|
28892
28199
|
const key = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
|
|
28893
28200
|
if (!url || !key) {
|
|
@@ -28895,9 +28202,9 @@ var getSupabaseClient3 = () => {
|
|
|
28895
28202
|
}
|
|
28896
28203
|
return createClient(url, key);
|
|
28897
28204
|
};
|
|
28898
|
-
var
|
|
28205
|
+
var getAuthToken2 = async () => {
|
|
28899
28206
|
try {
|
|
28900
|
-
const supabase =
|
|
28207
|
+
const supabase = getSupabaseClient2();
|
|
28901
28208
|
const { data: { session } } = await supabase.auth.getSession();
|
|
28902
28209
|
return session?.access_token || null;
|
|
28903
28210
|
} catch (error) {
|
|
@@ -28918,7 +28225,7 @@ function useWorkspaceCrop(workspaceId) {
|
|
|
28918
28225
|
setIsLoading(true);
|
|
28919
28226
|
setError(null);
|
|
28920
28227
|
try {
|
|
28921
|
-
const token = await
|
|
28228
|
+
const token = await getAuthToken2();
|
|
28922
28229
|
if (!token) {
|
|
28923
28230
|
throw new Error("Authentication required");
|
|
28924
28231
|
}
|
|
@@ -29051,6 +28358,7 @@ var FileManagerFilters = ({
|
|
|
29051
28358
|
}) => {
|
|
29052
28359
|
const [expandedNodes, setExpandedNodes] = useState(/* @__PURE__ */ new Set());
|
|
29053
28360
|
const [searchTerm, setSearchTerm] = useState("");
|
|
28361
|
+
const timezone = useAppTimezone();
|
|
29054
28362
|
const [clipMetadata, setClipMetadata] = useState({});
|
|
29055
28363
|
const [loadingCategories, setLoadingCategories] = useState(/* @__PURE__ */ new Set());
|
|
29056
28364
|
const [categoryPages, setCategoryPages] = useState({});
|
|
@@ -29071,7 +28379,7 @@ var FileManagerFilters = ({
|
|
|
29071
28379
|
method: "POST",
|
|
29072
28380
|
headers: {
|
|
29073
28381
|
"Content-Type": "application/json",
|
|
29074
|
-
"Authorization": `Bearer ${await
|
|
28382
|
+
"Authorization": `Bearer ${await getAuthToken3()}`
|
|
29075
28383
|
},
|
|
29076
28384
|
body: JSON.stringify({
|
|
29077
28385
|
action: "clip-metadata",
|
|
@@ -29104,7 +28412,7 @@ var FileManagerFilters = ({
|
|
|
29104
28412
|
});
|
|
29105
28413
|
}
|
|
29106
28414
|
}, [workspaceId, date, shift]);
|
|
29107
|
-
const
|
|
28415
|
+
const getAuthToken3 = async () => {
|
|
29108
28416
|
try {
|
|
29109
28417
|
const { createClient: createClient5 } = await import('@supabase/supabase-js');
|
|
29110
28418
|
const supabase = createClient5(
|
|
@@ -29130,7 +28438,7 @@ var FileManagerFilters = ({
|
|
|
29130
28438
|
method: "POST",
|
|
29131
28439
|
headers: {
|
|
29132
28440
|
"Content-Type": "application/json",
|
|
29133
|
-
"Authorization": `Bearer ${await
|
|
28441
|
+
"Authorization": `Bearer ${await getAuthToken3()}`
|
|
29134
28442
|
},
|
|
29135
28443
|
body: JSON.stringify({
|
|
29136
28444
|
action: "percentile-clips",
|
|
@@ -29216,23 +28524,27 @@ var FileManagerFilters = ({
|
|
|
29216
28524
|
}
|
|
29217
28525
|
if (categoryCount > 0 && shouldShowCategory(category.id)) {
|
|
29218
28526
|
const colorClasses = getColorClasses(category.color);
|
|
29219
|
-
const clipNodes = filteredClips.map((clip) => {
|
|
28527
|
+
const clipNodes = filteredClips.map((clip, index) => {
|
|
29220
28528
|
const timeString = new Date(clip.clip_timestamp).toLocaleTimeString("en-US", {
|
|
29221
28529
|
hour12: false,
|
|
29222
28530
|
hour: "2-digit",
|
|
29223
28531
|
minute: "2-digit",
|
|
29224
|
-
second: "2-digit"
|
|
28532
|
+
second: "2-digit",
|
|
28533
|
+
timeZone: timezone
|
|
28534
|
+
// Use database timezone for display
|
|
29225
28535
|
});
|
|
29226
28536
|
return {
|
|
29227
28537
|
id: clip.id,
|
|
29228
|
-
label: `${timeString} - ${clip.description}${clip.duration ? ` (${clip.duration.toFixed(1)}s)` : ""}`,
|
|
28538
|
+
label: `${timeString} - ${clip.description}${clip.duration && category.id !== "idle_time" ? ` (${clip.duration.toFixed(1)}s)` : ""}`,
|
|
29229
28539
|
type: "video",
|
|
29230
28540
|
icon: getSeverityIcon(clip.severity),
|
|
29231
28541
|
timestamp: clip.clip_timestamp,
|
|
29232
28542
|
severity: clip.severity,
|
|
29233
28543
|
clipId: clip.clipId,
|
|
29234
28544
|
// Store stable UUID for navigation
|
|
29235
|
-
categoryId: category.id
|
|
28545
|
+
categoryId: category.id,
|
|
28546
|
+
clipPosition: index + 1
|
|
28547
|
+
// Store 1-based position
|
|
29236
28548
|
};
|
|
29237
28549
|
});
|
|
29238
28550
|
tree.push({
|
|
@@ -29348,8 +28660,8 @@ var FileManagerFilters = ({
|
|
|
29348
28660
|
onFilterChange(node.id);
|
|
29349
28661
|
} else if (node.type === "video") {
|
|
29350
28662
|
if (onClipSelect && node.categoryId !== void 0 && node.clipId !== void 0) {
|
|
29351
|
-
console.log(`[FileManager] Selecting clip: category=${node.categoryId}, clipId=${node.clipId}`);
|
|
29352
|
-
onClipSelect(node.categoryId, node.clipId);
|
|
28663
|
+
console.log(`[FileManager] Selecting clip: category=${node.categoryId}, clipId=${node.clipId}, position=${node.clipPosition}`);
|
|
28664
|
+
onClipSelect(node.categoryId, node.clipId, node.clipPosition);
|
|
29353
28665
|
} else {
|
|
29354
28666
|
const videoIndex = videos.findIndex((v) => v.id === node.id);
|
|
29355
28667
|
if (videoIndex !== -1) {
|
|
@@ -29873,8 +29185,6 @@ function useClipsRealtimeUpdates({
|
|
|
29873
29185
|
hasNewClips: newClipsNotification !== null && newClipsNotification.count > 0
|
|
29874
29186
|
};
|
|
29875
29187
|
}
|
|
29876
|
-
var USE_SUPABASE_CLIPS2 = true;
|
|
29877
|
-
var S3ClipsService3 = S3ClipsSupabaseService ;
|
|
29878
29188
|
var BottlenecksContent = ({
|
|
29879
29189
|
workspaceId,
|
|
29880
29190
|
workspaceName,
|
|
@@ -29883,6 +29193,7 @@ var BottlenecksContent = ({
|
|
|
29883
29193
|
className
|
|
29884
29194
|
}) => {
|
|
29885
29195
|
const dashboardConfig = useDashboardConfig();
|
|
29196
|
+
const timezone = useAppTimezone();
|
|
29886
29197
|
const effectiveShift = useMemo(() => {
|
|
29887
29198
|
if (shift !== void 0 && shift !== null) {
|
|
29888
29199
|
const shiftStr = shift.toString();
|
|
@@ -29894,13 +29205,14 @@ var BottlenecksContent = ({
|
|
|
29894
29205
|
return "0";
|
|
29895
29206
|
} else {
|
|
29896
29207
|
const currentShift = getCurrentShift(
|
|
29897
|
-
|
|
29208
|
+
timezone,
|
|
29209
|
+
// Use dynamic timezone instead of hardcoded default
|
|
29898
29210
|
dashboardConfig.shiftConfig
|
|
29899
29211
|
);
|
|
29900
|
-
console.log(`[BottlenecksContent] Using current operational shift: ${currentShift.shiftId}`);
|
|
29212
|
+
console.log(`[BottlenecksContent] Using current operational shift: ${currentShift.shiftId} with timezone: ${timezone}`);
|
|
29901
29213
|
return currentShift.shiftId.toString();
|
|
29902
29214
|
}
|
|
29903
|
-
}, [shift, date, dashboardConfig]);
|
|
29215
|
+
}, [shift, date, timezone, dashboardConfig]);
|
|
29904
29216
|
const { crop: workspaceCrop} = useWorkspaceCrop(workspaceId);
|
|
29905
29217
|
const videoRef = useRef(null);
|
|
29906
29218
|
const [initialFilter, setInitialFilter] = useState("");
|
|
@@ -29913,6 +29225,11 @@ var BottlenecksContent = ({
|
|
|
29913
29225
|
const [duration, setDuration] = useState(0);
|
|
29914
29226
|
const [currentIndex, setCurrentIndex] = useState(0);
|
|
29915
29227
|
const [currentClipId, setCurrentClipId] = useState(null);
|
|
29228
|
+
const [isTransitioning, setIsTransitioning] = useState(false);
|
|
29229
|
+
const [pendingVideo, setPendingVideo] = useState(null);
|
|
29230
|
+
const [isVideoBuffering, setIsVideoBuffering] = useState(false);
|
|
29231
|
+
const [isInitialLoading, setIsInitialLoading] = useState(false);
|
|
29232
|
+
const loadingTimeoutRef = useRef(null);
|
|
29916
29233
|
const [activeFilter, setActiveFilter] = useState(initialFilter);
|
|
29917
29234
|
const previousFilterRef = useRef("");
|
|
29918
29235
|
const [allVideos, setAllVideos] = useState([]);
|
|
@@ -29922,7 +29239,11 @@ var BottlenecksContent = ({
|
|
|
29922
29239
|
const [isNavigating, setIsNavigating] = useState(false);
|
|
29923
29240
|
const [error, setError] = useState(null);
|
|
29924
29241
|
const [clipCounts, setClipCounts] = useState({});
|
|
29925
|
-
const [
|
|
29242
|
+
const [categoryMetadata, setCategoryMetadata] = useState([]);
|
|
29243
|
+
const [currentMetadataIndex, setCurrentMetadataIndex] = useState(0);
|
|
29244
|
+
const [metadataCache, setMetadataCache] = useState({});
|
|
29245
|
+
const categoryMetadataRef = useRef([]);
|
|
29246
|
+
const currentMetadataIndexRef = useRef(0);
|
|
29926
29247
|
const {
|
|
29927
29248
|
newClipsNotification,
|
|
29928
29249
|
hasNewClips,
|
|
@@ -29930,10 +29251,10 @@ var BottlenecksContent = ({
|
|
|
29930
29251
|
clearNotification
|
|
29931
29252
|
} = useClipsRealtimeUpdates({
|
|
29932
29253
|
workspaceId,
|
|
29933
|
-
date: date || getOperationalDate(),
|
|
29254
|
+
date: date || getOperationalDate(timezone),
|
|
29934
29255
|
shiftId: effectiveShift,
|
|
29935
|
-
enabled:
|
|
29936
|
-
//
|
|
29256
|
+
enabled: true,
|
|
29257
|
+
// Supabase implementation
|
|
29937
29258
|
onNewClips: (notification) => {
|
|
29938
29259
|
console.log(`[BottlenecksContent] New clips detected:`, notification);
|
|
29939
29260
|
if (notification.clips.length > 0) {
|
|
@@ -29958,7 +29279,7 @@ var BottlenecksContent = ({
|
|
|
29958
29279
|
console.warn("S3 configuration not found in dashboard config");
|
|
29959
29280
|
return null;
|
|
29960
29281
|
}
|
|
29961
|
-
return new
|
|
29282
|
+
return new S3ClipsSupabaseService(dashboardConfig);
|
|
29962
29283
|
}, [dashboardConfig]);
|
|
29963
29284
|
const {
|
|
29964
29285
|
clipTypes,
|
|
@@ -29967,7 +29288,7 @@ var BottlenecksContent = ({
|
|
|
29967
29288
|
counts: dynamicCounts
|
|
29968
29289
|
} = useClipTypesWithCounts(
|
|
29969
29290
|
workspaceId,
|
|
29970
|
-
date || getOperationalDate(),
|
|
29291
|
+
date || getOperationalDate(timezone),
|
|
29971
29292
|
effectiveShift
|
|
29972
29293
|
// Use same shift as video loading for consistency
|
|
29973
29294
|
);
|
|
@@ -29978,9 +29299,8 @@ var BottlenecksContent = ({
|
|
|
29978
29299
|
clipTypesError,
|
|
29979
29300
|
dynamicCounts,
|
|
29980
29301
|
workspaceId,
|
|
29981
|
-
date: date || getOperationalDate(),
|
|
29982
|
-
shift: shift || "0"
|
|
29983
|
-
USE_SUPABASE_CLIPS: USE_SUPABASE_CLIPS2
|
|
29302
|
+
date: date || getOperationalDate(timezone),
|
|
29303
|
+
shift: shift || "0"
|
|
29984
29304
|
});
|
|
29985
29305
|
useEffect(() => {
|
|
29986
29306
|
if (clipTypes.length > 0 && !initialFilter) {
|
|
@@ -30005,7 +29325,7 @@ var BottlenecksContent = ({
|
|
|
30005
29325
|
}
|
|
30006
29326
|
fetchInProgressRef.current.add(operationKey);
|
|
30007
29327
|
try {
|
|
30008
|
-
const operationalDate = date || getOperationalDate();
|
|
29328
|
+
const operationalDate = date || getOperationalDate(timezone);
|
|
30009
29329
|
const shiftStr = effectiveShift;
|
|
30010
29330
|
console.log(`[BottlenecksContent] Fetching clip counts directly with params:`, {
|
|
30011
29331
|
workspaceId,
|
|
@@ -30051,7 +29371,7 @@ var BottlenecksContent = ({
|
|
|
30051
29371
|
setError(null);
|
|
30052
29372
|
}
|
|
30053
29373
|
try {
|
|
30054
|
-
const operationalDate = date || getOperationalDate();
|
|
29374
|
+
const operationalDate = date || getOperationalDate(timezone);
|
|
30055
29375
|
const shiftStr = effectiveShift;
|
|
30056
29376
|
console.log(`[BottlenecksContent] Loading first video for category: ${targetCategory}`);
|
|
30057
29377
|
try {
|
|
@@ -30075,7 +29395,6 @@ var BottlenecksContent = ({
|
|
|
30075
29395
|
setHasInitialLoad(true);
|
|
30076
29396
|
loadingCategoryRef.current = null;
|
|
30077
29397
|
setIsCategoryLoading(false);
|
|
30078
|
-
setCategoryPosition(1);
|
|
30079
29398
|
return;
|
|
30080
29399
|
}
|
|
30081
29400
|
} catch (directError) {
|
|
@@ -30088,12 +29407,8 @@ var BottlenecksContent = ({
|
|
|
30088
29407
|
operationalDate,
|
|
30089
29408
|
shiftStr,
|
|
30090
29409
|
targetCategory,
|
|
30091
|
-
0
|
|
29410
|
+
0
|
|
30092
29411
|
// First video (index 0)
|
|
30093
|
-
true,
|
|
30094
|
-
// includeCycleTime
|
|
30095
|
-
true
|
|
30096
|
-
// includeMetadata
|
|
30097
29412
|
);
|
|
30098
29413
|
if (firstVideo && isMountedRef.current) {
|
|
30099
29414
|
console.log(`[BottlenecksContent] Successfully loaded first video via index`);
|
|
@@ -30105,7 +29420,6 @@ var BottlenecksContent = ({
|
|
|
30105
29420
|
return prev;
|
|
30106
29421
|
});
|
|
30107
29422
|
setIsCategoryLoading(false);
|
|
30108
|
-
setCategoryPosition(1);
|
|
30109
29423
|
return;
|
|
30110
29424
|
}
|
|
30111
29425
|
} catch (indexError) {
|
|
@@ -30158,10 +29472,16 @@ var BottlenecksContent = ({
|
|
|
30158
29472
|
setIsCategoryLoading(true);
|
|
30159
29473
|
setError(null);
|
|
30160
29474
|
setCurrentIndex(0);
|
|
30161
|
-
setCategoryPosition(1);
|
|
30162
29475
|
setIsNavigating(false);
|
|
30163
29476
|
loadingCategoryRef.current = null;
|
|
29477
|
+
setCategoryMetadata([]);
|
|
29478
|
+
setCurrentMetadataIndex(0);
|
|
29479
|
+
categoryMetadataRef.current = [];
|
|
29480
|
+
currentMetadataIndexRef.current = 0;
|
|
30164
29481
|
previousFilterRef.current = activeFilter;
|
|
29482
|
+
if (activeFilter !== "all") {
|
|
29483
|
+
loadCategoryMetadata(activeFilter, true);
|
|
29484
|
+
}
|
|
30165
29485
|
const filtered = allVideos.filter((video) => {
|
|
30166
29486
|
if (activeFilter === "all") return true;
|
|
30167
29487
|
return video.type === activeFilter;
|
|
@@ -30195,14 +29515,11 @@ var BottlenecksContent = ({
|
|
|
30195
29515
|
}
|
|
30196
29516
|
}, []);
|
|
30197
29517
|
const getCategoryCount = useCallback((categoryId) => {
|
|
30198
|
-
if (
|
|
30199
|
-
|
|
30200
|
-
return allVideos.length;
|
|
30201
|
-
}
|
|
30202
|
-
return 0;
|
|
29518
|
+
if (activeFilter === categoryId && categoryMetadata.length > 0) {
|
|
29519
|
+
return categoryMetadata.length;
|
|
30203
29520
|
}
|
|
30204
29521
|
return mergedCounts[categoryId] || 0;
|
|
30205
|
-
}, [
|
|
29522
|
+
}, [activeFilter, categoryMetadata, mergedCounts]);
|
|
30206
29523
|
const filteredVideos = useMemo(() => {
|
|
30207
29524
|
if (!allVideos) return [];
|
|
30208
29525
|
let filtered = [];
|
|
@@ -30225,29 +29542,61 @@ var BottlenecksContent = ({
|
|
|
30225
29542
|
setError(null);
|
|
30226
29543
|
}
|
|
30227
29544
|
}, [isNavigating, currentIndex, filteredVideos.length]);
|
|
30228
|
-
const
|
|
30229
|
-
|
|
30230
|
-
|
|
30231
|
-
|
|
30232
|
-
|
|
30233
|
-
|
|
30234
|
-
|
|
29545
|
+
const clearLoadingState = useCallback(() => {
|
|
29546
|
+
setIsTransitioning(false);
|
|
29547
|
+
setIsNavigating(false);
|
|
29548
|
+
if (loadingTimeoutRef.current) {
|
|
29549
|
+
clearTimeout(loadingTimeoutRef.current);
|
|
29550
|
+
loadingTimeoutRef.current = null;
|
|
29551
|
+
}
|
|
29552
|
+
}, []);
|
|
29553
|
+
const loadCategoryMetadata = useCallback(async (categoryId, autoLoadFirstVideo = false) => {
|
|
29554
|
+
if (!workspaceId) {
|
|
29555
|
+
return;
|
|
29556
|
+
}
|
|
29557
|
+
const cacheKey = `${categoryId}-${date || getOperationalDate(timezone)}-${effectiveShift}`;
|
|
29558
|
+
if (metadataCache[cacheKey]) {
|
|
29559
|
+
console.log(`[BottlenecksContent] Using cached metadata for ${categoryId}`);
|
|
29560
|
+
setCategoryMetadata(metadataCache[cacheKey]);
|
|
29561
|
+
categoryMetadataRef.current = metadataCache[cacheKey];
|
|
29562
|
+
if (autoLoadFirstVideo && metadataCache[cacheKey].length > 0 && s3ClipsService) {
|
|
29563
|
+
const firstClipMeta = metadataCache[cacheKey][0];
|
|
29564
|
+
try {
|
|
29565
|
+
const video = await s3ClipsService.getClipById(firstClipMeta.clipId);
|
|
29566
|
+
if (video && isMountedRef.current) {
|
|
29567
|
+
setCurrentClipId(firstClipMeta.clipId);
|
|
29568
|
+
setAllVideos([video]);
|
|
29569
|
+
setCurrentIndex(0);
|
|
29570
|
+
setCurrentMetadataIndex(0);
|
|
29571
|
+
currentMetadataIndexRef.current = 0;
|
|
29572
|
+
setIsCategoryLoading(false);
|
|
29573
|
+
console.log(`[BottlenecksContent] Auto-loaded first video from cache: ${video.id} (1/${metadataCache[cacheKey].length})`);
|
|
29574
|
+
}
|
|
29575
|
+
} catch (error2) {
|
|
29576
|
+
console.error(`[BottlenecksContent] Error loading first video from cache:`, error2);
|
|
29577
|
+
setIsCategoryLoading(false);
|
|
29578
|
+
clearLoadingState();
|
|
29579
|
+
}
|
|
29580
|
+
}
|
|
29581
|
+
return;
|
|
30235
29582
|
}
|
|
30236
29583
|
try {
|
|
29584
|
+
console.log(`[BottlenecksContent] Loading metadata for category: ${categoryId}`);
|
|
29585
|
+
const { createClient: createClient5 } = await import('@supabase/supabase-js');
|
|
29586
|
+
const supabase = createClient5(
|
|
29587
|
+
process.env.NEXT_PUBLIC_SUPABASE_URL || "",
|
|
29588
|
+
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || ""
|
|
29589
|
+
);
|
|
29590
|
+
const { data: { session } } = await supabase.auth.getSession();
|
|
29591
|
+
const authToken = session?.access_token;
|
|
29592
|
+
if (!authToken) {
|
|
29593
|
+
console.error("Authentication required for metadata loading");
|
|
29594
|
+
return;
|
|
29595
|
+
}
|
|
29596
|
+
let response;
|
|
30237
29597
|
if (isPercentileCategory(categoryId)) {
|
|
30238
|
-
console.log(`[BottlenecksContent] Loading percentile category: ${categoryId}`);
|
|
30239
29598
|
const percentileType = categoryId === "fast-cycles" ? "fast-cycles" : categoryId === "slow-cycles" ? "slow-cycles" : "idle-times";
|
|
30240
|
-
|
|
30241
|
-
const supabase = createClient5(
|
|
30242
|
-
process.env.NEXT_PUBLIC_SUPABASE_URL || "",
|
|
30243
|
-
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || ""
|
|
30244
|
-
);
|
|
30245
|
-
const { data: { session } } = await supabase.auth.getSession();
|
|
30246
|
-
const authToken = session?.access_token;
|
|
30247
|
-
if (!authToken) {
|
|
30248
|
-
throw new Error("Authentication required");
|
|
30249
|
-
}
|
|
30250
|
-
const response = await fetch("/api/clips/supabase", {
|
|
29599
|
+
response = await fetch("/api/clips/supabase", {
|
|
30251
29600
|
method: "POST",
|
|
30252
29601
|
headers: {
|
|
30253
29602
|
"Content-Type": "application/json",
|
|
@@ -30257,62 +29606,132 @@ var BottlenecksContent = ({
|
|
|
30257
29606
|
action: "percentile-clips",
|
|
30258
29607
|
percentileAction: percentileType,
|
|
30259
29608
|
workspaceId,
|
|
30260
|
-
startDate: `${date || getOperationalDate()}T00:00:00Z`,
|
|
30261
|
-
endDate: `${date || getOperationalDate()}T23:59:59Z`,
|
|
29609
|
+
startDate: `${date || getOperationalDate(timezone)}T00:00:00Z`,
|
|
29610
|
+
endDate: `${date || getOperationalDate(timezone)}T23:59:59Z`,
|
|
30262
29611
|
percentile: 10,
|
|
30263
29612
|
shiftId: effectiveShift,
|
|
30264
29613
|
limit: 100
|
|
30265
29614
|
})
|
|
30266
29615
|
});
|
|
30267
|
-
|
|
30268
|
-
|
|
30269
|
-
|
|
29616
|
+
} else {
|
|
29617
|
+
response = await fetch("/api/clips/supabase", {
|
|
29618
|
+
method: "POST",
|
|
29619
|
+
headers: {
|
|
29620
|
+
"Content-Type": "application/json",
|
|
29621
|
+
"Authorization": `Bearer ${authToken}`
|
|
29622
|
+
},
|
|
29623
|
+
body: JSON.stringify({
|
|
29624
|
+
action: "clip-metadata",
|
|
29625
|
+
workspaceId,
|
|
29626
|
+
date: date || getOperationalDate(timezone),
|
|
29627
|
+
shift: effectiveShift,
|
|
29628
|
+
category: categoryId,
|
|
29629
|
+
page: 1,
|
|
29630
|
+
limit: 1e3
|
|
29631
|
+
})
|
|
29632
|
+
});
|
|
29633
|
+
}
|
|
29634
|
+
if (!response.ok) {
|
|
29635
|
+
throw new Error(`Metadata API error: ${response.status}`);
|
|
29636
|
+
}
|
|
29637
|
+
const categoryData = await response.json();
|
|
29638
|
+
if (categoryData.clips && isMountedRef.current) {
|
|
29639
|
+
let metadataClips;
|
|
29640
|
+
if (isPercentileCategory(categoryId)) {
|
|
29641
|
+
metadataClips = categoryData.clips.map((clip, index) => ({
|
|
29642
|
+
id: clip.id,
|
|
29643
|
+
clipId: clip.id,
|
|
29644
|
+
clip_timestamp: clip.timestamp || clip.creation_timestamp,
|
|
29645
|
+
description: clip.description,
|
|
29646
|
+
severity: clip.severity,
|
|
29647
|
+
category: categoryId,
|
|
29648
|
+
duration: clip.cycle_time_seconds,
|
|
29649
|
+
index
|
|
29650
|
+
}));
|
|
29651
|
+
} else {
|
|
29652
|
+
metadataClips = categoryData.clips;
|
|
30270
29653
|
}
|
|
30271
|
-
|
|
30272
|
-
|
|
30273
|
-
|
|
30274
|
-
|
|
30275
|
-
|
|
30276
|
-
|
|
30277
|
-
|
|
30278
|
-
|
|
30279
|
-
|
|
29654
|
+
setMetadataCache((prev) => ({
|
|
29655
|
+
...prev,
|
|
29656
|
+
[cacheKey]: metadataClips
|
|
29657
|
+
}));
|
|
29658
|
+
setCategoryMetadata(metadataClips);
|
|
29659
|
+
categoryMetadataRef.current = metadataClips;
|
|
29660
|
+
console.log(`[BottlenecksContent] Loaded metadata for ${categoryId}: ${metadataClips.length} clips`);
|
|
29661
|
+
if (autoLoadFirstVideo && metadataClips.length > 0 && s3ClipsService) {
|
|
29662
|
+
const firstClipMeta = metadataClips[0];
|
|
29663
|
+
try {
|
|
29664
|
+
const video = await s3ClipsService.getClipById(firstClipMeta.clipId);
|
|
29665
|
+
if (video && isMountedRef.current) {
|
|
29666
|
+
setCurrentClipId(firstClipMeta.clipId);
|
|
29667
|
+
setAllVideos([video]);
|
|
29668
|
+
setCurrentIndex(0);
|
|
29669
|
+
setCurrentMetadataIndex(0);
|
|
29670
|
+
currentMetadataIndexRef.current = 0;
|
|
29671
|
+
setIsCategoryLoading(false);
|
|
29672
|
+
console.log(`[BottlenecksContent] Auto-loaded first video: ${video.id} (1/${metadataClips.length})`);
|
|
29673
|
+
}
|
|
29674
|
+
} catch (error2) {
|
|
29675
|
+
console.error(`[BottlenecksContent] Error loading first video:`, error2);
|
|
29676
|
+
setIsCategoryLoading(false);
|
|
30280
29677
|
}
|
|
30281
29678
|
}
|
|
30282
|
-
}
|
|
30283
|
-
|
|
30284
|
-
|
|
30285
|
-
|
|
30286
|
-
|
|
30287
|
-
|
|
30288
|
-
|
|
30289
|
-
|
|
30290
|
-
|
|
29679
|
+
}
|
|
29680
|
+
} catch (error2) {
|
|
29681
|
+
console.error(`[BottlenecksContent] Error loading category metadata:`, error2);
|
|
29682
|
+
}
|
|
29683
|
+
}, [workspaceId, date, effectiveShift, isPercentileCategory, metadataCache, s3ClipsService]);
|
|
29684
|
+
const loadAndPlayClipById = useCallback(async (clipId, categoryId, position) => {
|
|
29685
|
+
if (!workspaceId || !s3ClipsService || !isMountedRef.current) return;
|
|
29686
|
+
console.log(`[BottlenecksContent] Loading clip by ID: ${clipId}, category=${categoryId}, position=${position}`);
|
|
29687
|
+
setIsTransitioning(true);
|
|
29688
|
+
setIsInitialLoading(true);
|
|
29689
|
+
setError(null);
|
|
29690
|
+
loadingTimeoutRef.current = setTimeout(() => {
|
|
29691
|
+
console.warn("[BottlenecksContent] Loading timeout - clearing stuck loading state");
|
|
29692
|
+
clearLoadingState();
|
|
29693
|
+
}, 1e3);
|
|
29694
|
+
if (activeFilterRef.current !== categoryId) {
|
|
29695
|
+
updateActiveFilter(categoryId);
|
|
29696
|
+
}
|
|
29697
|
+
try {
|
|
29698
|
+
await loadCategoryMetadata(categoryId, false);
|
|
29699
|
+
const metadataArray = categoryMetadataRef.current;
|
|
29700
|
+
if (metadataArray.length > 0) {
|
|
29701
|
+
const clickedClipIndex = metadataArray.findIndex((clip) => clip.clipId === clipId);
|
|
29702
|
+
if (clickedClipIndex !== -1) {
|
|
29703
|
+
setCurrentMetadataIndex(clickedClipIndex);
|
|
29704
|
+
currentMetadataIndexRef.current = clickedClipIndex;
|
|
29705
|
+
const video = await s3ClipsService.getClipById(clipId);
|
|
29706
|
+
if (video) {
|
|
29707
|
+
setPendingVideo(video);
|
|
29708
|
+
setCurrentClipId(clipId);
|
|
29709
|
+
setAllVideos([video]);
|
|
29710
|
+
setCurrentIndex(0);
|
|
29711
|
+
console.log(`[BottlenecksContent] Loaded clip ${clipId} (${clickedClipIndex + 1}/${metadataArray.length})`);
|
|
29712
|
+
}
|
|
30291
29713
|
}
|
|
30292
29714
|
}
|
|
30293
|
-
setIsNavigating(false);
|
|
30294
29715
|
} catch (error2) {
|
|
30295
29716
|
console.error(`[BottlenecksContent] Error loading clip by ID (${clipId}):`, error2);
|
|
30296
29717
|
if (isMountedRef.current) {
|
|
30297
29718
|
setError("Failed to load selected clip. Please try again.");
|
|
30298
|
-
|
|
29719
|
+
clearLoadingState();
|
|
30299
29720
|
}
|
|
30300
29721
|
}
|
|
30301
|
-
}, [workspaceId, s3ClipsService, date, effectiveShift, updateActiveFilter]);
|
|
29722
|
+
}, [workspaceId, s3ClipsService, date, effectiveShift, updateActiveFilter, clearLoadingState]);
|
|
30302
29723
|
useCallback(async (categoryId, clipIndex) => {
|
|
30303
29724
|
console.warn("[BottlenecksContent] loadAndPlayClip is deprecated, use loadAndPlayClipById instead");
|
|
30304
29725
|
if (!workspaceId || !s3ClipsService || !isMountedRef.current) return;
|
|
30305
29726
|
try {
|
|
30306
|
-
const operationalDate = date || getOperationalDate();
|
|
29727
|
+
const operationalDate = date || getOperationalDate(timezone);
|
|
30307
29728
|
const shiftStr = effectiveShift;
|
|
30308
29729
|
const video = await s3ClipsService.getClipByIndex(
|
|
30309
29730
|
workspaceId,
|
|
30310
29731
|
operationalDate,
|
|
30311
29732
|
shiftStr,
|
|
30312
29733
|
categoryId,
|
|
30313
|
-
clipIndex
|
|
30314
|
-
true,
|
|
30315
|
-
true
|
|
29734
|
+
clipIndex
|
|
30316
29735
|
);
|
|
30317
29736
|
if (video?.id) {
|
|
30318
29737
|
await loadAndPlayClipById(video.id, categoryId);
|
|
@@ -30326,105 +29745,79 @@ var BottlenecksContent = ({
|
|
|
30326
29745
|
const handleNext = useCallback(async () => {
|
|
30327
29746
|
if (!isMountedRef.current) return;
|
|
30328
29747
|
const currentFilter = activeFilterRef.current;
|
|
30329
|
-
|
|
30330
|
-
|
|
29748
|
+
setIsTransitioning(true);
|
|
29749
|
+
setIsInitialLoading(true);
|
|
30331
29750
|
setError(null);
|
|
30332
29751
|
try {
|
|
30333
|
-
|
|
30334
|
-
|
|
30335
|
-
|
|
30336
|
-
|
|
30337
|
-
|
|
30338
|
-
|
|
30339
|
-
|
|
30340
|
-
|
|
30341
|
-
|
|
30342
|
-
}
|
|
30343
|
-
|
|
30344
|
-
|
|
30345
|
-
|
|
30346
|
-
|
|
30347
|
-
|
|
30348
|
-
setIsNavigating(false);
|
|
30349
|
-
return;
|
|
30350
|
-
}
|
|
30351
|
-
const neighbors = await s3ClipsService.getNeighboringClips(
|
|
30352
|
-
workspaceId,
|
|
30353
|
-
date || getOperationalDate(),
|
|
30354
|
-
effectiveShift,
|
|
30355
|
-
currentFilter,
|
|
30356
|
-
currentClipId
|
|
30357
|
-
);
|
|
30358
|
-
if (neighbors.next) {
|
|
30359
|
-
console.log(`[handleNext] Found next clip: ${neighbors.next.id}`);
|
|
30360
|
-
setCurrentClipId(neighbors.next.id || null);
|
|
30361
|
-
setAllVideos([neighbors.next]);
|
|
29752
|
+
const currentMetaIndex = currentMetadataIndexRef.current;
|
|
29753
|
+
const metadataArray = categoryMetadataRef.current;
|
|
29754
|
+
console.log(`[handleNext] Unified navigation: ${currentFilter}, metadata index: ${currentMetaIndex}/${metadataArray.length}`);
|
|
29755
|
+
if (currentMetaIndex < metadataArray.length - 1) {
|
|
29756
|
+
const nextMetadataIndex = currentMetaIndex + 1;
|
|
29757
|
+
const nextClipMeta = metadataArray[nextMetadataIndex];
|
|
29758
|
+
console.log(`[handleNext] Loading next clip: ${nextClipMeta.clipId} at metadata index ${nextMetadataIndex}`);
|
|
29759
|
+
if (!s3ClipsService) {
|
|
29760
|
+
throw new Error("S3 clips service not available");
|
|
29761
|
+
}
|
|
29762
|
+
const nextVideo = await s3ClipsService.getClipById(nextClipMeta.clipId);
|
|
29763
|
+
if (nextVideo) {
|
|
29764
|
+
setPendingVideo(nextVideo);
|
|
29765
|
+
setCurrentClipId(nextClipMeta.clipId);
|
|
29766
|
+
setAllVideos([nextVideo]);
|
|
30362
29767
|
setCurrentIndex(0);
|
|
30363
|
-
|
|
30364
|
-
|
|
30365
|
-
|
|
29768
|
+
setCurrentMetadataIndex(nextMetadataIndex);
|
|
29769
|
+
currentMetadataIndexRef.current = nextMetadataIndex;
|
|
29770
|
+
console.log(`[handleNext] Successfully loaded next clip: ${nextVideo.id} (${nextMetadataIndex + 1}/${metadataArray.length})`);
|
|
30366
29771
|
} else {
|
|
30367
|
-
console.
|
|
30368
|
-
|
|
29772
|
+
console.error(`[handleNext] Failed to load next video for clip ID: ${nextClipMeta.clipId}`);
|
|
29773
|
+
clearLoadingState();
|
|
30369
29774
|
}
|
|
29775
|
+
} else {
|
|
29776
|
+
console.log(`[handleNext] Already at last clip in category`);
|
|
29777
|
+
clearLoadingState();
|
|
30370
29778
|
}
|
|
30371
29779
|
} catch (error2) {
|
|
30372
29780
|
console.error(`[handleNext] Error navigating:`, error2);
|
|
30373
29781
|
setError("Failed to navigate to next clip");
|
|
30374
|
-
|
|
29782
|
+
clearLoadingState();
|
|
30375
29783
|
}
|
|
30376
|
-
}, [
|
|
29784
|
+
}, [clearLoadingState, s3ClipsService]);
|
|
30377
29785
|
const handlePrevious = useCallback(async () => {
|
|
30378
29786
|
if (!isMountedRef.current) return;
|
|
30379
29787
|
const currentFilter = activeFilterRef.current;
|
|
30380
|
-
|
|
30381
|
-
|
|
29788
|
+
setIsTransitioning(true);
|
|
29789
|
+
setIsInitialLoading(true);
|
|
30382
29790
|
setError(null);
|
|
30383
29791
|
try {
|
|
30384
|
-
|
|
30385
|
-
|
|
30386
|
-
|
|
30387
|
-
|
|
30388
|
-
|
|
30389
|
-
|
|
30390
|
-
|
|
30391
|
-
|
|
30392
|
-
|
|
30393
|
-
|
|
30394
|
-
|
|
30395
|
-
|
|
30396
|
-
|
|
30397
|
-
|
|
30398
|
-
if (!currentClipId || !s3ClipsService) {
|
|
30399
|
-
setIsNavigating(false);
|
|
30400
|
-
return;
|
|
30401
|
-
}
|
|
30402
|
-
const neighbors = await s3ClipsService.getNeighboringClips(
|
|
30403
|
-
workspaceId,
|
|
30404
|
-
date || getOperationalDate(),
|
|
30405
|
-
effectiveShift,
|
|
30406
|
-
currentFilter,
|
|
30407
|
-
currentClipId
|
|
30408
|
-
);
|
|
30409
|
-
if (neighbors.previous) {
|
|
30410
|
-
console.log(`[handlePrevious] Found previous clip: ${neighbors.previous.id}`);
|
|
30411
|
-
setCurrentClipId(neighbors.previous.id || null);
|
|
30412
|
-
setAllVideos([neighbors.previous]);
|
|
29792
|
+
const currentMetaIndex = currentMetadataIndexRef.current;
|
|
29793
|
+
const metadataArray = categoryMetadataRef.current;
|
|
29794
|
+
console.log(`[handlePrevious] Unified navigation: ${currentFilter}, metadata index: ${currentMetaIndex}/${metadataArray.length}`);
|
|
29795
|
+
if (currentMetaIndex > 0) {
|
|
29796
|
+
const prevMetadataIndex = currentMetaIndex - 1;
|
|
29797
|
+
const prevClipMeta = metadataArray[prevMetadataIndex];
|
|
29798
|
+
if (!s3ClipsService) {
|
|
29799
|
+
throw new Error("S3 clips service not available");
|
|
29800
|
+
}
|
|
29801
|
+
const prevVideo = await s3ClipsService.getClipById(prevClipMeta.clipId);
|
|
29802
|
+
if (prevVideo) {
|
|
29803
|
+
setPendingVideo(prevVideo);
|
|
29804
|
+
setCurrentClipId(prevClipMeta.clipId);
|
|
29805
|
+
setAllVideos([prevVideo]);
|
|
30413
29806
|
setCurrentIndex(0);
|
|
30414
|
-
|
|
30415
|
-
|
|
30416
|
-
|
|
30417
|
-
} else {
|
|
30418
|
-
console.log(`[handlePrevious] No previous clip available`);
|
|
30419
|
-
setIsNavigating(false);
|
|
29807
|
+
setCurrentMetadataIndex(prevMetadataIndex);
|
|
29808
|
+
currentMetadataIndexRef.current = prevMetadataIndex;
|
|
29809
|
+
console.log(`[handlePrevious] Loaded previous clip: ${prevVideo.id} (${prevMetadataIndex + 1}/${metadataArray.length})`);
|
|
30420
29810
|
}
|
|
29811
|
+
} else {
|
|
29812
|
+
console.log(`[handlePrevious] Already at first clip in category`);
|
|
29813
|
+
clearLoadingState();
|
|
30421
29814
|
}
|
|
30422
29815
|
} catch (error2) {
|
|
30423
29816
|
console.error(`[handlePrevious] Error navigating:`, error2);
|
|
30424
29817
|
setError("Failed to navigate to previous clip");
|
|
30425
|
-
|
|
29818
|
+
clearLoadingState();
|
|
30426
29819
|
}
|
|
30427
|
-
}, [
|
|
29820
|
+
}, [clearLoadingState, s3ClipsService]);
|
|
30428
29821
|
const currentVideo = useMemo(() => {
|
|
30429
29822
|
if (!filteredVideos || filteredVideos.length === 0 || currentIndex >= filteredVideos.length) {
|
|
30430
29823
|
return null;
|
|
@@ -30432,7 +29825,7 @@ var BottlenecksContent = ({
|
|
|
30432
29825
|
return filteredVideos[currentIndex];
|
|
30433
29826
|
}, [filteredVideos, currentIndex]);
|
|
30434
29827
|
const handleVideoReady = useCallback((player) => {
|
|
30435
|
-
console.log("Video.js player ready");
|
|
29828
|
+
console.log("Video.js player ready - NOT clearing loading (wait for playing event)");
|
|
30436
29829
|
videoRetryCountRef.current = 0;
|
|
30437
29830
|
if (error?.includes("Retrying")) {
|
|
30438
29831
|
setError(null);
|
|
@@ -30440,6 +29833,7 @@ var BottlenecksContent = ({
|
|
|
30440
29833
|
}, [error]);
|
|
30441
29834
|
const handleVideoPlay = useCallback(async (player) => {
|
|
30442
29835
|
setIsPlaying(true);
|
|
29836
|
+
setIsInitialLoading(false);
|
|
30443
29837
|
if (currentVideo && !currentVideo.creation_timestamp && s3ClipsService) {
|
|
30444
29838
|
try {
|
|
30445
29839
|
const originalUri = currentVideo.originalUri || currentVideo.src;
|
|
@@ -30469,6 +29863,17 @@ var BottlenecksContent = ({
|
|
|
30469
29863
|
const handleDurationChange = useCallback((player, duration2) => {
|
|
30470
29864
|
setDuration(duration2);
|
|
30471
29865
|
}, []);
|
|
29866
|
+
const handleLoadedData = useCallback((player) => {
|
|
29867
|
+
console.log("Video data loaded - NOT clearing loading (wait for playing event)");
|
|
29868
|
+
}, []);
|
|
29869
|
+
const handleVideoPlaying = useCallback((player) => {
|
|
29870
|
+
console.log("Video playing - hiding transition overlay (most reliable)");
|
|
29871
|
+
clearLoadingState();
|
|
29872
|
+
}, [clearLoadingState]);
|
|
29873
|
+
const handleVideoLoadingChange = useCallback((isLoading2) => {
|
|
29874
|
+
console.log(`[BottlenecksContent] Video loading state changed: ${isLoading2}`);
|
|
29875
|
+
setIsVideoBuffering(isLoading2);
|
|
29876
|
+
}, []);
|
|
30472
29877
|
const handleVideoEnded = useCallback((player) => {
|
|
30473
29878
|
handleNext();
|
|
30474
29879
|
}, [handleNext]);
|
|
@@ -30505,6 +29910,10 @@ var BottlenecksContent = ({
|
|
|
30505
29910
|
isMountedRef.current = false;
|
|
30506
29911
|
loadingCategoryRef.current = null;
|
|
30507
29912
|
fetchInProgressRef.current.clear();
|
|
29913
|
+
if (loadingTimeoutRef.current) {
|
|
29914
|
+
clearTimeout(loadingTimeoutRef.current);
|
|
29915
|
+
loadingTimeoutRef.current = null;
|
|
29916
|
+
}
|
|
30508
29917
|
setIsCategoryLoading(false);
|
|
30509
29918
|
setIsNavigating(false);
|
|
30510
29919
|
};
|
|
@@ -30514,6 +29923,14 @@ var BottlenecksContent = ({
|
|
|
30514
29923
|
setError(null);
|
|
30515
29924
|
}
|
|
30516
29925
|
}, [currentIndex, filteredVideos]);
|
|
29926
|
+
useEffect(() => {
|
|
29927
|
+
if (!isTransitioning && pendingVideo) {
|
|
29928
|
+
const timer = setTimeout(() => {
|
|
29929
|
+
setPendingVideo(null);
|
|
29930
|
+
}, 200);
|
|
29931
|
+
return () => clearTimeout(timer);
|
|
29932
|
+
}
|
|
29933
|
+
}, [isTransitioning, pendingVideo]);
|
|
30517
29934
|
useEffect(() => {
|
|
30518
29935
|
const handleKeyDown = (e) => {
|
|
30519
29936
|
if (e.key === "ArrowLeft") {
|
|
@@ -30614,173 +30031,169 @@ var BottlenecksContent = ({
|
|
|
30614
30031
|
}
|
|
30615
30032
|
const activeCategory = categoriesToShow.find((cat) => cat.type === activeFilter);
|
|
30616
30033
|
if (activeCategory) {
|
|
30617
|
-
return `${activeCategory.label} (${
|
|
30034
|
+
return `${activeCategory.label} (${getCategoryCount(activeCategory.type)})`;
|
|
30618
30035
|
}
|
|
30619
|
-
return `${activeFilter.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase())} (${
|
|
30036
|
+
return `${activeFilter.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase())} (${getCategoryCount(activeFilter)})`;
|
|
30620
30037
|
})() }),
|
|
30621
30038
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2", children: [
|
|
30622
30039
|
/* @__PURE__ */ jsx(
|
|
30623
30040
|
"button",
|
|
30624
30041
|
{
|
|
30625
30042
|
onClick: handlePrevious,
|
|
30626
|
-
disabled:
|
|
30627
|
-
className: `p-2 rounded-full transition-colors ${
|
|
30043
|
+
disabled: currentMetadataIndex <= 0 || categoryMetadata.length === 0,
|
|
30044
|
+
className: `p-2 rounded-full transition-colors ${currentMetadataIndex <= 0 || categoryMetadata.length === 0 ? "text-gray-300 cursor-not-allowed" : "text-gray-600 hover:bg-gray-100"}`,
|
|
30628
30045
|
"aria-label": "Previous video",
|
|
30629
30046
|
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-5 w-5" })
|
|
30630
30047
|
}
|
|
30631
30048
|
),
|
|
30632
30049
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-1", children: [
|
|
30633
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm px-2 py-1 bg-blue-50 text-blue-700 rounded-full font-medium tabular-nums", children:
|
|
30050
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm px-2 py-1 bg-blue-50 text-blue-700 rounded-full font-medium tabular-nums", children: categoryMetadata.length > 0 ? `${currentMetadataIndex + 1} / ${categoryMetadata.length}` : "0 / 0" }),
|
|
30634
30051
|
error && /* @__PURE__ */ jsx("span", { className: "text-xs text-red-600 font-medium", children: error })
|
|
30635
30052
|
] }),
|
|
30636
30053
|
/* @__PURE__ */ jsx(
|
|
30637
30054
|
"button",
|
|
30638
30055
|
{
|
|
30639
30056
|
onClick: handleNext,
|
|
30640
|
-
disabled:
|
|
30641
|
-
className: `p-2 rounded-full transition-colors ${
|
|
30057
|
+
disabled: currentMetadataIndex >= categoryMetadata.length - 1 || categoryMetadata.length === 0,
|
|
30058
|
+
className: `p-2 rounded-full transition-colors ${currentMetadataIndex >= categoryMetadata.length - 1 || categoryMetadata.length === 0 ? "text-gray-300 cursor-not-allowed" : "text-gray-600 hover:bg-gray-100"}`,
|
|
30642
30059
|
"aria-label": "Next video",
|
|
30643
30060
|
children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-5 w-5" })
|
|
30644
30061
|
}
|
|
30645
30062
|
)
|
|
30646
30063
|
] })
|
|
30647
30064
|
] }) }),
|
|
30648
|
-
/*
|
|
30649
|
-
|
|
30650
|
-
/*
|
|
30651
|
-
|
|
30652
|
-
|
|
30653
|
-
|
|
30654
|
-
|
|
30655
|
-
|
|
30656
|
-
|
|
30657
|
-
|
|
30658
|
-
|
|
30659
|
-
|
|
30660
|
-
|
|
30661
|
-
|
|
30662
|
-
|
|
30663
|
-
|
|
30664
|
-
|
|
30665
|
-
|
|
30666
|
-
|
|
30667
|
-
|
|
30668
|
-
|
|
30669
|
-
|
|
30670
|
-
|
|
30671
|
-
|
|
30672
|
-
|
|
30673
|
-
|
|
30674
|
-
|
|
30675
|
-
|
|
30676
|
-
|
|
30677
|
-
|
|
30678
|
-
|
|
30679
|
-
|
|
30680
|
-
|
|
30681
|
-
|
|
30682
|
-
|
|
30683
|
-
|
|
30684
|
-
|
|
30685
|
-
|
|
30686
|
-
|
|
30065
|
+
/* Show video if we have filtered videos and current video, or show loading */
|
|
30066
|
+
filteredVideos.length > 0 && currentVideo ? /* @__PURE__ */ jsx("div", { className: "p-4 h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsx("div", { className: "relative h-full group", children: /* @__PURE__ */ jsxs("div", { className: "relative w-full h-full overflow-hidden rounded-md shadow-inner bg-gray-900", children: [
|
|
30067
|
+
/* @__PURE__ */ jsx(
|
|
30068
|
+
CroppedVideoPlayer,
|
|
30069
|
+
{
|
|
30070
|
+
ref: videoRef,
|
|
30071
|
+
src: currentVideo.src,
|
|
30072
|
+
className: "w-full h-full",
|
|
30073
|
+
crop: workspaceCrop?.crop || null,
|
|
30074
|
+
autoplay: true,
|
|
30075
|
+
playsInline: true,
|
|
30076
|
+
loop: false,
|
|
30077
|
+
externalLoadingControl: true,
|
|
30078
|
+
onReady: handleVideoReady,
|
|
30079
|
+
onPlay: handleVideoPlay,
|
|
30080
|
+
onPause: handleVideoPause,
|
|
30081
|
+
onTimeUpdate: handleTimeUpdate,
|
|
30082
|
+
onDurationChange: handleDurationChange,
|
|
30083
|
+
onEnded: handleVideoEnded,
|
|
30084
|
+
onError: handleVideoError,
|
|
30085
|
+
onLoadedData: handleLoadedData,
|
|
30086
|
+
onPlaying: handleVideoPlaying,
|
|
30087
|
+
onLoadingChange: handleVideoLoadingChange,
|
|
30088
|
+
options: {
|
|
30089
|
+
// Ensure full height is always visible - no cropping
|
|
30090
|
+
fluid: false,
|
|
30091
|
+
responsive: false,
|
|
30092
|
+
fill: false
|
|
30093
|
+
}
|
|
30094
|
+
}
|
|
30095
|
+
),
|
|
30096
|
+
(isTransitioning || isVideoBuffering && isInitialLoading) && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 z-30 flex items-center justify-center bg-black", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
|
|
30097
|
+
!isTransitioning && isVideoBuffering && !isInitialLoading && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 z-30 flex items-center justify-center bg-black/60", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
|
|
30098
|
+
/* @__PURE__ */ jsx(
|
|
30099
|
+
"div",
|
|
30100
|
+
{
|
|
30101
|
+
className: "absolute inset-0 z-10 cursor-pointer",
|
|
30102
|
+
onClick: togglePlayback
|
|
30103
|
+
}
|
|
30104
|
+
),
|
|
30105
|
+
error && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-black/80 text-white p-4", children: /* @__PURE__ */ jsxs("div", { className: "text-center max-w-md", children: [
|
|
30106
|
+
/* @__PURE__ */ jsx("svg", { className: "w-16 h-16 mx-auto mb-4 text-red-400", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.732-.833-2.5 0L4.268 16.5c-.77.833.192 2.5 1.732 2.5z" }) }),
|
|
30107
|
+
/* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold mb-2", children: "Video Stream Error" }),
|
|
30108
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-gray-300 mb-4", children: "The video stream appears to be corrupted or unavailable. This may be due to:" }),
|
|
30109
|
+
/* @__PURE__ */ jsxs("ul", { className: "text-sm text-gray-300 text-left space-y-1 mb-4", children: [
|
|
30110
|
+
/* @__PURE__ */ jsx("li", { children: "\u2022 Incomplete video encoding" }),
|
|
30111
|
+
/* @__PURE__ */ jsx("li", { children: "\u2022 Network connectivity issues" }),
|
|
30112
|
+
/* @__PURE__ */ jsx("li", { children: "\u2022 Server processing errors" })
|
|
30113
|
+
] }),
|
|
30114
|
+
/* @__PURE__ */ jsx(
|
|
30115
|
+
"button",
|
|
30116
|
+
{
|
|
30117
|
+
onClick: () => {
|
|
30118
|
+
setError(null);
|
|
30119
|
+
if (videoRef.current) {
|
|
30120
|
+
videoRef.current.dispose();
|
|
30687
30121
|
}
|
|
30688
|
-
|
|
30689
|
-
|
|
30690
|
-
|
|
30691
|
-
|
|
30692
|
-
|
|
30693
|
-
|
|
30694
|
-
|
|
30695
|
-
|
|
30696
|
-
|
|
30697
|
-
|
|
30698
|
-
|
|
30699
|
-
|
|
30700
|
-
|
|
30701
|
-
|
|
30702
|
-
|
|
30703
|
-
|
|
30704
|
-
|
|
30705
|
-
|
|
30706
|
-
|
|
30707
|
-
|
|
30708
|
-
|
|
30709
|
-
|
|
30710
|
-
|
|
30711
|
-
|
|
30712
|
-
|
|
30713
|
-
|
|
30714
|
-
|
|
30715
|
-
|
|
30716
|
-
|
|
30717
|
-
|
|
30718
|
-
|
|
30719
|
-
|
|
30720
|
-
|
|
30721
|
-
|
|
30722
|
-
|
|
30723
|
-
|
|
30724
|
-
|
|
30725
|
-
|
|
30726
|
-
|
|
30727
|
-
|
|
30728
|
-
|
|
30729
|
-
|
|
30730
|
-
|
|
30731
|
-
|
|
30732
|
-
|
|
30733
|
-
|
|
30734
|
-
|
|
30735
|
-
|
|
30736
|
-
|
|
30737
|
-
|
|
30738
|
-
|
|
30739
|
-
|
|
30740
|
-
|
|
30741
|
-
|
|
30742
|
-
|
|
30743
|
-
|
|
30744
|
-
|
|
30745
|
-
|
|
30746
|
-
|
|
30747
|
-
|
|
30748
|
-
|
|
30749
|
-
|
|
30750
|
-
|
|
30751
|
-
|
|
30752
|
-
|
|
30753
|
-
|
|
30754
|
-
|
|
30755
|
-
|
|
30756
|
-
|
|
30757
|
-
|
|
30758
|
-
|
|
30759
|
-
|
|
30760
|
-
|
|
30761
|
-
|
|
30762
|
-
|
|
30763
|
-
}
|
|
30764
|
-
)
|
|
30765
|
-
] }) })
|
|
30766
|
-
] }) }) }) : (
|
|
30767
|
-
/* Priority 5: Show "no clips found" only if we have counts and there are truly no clips for workspace */
|
|
30768
|
-
hasInitialLoad && Object.keys(mergedCounts).length > 0 && Object.values(mergedCounts).every((count) => count === 0) ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsxs("div", { className: "text-center p-8", children: [
|
|
30769
|
-
/* @__PURE__ */ jsx("svg", { className: "w-16 h-16 text-gray-300 mx-auto mb-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z" }) }),
|
|
30770
|
-
/* @__PURE__ */ jsx("h3", { className: "text-xl font-medium text-gray-700 mb-2", children: "No Clips Found" }),
|
|
30771
|
-
/* @__PURE__ */ jsx("p", { className: "text-gray-500", children: "There were no video clips found for this workspace today." })
|
|
30772
|
-
] }) }) : (
|
|
30773
|
-
/* Priority 6: Show "no matching clips" only if we have data loaded and specifically no clips for this filter */
|
|
30774
|
-
hasInitialLoad && (mergedCounts[activeFilter] || 0) === 0 ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsxs("div", { className: "text-center p-8", children: [
|
|
30775
|
-
/* @__PURE__ */ jsx("svg", { className: "w-16 h-16 text-gray-300 mx-auto mb-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z" }) }),
|
|
30776
|
-
/* @__PURE__ */ jsx("h3", { className: "text-xl font-medium text-gray-700 mb-2", children: "No Matching Clips" }),
|
|
30777
|
-
/* @__PURE__ */ jsx("p", { className: "text-gray-500", children: "There are no clips matching the selected filter." })
|
|
30778
|
-
] }) }) : (
|
|
30779
|
-
/* Priority 7: Default loading state for any other case */
|
|
30780
|
-
/* @__PURE__ */ jsx("div", { className: "p-4 h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsx("div", { className: "relative h-full", children: /* @__PURE__ */ jsx("div", { className: "relative w-full h-full overflow-hidden rounded-md shadow-inner bg-gray-900 flex items-center justify-center", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading..." }) }) }) })
|
|
30781
|
-
)
|
|
30782
|
-
)
|
|
30783
|
-
)
|
|
30122
|
+
},
|
|
30123
|
+
className: "px-4 py-2 bg-blue-600 hover:bg-blue-700 rounded text-sm font-medium",
|
|
30124
|
+
children: "Retry"
|
|
30125
|
+
}
|
|
30126
|
+
)
|
|
30127
|
+
] }) }),
|
|
30128
|
+
(currentVideo.type === "cycle_completion" || currentVideo.type === "bottleneck" && currentVideo.description.toLowerCase().includes("cycle time")) && currentVideo.cycle_time_seconds || currentVideo.type === "idle_time" || currentVideo.type === "low_value" ? /* @__PURE__ */ jsx("div", { className: "absolute top-3 left-3 z-10 bg-black/60 backdrop-blur-sm px-3 py-1.5 rounded-lg text-white shadow-lg text-xs", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
|
|
30129
|
+
/* @__PURE__ */ jsx("div", { className: `flex-shrink-0 h-2.5 w-2.5 rounded-full ${currentVideo.type === "low_value" || currentVideo.type === "idle_time" ? "bg-purple-400" : isPercentileCategory(activeFilterRef.current) ? activeFilterRef.current === "fast-cycles" ? "bg-green-600" : activeFilterRef.current === "slow-cycles" ? "bg-red-700" : "bg-orange-500" : currentVideo.type === "cycle_completion" ? "bg-blue-600" : "bg-gray-500"} mr-2 animate-pulse` }),
|
|
30130
|
+
(currentVideo.type === "cycle_completion" || currentVideo.type === "bottleneck" && currentVideo.description.toLowerCase().includes("cycle time")) && currentVideo.cycle_time_seconds ? /* @__PURE__ */ jsxs("span", { className: "opacity-90 font-mono bg-black/30 px-2 py-0.5 rounded", children: [
|
|
30131
|
+
"Cycle time: ",
|
|
30132
|
+
currentVideo.cycle_time_seconds.toFixed(1),
|
|
30133
|
+
"s"
|
|
30134
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
30135
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium mr-2", children: getClipTypeLabel(currentVideo) }),
|
|
30136
|
+
/* @__PURE__ */ jsx("span", { className: "opacity-80 hidden sm:inline", children: currentVideo.description })
|
|
30137
|
+
] })
|
|
30138
|
+
] }) }) : (
|
|
30139
|
+
/* Right side display for other video types */
|
|
30140
|
+
/* @__PURE__ */ jsx("div", { className: "absolute top-3 right-3 z-10 bg-black/60 backdrop-blur-sm px-3 py-1.5 rounded-lg text-white shadow-lg text-xs", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
|
|
30141
|
+
/* @__PURE__ */ jsx("div", { className: `flex-shrink-0 h-2.5 w-2.5 rounded-full ${getSeverityColor(currentVideo.severity)} mr-2 animate-pulse` }),
|
|
30142
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium mr-2", children: getClipTypeLabel(currentVideo) }),
|
|
30143
|
+
/* @__PURE__ */ jsx("span", { className: "opacity-80 hidden sm:inline", children: currentVideo.description })
|
|
30144
|
+
] }) })
|
|
30145
|
+
),
|
|
30146
|
+
/* @__PURE__ */ jsx("div", { className: "absolute bottom-0 left-0 right-0 p-3 bg-gradient-to-t from-black/70 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300 z-10", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-white", children: [
|
|
30147
|
+
/* @__PURE__ */ jsx(
|
|
30148
|
+
"button",
|
|
30149
|
+
{
|
|
30150
|
+
onClick: togglePlayback,
|
|
30151
|
+
className: "p-1.5 hover:bg-white/20 rounded-full focus:outline-none focus:ring-2 focus:ring-white/50",
|
|
30152
|
+
"aria-label": isPlaying ? "Pause" : "Play",
|
|
30153
|
+
children: isPlaying ? /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", viewBox: "0 0 20 20", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M18 10a8 8 0 11-16 0 8 8 0 0116 0zM7 8a1 1 0 00-1 1v2a1 1 0 102 0V9a1 1 0 00-1-1zm5 0a1 1 0 00-1 1v2a1 1 0 102 0V9a1 1 0 00-1-1z", clipRule: "evenodd" }) }) : /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", viewBox: "0 0 20 20", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M10 18a8 8 0 100-16 8 8 0 000 16zM9.555 7.168A1 1 0 008 8.118l-.001 3.764a1 1 0 001.555.832l3.196-1.882a1 1 0 000-1.664l-3.196-1.882z", clipRule: "evenodd" }) })
|
|
30154
|
+
}
|
|
30155
|
+
),
|
|
30156
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs font-mono px-2", children: [
|
|
30157
|
+
formatTime2(currentTime),
|
|
30158
|
+
" / ",
|
|
30159
|
+
formatTime2(duration)
|
|
30160
|
+
] }),
|
|
30161
|
+
/* @__PURE__ */ jsx(
|
|
30162
|
+
"input",
|
|
30163
|
+
{
|
|
30164
|
+
type: "range",
|
|
30165
|
+
min: "0",
|
|
30166
|
+
max: duration || 0,
|
|
30167
|
+
value: currentTime,
|
|
30168
|
+
onChange: (e) => {
|
|
30169
|
+
if (videoRef.current) {
|
|
30170
|
+
videoRef.current.currentTime(Number(e.target.value));
|
|
30171
|
+
}
|
|
30172
|
+
},
|
|
30173
|
+
className: "flex-grow mx-3 h-2.5 bg-white/30 rounded-full appearance-none cursor-pointer focus:outline-none focus:ring-2 focus:ring-white/50 touch-manipulation",
|
|
30174
|
+
style: {
|
|
30175
|
+
WebkitAppearance: "none",
|
|
30176
|
+
appearance: "none"
|
|
30177
|
+
},
|
|
30178
|
+
"aria-label": "Seek slider"
|
|
30179
|
+
}
|
|
30180
|
+
)
|
|
30181
|
+
] }) })
|
|
30182
|
+
] }) }) }) : (
|
|
30183
|
+
/* Priority 5: Show "no clips found" only if we have counts and there are truly no clips for workspace */
|
|
30184
|
+
hasInitialLoad && Object.keys(mergedCounts).length > 0 && Object.values(mergedCounts).every((count) => count === 0) ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsxs("div", { className: "text-center p-8", children: [
|
|
30185
|
+
/* @__PURE__ */ jsx("svg", { className: "w-16 h-16 text-gray-300 mx-auto mb-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z" }) }),
|
|
30186
|
+
/* @__PURE__ */ jsx("h3", { className: "text-xl font-medium text-gray-700 mb-2", children: "No Clips Found" }),
|
|
30187
|
+
/* @__PURE__ */ jsx("p", { className: "text-gray-500", children: "There were no video clips found for this workspace today." })
|
|
30188
|
+
] }) }) : (
|
|
30189
|
+
/* Priority 6: Show "no matching clips" only if we have data loaded and specifically no clips for this filter */
|
|
30190
|
+
hasInitialLoad && (mergedCounts[activeFilter] || 0) === 0 ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsxs("div", { className: "text-center p-8", children: [
|
|
30191
|
+
/* @__PURE__ */ jsx("svg", { className: "w-16 h-16 text-gray-300 mx-auto mb-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z" }) }),
|
|
30192
|
+
/* @__PURE__ */ jsx("h3", { className: "text-xl font-medium text-gray-700 mb-2", children: "No Matching Clips" }),
|
|
30193
|
+
/* @__PURE__ */ jsx("p", { className: "text-gray-500", children: "There are no clips matching the selected filter." })
|
|
30194
|
+
] }) }) : (
|
|
30195
|
+
/* Priority 7: Default loading state for any other case */
|
|
30196
|
+
/* @__PURE__ */ jsx("div", { className: "p-4 h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsx("div", { className: "relative h-full", children: /* @__PURE__ */ jsx("div", { className: "relative w-full h-full overflow-hidden rounded-md shadow-inner bg-gray-900 flex items-center justify-center", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading..." }) }) }) })
|
|
30784
30197
|
)
|
|
30785
30198
|
)
|
|
30786
30199
|
)
|
|
@@ -30798,7 +30211,7 @@ var BottlenecksContent = ({
|
|
|
30798
30211
|
currentVideoId: currentVideo?.id,
|
|
30799
30212
|
counts: mergedCounts,
|
|
30800
30213
|
workspaceId,
|
|
30801
|
-
date: date || getOperationalDate(),
|
|
30214
|
+
date: date || getOperationalDate(timezone),
|
|
30802
30215
|
shift: effectiveShift,
|
|
30803
30216
|
onFilterChange: (filterId) => {
|
|
30804
30217
|
updateActiveFilter(filterId);
|
|
@@ -30823,9 +30236,9 @@ var BottlenecksContent = ({
|
|
|
30823
30236
|
}
|
|
30824
30237
|
}
|
|
30825
30238
|
},
|
|
30826
|
-
onClipSelect: (categoryId, clipId) => {
|
|
30827
|
-
console.log(`[BottlenecksContent] Clip selected: ${categoryId}, clipId ${clipId}`);
|
|
30828
|
-
loadAndPlayClipById(clipId, categoryId);
|
|
30239
|
+
onClipSelect: (categoryId, clipId, position) => {
|
|
30240
|
+
console.log(`[BottlenecksContent] Clip selected: ${categoryId}, clipId ${clipId}, position=${position}`);
|
|
30241
|
+
loadAndPlayClipById(clipId, categoryId, position);
|
|
30829
30242
|
const category = categoriesToShow.find((cat) => cat.type === categoryId);
|
|
30830
30243
|
if (category) {
|
|
30831
30244
|
trackCoreEvent(`${category.label} Clip Selected`, {
|
|
@@ -30952,7 +30365,7 @@ var arePropsEqual = (prevProps, nextProps) => {
|
|
|
30952
30365
|
return prevProps.data.efficiency === nextProps.data.efficiency && prevProps.data.trend_score === nextProps.data.trend_score && prevProps.data.workspace_id === nextProps.data.workspace_id && prevProps.data.workspace_name === nextProps.data.workspace_name && prevProps.isBottleneck === nextProps.isBottleneck && prevProps.isLowEfficiency === nextProps.isLowEfficiency && prevProps.isVeryLowEfficiency === nextProps.isVeryLowEfficiency && // Position doesn't need deep equality check as it's generally static
|
|
30953
30366
|
prevProps.position.id === nextProps.position.id;
|
|
30954
30367
|
};
|
|
30955
|
-
var WorkspaceGridItem =
|
|
30368
|
+
var WorkspaceGridItem = React21__default.memo(({
|
|
30956
30369
|
data,
|
|
30957
30370
|
position,
|
|
30958
30371
|
isBottleneck = false,
|
|
@@ -31045,7 +30458,7 @@ var WorkspaceGridItem = React20__default.memo(({
|
|
|
31045
30458
|
);
|
|
31046
30459
|
}, arePropsEqual);
|
|
31047
30460
|
WorkspaceGridItem.displayName = "WorkspaceGridItem";
|
|
31048
|
-
var WorkspaceGrid =
|
|
30461
|
+
var WorkspaceGrid = React21__default.memo(({
|
|
31049
30462
|
workspaces,
|
|
31050
30463
|
isPdfMode = false,
|
|
31051
30464
|
customWorkspacePositions,
|
|
@@ -31239,7 +30652,7 @@ var KPICard = ({
|
|
|
31239
30652
|
}) => {
|
|
31240
30653
|
useThemeConfig();
|
|
31241
30654
|
const { formatNumber } = useFormatNumber();
|
|
31242
|
-
const trendInfo =
|
|
30655
|
+
const trendInfo = React21__default.useMemo(() => {
|
|
31243
30656
|
let trendValue = trend || "neutral";
|
|
31244
30657
|
if (change !== void 0 && trend === void 0) {
|
|
31245
30658
|
trendValue = change > 0 ? "up" : change < 0 ? "down" : "neutral";
|
|
@@ -31262,7 +30675,7 @@ var KPICard = ({
|
|
|
31262
30675
|
const shouldShowTrend = !(change === 0 && trend === void 0);
|
|
31263
30676
|
return { trendValue, Icon: Icon2, colorClass, shouldShowTrend };
|
|
31264
30677
|
}, [trend, change]);
|
|
31265
|
-
const formattedValue =
|
|
30678
|
+
const formattedValue = React21__default.useMemo(() => {
|
|
31266
30679
|
if (title === "Quality Compliance" && typeof value === "number") {
|
|
31267
30680
|
return value.toFixed(1);
|
|
31268
30681
|
}
|
|
@@ -31276,7 +30689,7 @@ var KPICard = ({
|
|
|
31276
30689
|
}
|
|
31277
30690
|
return value;
|
|
31278
30691
|
}, [value, title]);
|
|
31279
|
-
const formattedChange =
|
|
30692
|
+
const formattedChange = React21__default.useMemo(() => {
|
|
31280
30693
|
if (change === void 0 || change === 0) return null;
|
|
31281
30694
|
const absChange = Math.abs(change);
|
|
31282
30695
|
return formatNumber(absChange, { minimumFractionDigits: 0, maximumFractionDigits: 1 });
|
|
@@ -32025,11 +31438,11 @@ var HealthStatusGrid = ({
|
|
|
32025
31438
|
filteredWorkspaces.length === 0 && /* @__PURE__ */ jsx("div", { className: "text-center py-12", children: /* @__PURE__ */ jsx("p", { className: "text-gray-500 dark:text-gray-400", children: searchTerm || statusFilter !== "all" ? "No workspaces found matching your filters." : "No workspaces available." }) })
|
|
32026
31439
|
] });
|
|
32027
31440
|
};
|
|
32028
|
-
var
|
|
31441
|
+
var Timer2 = Timer_default;
|
|
32029
31442
|
var DashboardHeader = memo(({ lineTitle, className = "", headerControls }) => {
|
|
32030
31443
|
const dashboardConfig = useDashboardConfig();
|
|
31444
|
+
const timezone = useAppTimezone();
|
|
32031
31445
|
const getShiftName = () => {
|
|
32032
|
-
const timezone = dashboardConfig.dateTimeConfig?.defaultTimezone || "Asia/Kolkata";
|
|
32033
31446
|
const currentShift = getCurrentShift(timezone, dashboardConfig.shiftConfig);
|
|
32034
31447
|
return currentShift.shiftId === 0 ? "Day" : "Night";
|
|
32035
31448
|
};
|
|
@@ -32048,10 +31461,7 @@ var DashboardHeader = memo(({ lineTitle, className = "", headerControls }) => {
|
|
|
32048
31461
|
/* @__PURE__ */ jsx("div", { className: "h-1.5 w-1.5 sm:h-1.5 sm:w-1.5 md:h-2 md:w-2 rounded-full bg-green-500 animate-pulse ring-1 sm:ring-2 ring-green-500/30 ring-offset-1 flex-shrink-0" })
|
|
32049
31462
|
] }),
|
|
32050
31463
|
/* @__PURE__ */ jsxs("div", { className: "mt-0.5 sm:mt-2 inline-flex flex-wrap items-center gap-1.5 sm:gap-3", children: [
|
|
32051
|
-
/* @__PURE__ */
|
|
32052
|
-
/* @__PURE__ */ jsx(ISTTimer2, {}),
|
|
32053
|
-
" IST"
|
|
32054
|
-
] }),
|
|
31464
|
+
/* @__PURE__ */ jsx("div", { className: "text-[10px] sm:text-xs md:text-sm font-medium text-gray-500 sm:text-gray-600 whitespace-nowrap", children: /* @__PURE__ */ jsx(Timer2, {}) }),
|
|
32055
31465
|
/* @__PURE__ */ jsxs("div", { className: "inline-flex items-center gap-0.5 sm:gap-1", children: [
|
|
32056
31466
|
/* @__PURE__ */ jsx("div", { className: "text-gray-500 sm:text-gray-600 scale-90 sm:scale-100", children: getShiftIcon() }),
|
|
32057
31467
|
/* @__PURE__ */ jsxs("span", { className: "text-[10px] sm:text-xs md:text-sm font-medium text-gray-500 sm:text-gray-600 whitespace-nowrap", children: [
|
|
@@ -32446,7 +31856,7 @@ var Breadcrumbs = ({ items }) => {
|
|
|
32446
31856
|
}
|
|
32447
31857
|
}
|
|
32448
31858
|
};
|
|
32449
|
-
return /* @__PURE__ */ jsx("nav", { "aria-label": "Breadcrumb", className: "mb-1 flex items-center space-x-1 text-xs font-medium text-gray-500 dark:text-gray-400", children: items.map((item, index) => /* @__PURE__ */ jsxs(
|
|
31859
|
+
return /* @__PURE__ */ jsx("nav", { "aria-label": "Breadcrumb", className: "mb-1 flex items-center space-x-1 text-xs font-medium text-gray-500 dark:text-gray-400", children: items.map((item, index) => /* @__PURE__ */ jsxs(React21__default.Fragment, { children: [
|
|
32450
31860
|
index > 0 && /* @__PURE__ */ jsx(ChevronRight, { className: "h-3 w-3 text-gray-400 dark:text-gray-500" }),
|
|
32451
31861
|
/* @__PURE__ */ jsxs(
|
|
32452
31862
|
"span",
|
|
@@ -34903,7 +34313,7 @@ var ThreadSidebar = ({
|
|
|
34903
34313
|
] });
|
|
34904
34314
|
};
|
|
34905
34315
|
var axelProfilePng = "/axel-profile.png";
|
|
34906
|
-
var ProfilePicture =
|
|
34316
|
+
var ProfilePicture = React21__default.memo(({ alt = "Axel", className = "w-8 h-8 sm:w-10 sm:h-10 md:w-12 md:h-12" }) => {
|
|
34907
34317
|
return /* @__PURE__ */ jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsx("div", { className: `${className} rounded-xl overflow-hidden shadow-sm`, children: /* @__PURE__ */ jsx(
|
|
34908
34318
|
"img",
|
|
34909
34319
|
{
|
|
@@ -36887,7 +36297,6 @@ function DebugAuthView() {
|
|
|
36887
36297
|
return /* @__PURE__ */ jsx(DebugAuth, {});
|
|
36888
36298
|
}
|
|
36889
36299
|
var DebugAuthView_default = DebugAuthView;
|
|
36890
|
-
var DEFAULT_TIMEZONE = "Asia/Kolkata";
|
|
36891
36300
|
var DEFAULT_SHIFT_CONFIG2 = {
|
|
36892
36301
|
dayShift: { id: 0, startTime: "06:00" },
|
|
36893
36302
|
nightShift: { id: 1, startTime: "18:00" }
|
|
@@ -36898,10 +36307,12 @@ var FactoryView = ({
|
|
|
36898
36307
|
lineIds,
|
|
36899
36308
|
lineNames = {},
|
|
36900
36309
|
factoryName = "Plant 1",
|
|
36901
|
-
timezone
|
|
36310
|
+
timezone: propTimezone,
|
|
36902
36311
|
shiftConfig = DEFAULT_SHIFT_CONFIG2,
|
|
36903
36312
|
productIds = {}
|
|
36904
36313
|
}) => {
|
|
36314
|
+
const contextTimezone = useAppTimezone();
|
|
36315
|
+
const timezone = propTimezone || contextTimezone;
|
|
36905
36316
|
const effectiveLineIds = useMemo(() => {
|
|
36906
36317
|
if (lineIds && lineIds.length > 0) {
|
|
36907
36318
|
return lineIds;
|
|
@@ -36962,7 +36373,7 @@ var FactoryView = ({
|
|
|
36962
36373
|
return;
|
|
36963
36374
|
}
|
|
36964
36375
|
const { shiftId } = getCurrentShift(timezone, shiftConfig);
|
|
36965
|
-
const date = getOperationalDate();
|
|
36376
|
+
const date = getOperationalDate(timezone);
|
|
36966
36377
|
const hourlyDataPromises = effectiveLineIds.map(
|
|
36967
36378
|
(lineId) => supabase.from("line_hourly_metrics").select("hour, efficiency").eq("line_id", lineId).eq("shift_id", shiftId).eq("date", date).order("hour", { ascending: false }).limit(5)
|
|
36968
36379
|
);
|
|
@@ -37444,6 +36855,7 @@ function HomeView({
|
|
|
37444
36855
|
const [displayNamesInitialized, setDisplayNamesInitialized] = useState(false);
|
|
37445
36856
|
const [hasInitialDataLoaded, setHasInitialDataLoaded] = useState(false);
|
|
37446
36857
|
const dashboardConfig = useDashboardConfig();
|
|
36858
|
+
const timezone = useAppTimezone();
|
|
37447
36859
|
useEffect(() => {
|
|
37448
36860
|
const initDisplayNames = async () => {
|
|
37449
36861
|
try {
|
|
@@ -37508,7 +36920,7 @@ function HomeView({
|
|
|
37508
36920
|
useEffect(() => {
|
|
37509
36921
|
if (!dashboardConfig?.s3Config) return;
|
|
37510
36922
|
if (!workspaceMetrics || workspaceMetrics.length === 0) return;
|
|
37511
|
-
getOperationalDate(dashboardConfig.dateTimeConfig?.defaultTimezone);
|
|
36923
|
+
getOperationalDate(timezone || dashboardConfig.dateTimeConfig?.defaultTimezone || "UTC");
|
|
37512
36924
|
console.log(`[HomeView] Starting optimized prefetch for ${workspaceMetrics.length} workspaces`);
|
|
37513
36925
|
workspaceMetrics.map((ws) => ws.workspace_uuid).filter(Boolean);
|
|
37514
36926
|
}, [dashboardConfig, workspaceMetrics]);
|
|
@@ -37612,7 +37024,7 @@ function HomeView({
|
|
|
37612
37024
|
animate: { opacity: 1, scale: 1 },
|
|
37613
37025
|
transition: { duration: 0.3 },
|
|
37614
37026
|
className: "h-full",
|
|
37615
|
-
children:
|
|
37027
|
+
children: React21__default.createElement(WorkspaceGrid, {
|
|
37616
37028
|
workspaces: memoizedWorkspaceMetrics,
|
|
37617
37029
|
lineNames,
|
|
37618
37030
|
factoryView: factoryViewId,
|
|
@@ -37638,7 +37050,7 @@ function HomeView({
|
|
|
37638
37050
|
animate: { opacity: 1, scale: 1 },
|
|
37639
37051
|
transition: { duration: 0.3 },
|
|
37640
37052
|
className: "h-full",
|
|
37641
|
-
children:
|
|
37053
|
+
children: React21__default.createElement(WorkspaceGrid, {
|
|
37642
37054
|
workspaces: [],
|
|
37643
37055
|
// Show empty grid while loading
|
|
37644
37056
|
lineNames,
|
|
@@ -37665,7 +37077,7 @@ function HomeView({
|
|
|
37665
37077
|
}
|
|
37666
37078
|
);
|
|
37667
37079
|
}
|
|
37668
|
-
var AuthenticatedHomeView = withAuth(
|
|
37080
|
+
var AuthenticatedHomeView = withAuth(React21__default.memo(HomeView));
|
|
37669
37081
|
var HomeView_default = HomeView;
|
|
37670
37082
|
function withWorkspaceDisplayNames(Component3, options = {}) {
|
|
37671
37083
|
const {
|
|
@@ -38048,6 +37460,7 @@ var KPIDetailView = ({
|
|
|
38048
37460
|
backLinkUrl,
|
|
38049
37461
|
onBackClick
|
|
38050
37462
|
}) => {
|
|
37463
|
+
const timezone = useAppTimezone();
|
|
38051
37464
|
const [activeTab, setActiveTab] = useState("overview");
|
|
38052
37465
|
const [currentMonth, setCurrentMonth] = useState(() => {
|
|
38053
37466
|
if (urlMonth && typeof urlMonth === "string") {
|
|
@@ -38084,7 +37497,7 @@ var KPIDetailView = ({
|
|
|
38084
37497
|
}, [urlShift]);
|
|
38085
37498
|
const supabase = useSupabase();
|
|
38086
37499
|
const dashboardConfig = useDashboardConfig();
|
|
38087
|
-
const configuredTimezone = dashboardConfig.dateTimeConfig?.defaultTimezone || "
|
|
37500
|
+
const configuredTimezone = timezone || dashboardConfig.dateTimeConfig?.defaultTimezone || "UTC";
|
|
38088
37501
|
useMemo(() => getCurrentTimeInZone(configuredTimezone), [configuredTimezone]);
|
|
38089
37502
|
const supervisorEnabled = dashboardConfig?.supervisorConfig?.enabled || false;
|
|
38090
37503
|
useEffect(() => {
|
|
@@ -38110,18 +37523,22 @@ var KPIDetailView = ({
|
|
|
38110
37523
|
}
|
|
38111
37524
|
}, [getShiftName]);
|
|
38112
37525
|
const getDaysDifference2 = useCallback((date) => {
|
|
38113
|
-
const today = /* @__PURE__ */ new Date();
|
|
38114
37526
|
const compareDate = new Date(date);
|
|
38115
|
-
const
|
|
37527
|
+
const shiftStartTime = dashboardConfig.shiftConfig?.dayShift?.startTime || "06:00";
|
|
37528
|
+
const operationalTodayString = getOperationalDate(configuredTimezone, /* @__PURE__ */ new Date(), shiftStartTime);
|
|
37529
|
+
const operationalTodayDate = new Date(operationalTodayString);
|
|
38116
37530
|
const compareDateInZone = new Date(compareDate.toLocaleString("en-US", { timeZone: configuredTimezone }));
|
|
38117
|
-
|
|
37531
|
+
operationalTodayDate.setHours(0, 0, 0, 0);
|
|
38118
37532
|
compareDateInZone.setHours(0, 0, 0, 0);
|
|
38119
|
-
const diffTime =
|
|
38120
|
-
const diffDays = Math.
|
|
37533
|
+
const diffTime = compareDateInZone.getTime() - operationalTodayDate.getTime();
|
|
37534
|
+
const diffDays = Math.round(diffTime / (1e3 * 60 * 60 * 24));
|
|
38121
37535
|
if (diffDays === 0) return "Today";
|
|
38122
|
-
if (diffDays === 1) return "Yesterday";
|
|
38123
|
-
|
|
38124
|
-
|
|
37536
|
+
if (diffDays === -1) return "Yesterday";
|
|
37537
|
+
if (diffDays === 1) return "Tomorrow";
|
|
37538
|
+
if (diffDays < -1) return `${Math.abs(diffDays)} days ago`;
|
|
37539
|
+
if (diffDays > 1) return `${diffDays} days ahead`;
|
|
37540
|
+
return "Today";
|
|
37541
|
+
}, [configuredTimezone, dashboardConfig.shiftConfig]);
|
|
38125
37542
|
const {
|
|
38126
37543
|
metrics: metrics2,
|
|
38127
37544
|
lineDetails,
|
|
@@ -38236,7 +37653,7 @@ var KPIDetailView = ({
|
|
|
38236
37653
|
factory_id: lineDetails.factory_id,
|
|
38237
37654
|
factory_name: lineDetails.factory.factory_name,
|
|
38238
37655
|
shift_id: metrics2.shift_id ?? 0,
|
|
38239
|
-
date: metrics2.date || getOperationalDate(),
|
|
37656
|
+
date: metrics2.date || getOperationalDate(timezone || "UTC"),
|
|
38240
37657
|
metrics: {
|
|
38241
37658
|
avg_efficiency: metrics2.avg_efficiency ?? 0,
|
|
38242
37659
|
avg_cycle_time: metrics2.avg_cycle_time ?? 0,
|
|
@@ -38783,7 +38200,7 @@ var KPIDetailViewWithDisplayNames = withSelectedLineDisplayNames(KPIDetailView);
|
|
|
38783
38200
|
var KPIDetailView_default = KPIDetailViewWithDisplayNames;
|
|
38784
38201
|
var LineCard = ({ line, onClick, supervisorEnabled = false }) => {
|
|
38785
38202
|
const { kpis, isLoading, error } = useLineKPIs({ lineId: line.id });
|
|
38786
|
-
const isOnTrack =
|
|
38203
|
+
const isOnTrack = React21__default.useMemo(() => {
|
|
38787
38204
|
if (!kpis) return null;
|
|
38788
38205
|
return kpis.efficiency.value > 90;
|
|
38789
38206
|
}, [kpis]);
|
|
@@ -38891,7 +38308,8 @@ var KPIsOverviewView = ({
|
|
|
38891
38308
|
const dateTimeConfig = useDateTimeConfig();
|
|
38892
38309
|
const shiftConfig = useShiftConfig();
|
|
38893
38310
|
const supervisorEnabled = dashboardConfig?.supervisorConfig?.enabled || false;
|
|
38894
|
-
const
|
|
38311
|
+
const dbTimezone = useAppTimezone();
|
|
38312
|
+
const configuredTimezone = dbTimezone || dateTimeConfig.defaultTimezone || "UTC";
|
|
38895
38313
|
useEffect(() => {
|
|
38896
38314
|
const fetchLines = async () => {
|
|
38897
38315
|
try {
|
|
@@ -40586,7 +40004,7 @@ var ShiftsView = ({
|
|
|
40586
40004
|
] })
|
|
40587
40005
|
] });
|
|
40588
40006
|
};
|
|
40589
|
-
var AuthenticatedShiftsView = withAuth(
|
|
40007
|
+
var AuthenticatedShiftsView = withAuth(React21__default.memo(ShiftsView));
|
|
40590
40008
|
var ShiftsView_default = ShiftsView;
|
|
40591
40009
|
|
|
40592
40010
|
// src/lib/constants/actions.ts
|
|
@@ -41802,6 +41220,7 @@ var TargetsView = ({
|
|
|
41802
41220
|
userId,
|
|
41803
41221
|
onSaveChanges
|
|
41804
41222
|
}) => {
|
|
41223
|
+
const timezone = useAppTimezone();
|
|
41805
41224
|
const initialLineWorkspaces = useMemo(() => {
|
|
41806
41225
|
return lineIds.reduce((acc, lineId) => ({
|
|
41807
41226
|
...acc,
|
|
@@ -42020,7 +41439,7 @@ var TargetsView = ({
|
|
|
42020
41439
|
updatedLineWorkspaces[result.lineId].factoryId = result.factoryId;
|
|
42021
41440
|
}
|
|
42022
41441
|
});
|
|
42023
|
-
const currentDate = getOperationalDate();
|
|
41442
|
+
const currentDate = getOperationalDate(timezone);
|
|
42024
41443
|
for (const lineId of lineIds) {
|
|
42025
41444
|
if (!updatedLineWorkspaces[lineId]?.factoryId) {
|
|
42026
41445
|
console.warn(`Skipping workspace fetch for line ${lineId} - no factory ID`);
|
|
@@ -42101,7 +41520,7 @@ var TargetsView = ({
|
|
|
42101
41520
|
}, [lineIds, companyId, loadOperatingHours]);
|
|
42102
41521
|
const fetchAllShiftsData = useCallback(async (currentWorkspaces) => {
|
|
42103
41522
|
if (!supabase) return;
|
|
42104
|
-
const currentDate = getOperationalDate();
|
|
41523
|
+
const currentDate = getOperationalDate(timezone);
|
|
42105
41524
|
const newAllShiftsData = {
|
|
42106
41525
|
0: JSON.parse(JSON.stringify(currentWorkspaces)),
|
|
42107
41526
|
// Deep clone for day shift
|
|
@@ -42340,7 +41759,7 @@ var TargetsView = ({
|
|
|
42340
41759
|
return;
|
|
42341
41760
|
}
|
|
42342
41761
|
const currentFactoryId = lineDataToSave.factoryId;
|
|
42343
|
-
const currentDate = getOperationalDate();
|
|
41762
|
+
const currentDate = getOperationalDate(timezone);
|
|
42344
41763
|
console.log(`[handleSaveLine] currentDate: ${currentDate}, selectedShift: ${selectedShift}`);
|
|
42345
41764
|
const workspaceThresholdUpdates = lineDataToSave.workspaces.map((ws) => ({
|
|
42346
41765
|
line_id: lineId,
|
|
@@ -42502,37 +41921,43 @@ var TargetsView = ({
|
|
|
42502
41921
|
};
|
|
42503
41922
|
var TargetsViewWithDisplayNames = withAllWorkspaceDisplayNames(TargetsView);
|
|
42504
41923
|
var TargetsView_default = TargetsViewWithDisplayNames;
|
|
42505
|
-
var AuthenticatedTargetsView = withAuth(
|
|
41924
|
+
var AuthenticatedTargetsView = withAuth(React21__default.memo(TargetsViewWithDisplayNames));
|
|
42506
41925
|
|
|
42507
41926
|
// src/views/workspace-detail-view.utils.ts
|
|
42508
|
-
var
|
|
41927
|
+
var formatDateInTimezone = (date = /* @__PURE__ */ new Date(), timezone, options) => {
|
|
42509
41928
|
const defaultOptions = {
|
|
42510
41929
|
day: "numeric",
|
|
42511
41930
|
month: "long",
|
|
42512
41931
|
year: "numeric",
|
|
42513
|
-
timeZone:
|
|
41932
|
+
timeZone: timezone
|
|
42514
41933
|
};
|
|
42515
|
-
const formatter = new Intl.DateTimeFormat("en-
|
|
41934
|
+
const formatter = new Intl.DateTimeFormat("en-US", {
|
|
42516
41935
|
...defaultOptions,
|
|
42517
41936
|
...options
|
|
42518
41937
|
});
|
|
42519
41938
|
return formatter.format(date);
|
|
42520
41939
|
};
|
|
41940
|
+
var formatISTDate2 = (date = /* @__PURE__ */ new Date(), options) => {
|
|
41941
|
+
return formatDateInTimezone(date, "Asia/Kolkata", options);
|
|
41942
|
+
};
|
|
42521
41943
|
var formatWorkspaceName3 = (name, lineId) => {
|
|
42522
41944
|
return getWorkspaceDisplayName(name, lineId);
|
|
42523
41945
|
};
|
|
42524
|
-
var getDaysDifference = (date) => {
|
|
42525
|
-
const today = /* @__PURE__ */ new Date();
|
|
41946
|
+
var getDaysDifference = (date, timezone = "UTC", shiftStartTime = "06:00") => {
|
|
42526
41947
|
const compareDate = new Date(date);
|
|
42527
|
-
const
|
|
42528
|
-
const
|
|
42529
|
-
|
|
42530
|
-
|
|
42531
|
-
|
|
42532
|
-
const
|
|
41948
|
+
const operationalTodayString = getOperationalDate(timezone, /* @__PURE__ */ new Date(), shiftStartTime);
|
|
41949
|
+
const operationalTodayDate = new Date(operationalTodayString);
|
|
41950
|
+
const compareDateInTz = new Date(compareDate.toLocaleString("en-US", { timeZone: timezone }));
|
|
41951
|
+
operationalTodayDate.setHours(0, 0, 0, 0);
|
|
41952
|
+
compareDateInTz.setHours(0, 0, 0, 0);
|
|
41953
|
+
const diffTime = compareDateInTz.getTime() - operationalTodayDate.getTime();
|
|
41954
|
+
const diffDays = Math.round(diffTime / (1e3 * 60 * 60 * 24));
|
|
42533
41955
|
if (diffDays === 0) return "Today";
|
|
42534
|
-
if (diffDays === 1) return "Yesterday";
|
|
42535
|
-
|
|
41956
|
+
if (diffDays === -1) return "Yesterday";
|
|
41957
|
+
if (diffDays === 1) return "Tomorrow";
|
|
41958
|
+
if (diffDays < -1) return `${Math.abs(diffDays)} days ago`;
|
|
41959
|
+
if (diffDays > 1) return `${diffDays} days ahead`;
|
|
41960
|
+
return "Today";
|
|
42536
41961
|
};
|
|
42537
41962
|
var getInitialTab = (sourceType, defaultTab, fromMonthly, urlDate) => {
|
|
42538
41963
|
if (sourceType === "lineMonthlyHistory") {
|
|
@@ -42580,7 +42005,8 @@ var WorkspaceDetailView = ({
|
|
|
42580
42005
|
const [previousView, setPreviousView] = useState("dashboard");
|
|
42581
42006
|
const [monthlyData, setMonthlyData] = useState([]);
|
|
42582
42007
|
const [monthlyDataLoading, setMonthlyDataLoading] = useState(false);
|
|
42583
|
-
const
|
|
42008
|
+
const timezone = useAppTimezone();
|
|
42009
|
+
const today = new Date((/* @__PURE__ */ new Date()).toLocaleString("en-US", { timeZone: timezone }));
|
|
42584
42010
|
const [selectedMonth, setSelectedMonth] = useState(today.getMonth());
|
|
42585
42011
|
const [selectedYear, setSelectedYear] = useState(today.getFullYear());
|
|
42586
42012
|
const [selectedShift, setSelectedShift] = useState("day");
|
|
@@ -42637,7 +42063,7 @@ var WorkspaceDetailView = ({
|
|
|
42637
42063
|
error: liveError
|
|
42638
42064
|
} = useWorkspaceDetailedMetrics(
|
|
42639
42065
|
workspaceId || "",
|
|
42640
|
-
getOperationalDate(),
|
|
42066
|
+
getOperationalDate(timezone),
|
|
42641
42067
|
void 0
|
|
42642
42068
|
);
|
|
42643
42069
|
const workspace = isHistoricView ? historicMetrics : liveMetrics;
|
|
@@ -42736,7 +42162,7 @@ var WorkspaceDetailView = ({
|
|
|
42736
42162
|
}, [isClipsEnabled, activeTab]);
|
|
42737
42163
|
useEffect(() => {
|
|
42738
42164
|
if (liveMetrics && !date && !shift) {
|
|
42739
|
-
const currentDate = getOperationalDate();
|
|
42165
|
+
const currentDate = getOperationalDate(timezone);
|
|
42740
42166
|
if (liveMetrics.date !== currentDate) {
|
|
42741
42167
|
setUsingFallbackData(true);
|
|
42742
42168
|
if (activeTab !== "monthly_history") {
|
|
@@ -42754,10 +42180,10 @@ var WorkspaceDetailView = ({
|
|
|
42754
42180
|
return date;
|
|
42755
42181
|
} catch (e) {
|
|
42756
42182
|
console.error("Error parsing historic date:", e);
|
|
42757
|
-
return getOperationalDate();
|
|
42183
|
+
return getOperationalDate(timezone);
|
|
42758
42184
|
}
|
|
42759
42185
|
}
|
|
42760
|
-
return getOperationalDate();
|
|
42186
|
+
return getOperationalDate(timezone);
|
|
42761
42187
|
}, [isHistoricView, date]);
|
|
42762
42188
|
const handleMonthlyDataLoaded = useCallback((data) => {
|
|
42763
42189
|
console.log("[handleMonthlyDataLoaded] Received data:", {
|
|
@@ -43004,7 +42430,7 @@ var WorkspaceDetailView = ({
|
|
|
43004
42430
|
/* @__PURE__ */ jsx("div", { className: "text-gray-700 scale-90", children: getShiftIcon(workspace.shift_type) }),
|
|
43005
42431
|
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-gray-700", children: workspace.shift_type })
|
|
43006
42432
|
] }),
|
|
43007
|
-
!date && !shift && !usingFallbackData ? /* @__PURE__ */ jsx("div", { className: "inline-flex items-center px-2.5 py-1 bg-green-100 rounded-full", children: /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-green-700", children: /* @__PURE__ */ jsx(LiveTimer, {}) }) }) : date ? /* @__PURE__ */ jsx("div", { className: "inline-flex items-center px-2.5 py-1 bg-blue-100 rounded-full", children: /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-blue-700", children: getDaysDifference(workspace.date) }) }) : usingFallbackData ? /* @__PURE__ */ jsx("div", { className: "inline-flex items-center px-2.5 py-1 bg-amber-100 rounded-full", children: /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-amber-700", children: getDaysDifference(workspace.date) }) }) : null
|
|
42433
|
+
!date && !shift && !usingFallbackData ? /* @__PURE__ */ jsx("div", { className: "inline-flex items-center px-2.5 py-1 bg-green-100 rounded-full", children: /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-green-700", children: /* @__PURE__ */ jsx(LiveTimer, {}) }) }) : date ? /* @__PURE__ */ jsx("div", { className: "inline-flex items-center px-2.5 py-1 bg-blue-100 rounded-full", children: /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-blue-700", children: getDaysDifference(workspace.date, timezone, dashboardConfig?.shiftConfig?.dayShift?.startTime || "06:00") }) }) : usingFallbackData ? /* @__PURE__ */ jsx("div", { className: "inline-flex items-center px-2.5 py-1 bg-amber-100 rounded-full", children: /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-amber-700", children: getDaysDifference(workspace.date, timezone, dashboardConfig?.shiftConfig?.dayShift?.startTime || "06:00") }) }) : null
|
|
43008
42434
|
] }),
|
|
43009
42435
|
/* @__PURE__ */ jsx("div", { className: "hidden sm:block mt-3 bg-blue-50 px-3 py-2 rounded-lg", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-center gap-3 md:gap-4", children: [
|
|
43010
42436
|
!date && !shift && !usingFallbackData && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -43014,13 +42440,13 @@ var WorkspaceDetailView = ({
|
|
|
43014
42440
|
/* @__PURE__ */ jsx("span", { className: "text-sm md:text-base font-medium text-blue-600", children: formatISTDate2(new Date(workspace.date)) }),
|
|
43015
42441
|
/* @__PURE__ */ jsx("div", { className: "w-px h-4 bg-blue-300" }),
|
|
43016
42442
|
date && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
43017
|
-
/* @__PURE__ */ jsx("span", { className: "px-2 py-1 text-xs font-medium bg-blue-200 text-blue-800 rounded-md", children: getDaysDifference(workspace.date) }),
|
|
42443
|
+
/* @__PURE__ */ jsx("span", { className: "px-2 py-1 text-xs font-medium bg-blue-200 text-blue-800 rounded-md", children: getDaysDifference(workspace.date, timezone, dashboardConfig?.shiftConfig?.dayShift?.startTime || "06:00") }),
|
|
43018
42444
|
/* @__PURE__ */ jsx("div", { className: "w-px h-4 bg-blue-300" })
|
|
43019
42445
|
] }),
|
|
43020
42446
|
!date && !shift && usingFallbackData && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
43021
42447
|
/* @__PURE__ */ jsxs("span", { className: "px-2 py-1 text-xs font-medium bg-amber-100 text-amber-700 rounded-md", children: [
|
|
43022
42448
|
"Latest available data (",
|
|
43023
|
-
getDaysDifference(workspace.date),
|
|
42449
|
+
getDaysDifference(workspace.date, timezone, dashboardConfig?.shiftConfig?.dayShift?.startTime || "06:00"),
|
|
43024
42450
|
")"
|
|
43025
42451
|
] }),
|
|
43026
42452
|
/* @__PURE__ */ jsx("div", { className: "w-px h-4 bg-blue-300" })
|
|
@@ -43583,7 +43009,8 @@ var WorkspaceHealthView = ({
|
|
|
43583
43009
|
}) => {
|
|
43584
43010
|
const router = useRouter();
|
|
43585
43011
|
const [groupBy, setGroupBy] = useState("line");
|
|
43586
|
-
const
|
|
43012
|
+
const timezone = useAppTimezone();
|
|
43013
|
+
const operationalDate = getOperationalDate(timezone || "UTC");
|
|
43587
43014
|
const currentHour = (/* @__PURE__ */ new Date()).getHours();
|
|
43588
43015
|
const isNightShift = currentHour >= 18 || currentHour < 6;
|
|
43589
43016
|
const shiftType = isNightShift ? "Night" : "Day";
|
|
@@ -44060,7 +43487,7 @@ var S3Service = class {
|
|
|
44060
43487
|
/**
|
|
44061
43488
|
* List S3 clips for a specific workspace and date
|
|
44062
43489
|
*/
|
|
44063
|
-
async listS3Clips(workspaceId, date
|
|
43490
|
+
async listS3Clips(workspaceId, date) {
|
|
44064
43491
|
const prefix = `sop_violations/${workspaceId}/${date}/`;
|
|
44065
43492
|
if (this.isSimulated()) {
|
|
44066
43493
|
console.log("Running in simulated mode - generating mock data for:", prefix);
|
|
@@ -44219,7 +43646,7 @@ var S3Service = class {
|
|
|
44219
43646
|
/**
|
|
44220
43647
|
* Get all clips for a workspace on a specific date
|
|
44221
43648
|
*/
|
|
44222
|
-
async getWorkspaceClips(workspaceId, date
|
|
43649
|
+
async getWorkspaceClips(workspaceId, date) {
|
|
44223
43650
|
try {
|
|
44224
43651
|
if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) {
|
|
44225
43652
|
throw new Error("Invalid date format. Use YYYY-MM-DD.");
|
|
@@ -44476,4 +43903,175 @@ var streamProxyConfig = {
|
|
|
44476
43903
|
}
|
|
44477
43904
|
};
|
|
44478
43905
|
|
|
44479
|
-
|
|
43906
|
+
// src/lib/api/s3-clips-parser.ts
|
|
43907
|
+
function parseS3Uri(s3Uri, sopCategories) {
|
|
43908
|
+
const path = new URL(s3Uri).pathname;
|
|
43909
|
+
const parts = path.split("/").filter((p) => p);
|
|
43910
|
+
if (s3Uri.includes("missed_qchecks")) {
|
|
43911
|
+
console.warn(`Skipping missed_qchecks URI in parseS3Uri: ${s3Uri}`);
|
|
43912
|
+
return null;
|
|
43913
|
+
}
|
|
43914
|
+
if (parts.length < 8) {
|
|
43915
|
+
console.warn(`Invalid S3 path structure: ${s3Uri} - Too few parts: ${parts.length}, expected at least 8`);
|
|
43916
|
+
return null;
|
|
43917
|
+
}
|
|
43918
|
+
try {
|
|
43919
|
+
const datePart = parts[2];
|
|
43920
|
+
const shiftPart = parts[3];
|
|
43921
|
+
const violationType = parts[4];
|
|
43922
|
+
let folderName = "";
|
|
43923
|
+
let timestamp = "";
|
|
43924
|
+
for (let i = 5; i < parts.length; i++) {
|
|
43925
|
+
const part = parts[i];
|
|
43926
|
+
if (part && part.includes("_") && /\d{8}_\d{6}/.test(part)) {
|
|
43927
|
+
folderName = part;
|
|
43928
|
+
const timeMatch = folderName.match(/_(\d{8})_(\d{6})_/);
|
|
43929
|
+
if (timeMatch) {
|
|
43930
|
+
timestamp = `${timeMatch[2].substring(0, 2)}:${timeMatch[2].substring(2, 4)}:${timeMatch[2].substring(4, 6)}`;
|
|
43931
|
+
break;
|
|
43932
|
+
}
|
|
43933
|
+
}
|
|
43934
|
+
}
|
|
43935
|
+
if (!timestamp) {
|
|
43936
|
+
console.warn(`Couldn't extract timestamp from any part: ${parts.join("/")}`);
|
|
43937
|
+
timestamp = "00:00:00";
|
|
43938
|
+
}
|
|
43939
|
+
let severity = "low";
|
|
43940
|
+
let type = "bottleneck";
|
|
43941
|
+
let description = "Analysis Clip";
|
|
43942
|
+
const normalizedViolationType = violationType.toLowerCase().trim();
|
|
43943
|
+
if (sopCategories && sopCategories.length > 0) {
|
|
43944
|
+
const matchedCategory = sopCategories.find((category) => {
|
|
43945
|
+
const categoryId = category.id.toLowerCase();
|
|
43946
|
+
const s3FolderName = (category.s3FolderName || category.id).toLowerCase();
|
|
43947
|
+
return categoryId === normalizedViolationType || s3FolderName === normalizedViolationType || // Also check for partial matches for flexibility
|
|
43948
|
+
normalizedViolationType.includes(categoryId) || normalizedViolationType.includes(s3FolderName);
|
|
43949
|
+
});
|
|
43950
|
+
if (matchedCategory) {
|
|
43951
|
+
type = matchedCategory.id;
|
|
43952
|
+
description = matchedCategory.description || matchedCategory.label;
|
|
43953
|
+
if (matchedCategory.color.includes("red")) {
|
|
43954
|
+
severity = "high";
|
|
43955
|
+
} else if (matchedCategory.color.includes("yellow") || matchedCategory.color.includes("orange")) {
|
|
43956
|
+
severity = "medium";
|
|
43957
|
+
} else {
|
|
43958
|
+
severity = "low";
|
|
43959
|
+
}
|
|
43960
|
+
console.log(`Matched SOP category: ${matchedCategory.id} for violation type: ${violationType}`);
|
|
43961
|
+
return { timestamp, severity, description, type, originalUri: s3Uri };
|
|
43962
|
+
}
|
|
43963
|
+
}
|
|
43964
|
+
switch (normalizedViolationType) {
|
|
43965
|
+
case "idle_time":
|
|
43966
|
+
case "idle":
|
|
43967
|
+
case "low_value":
|
|
43968
|
+
case "low value":
|
|
43969
|
+
case "low_value_moment":
|
|
43970
|
+
case "low_value_moments":
|
|
43971
|
+
case "low value moment":
|
|
43972
|
+
case "low value moments":
|
|
43973
|
+
type = "low_value";
|
|
43974
|
+
severity = "low";
|
|
43975
|
+
description = "Idle Time Detected";
|
|
43976
|
+
break;
|
|
43977
|
+
case "sop_deviation":
|
|
43978
|
+
type = "missing_quality_check";
|
|
43979
|
+
severity = "high";
|
|
43980
|
+
description = "SOP Deviations";
|
|
43981
|
+
break;
|
|
43982
|
+
case "long_cycle_time":
|
|
43983
|
+
severity = "high";
|
|
43984
|
+
type = "long_cycle_time";
|
|
43985
|
+
description = "Long Cycle Time Detected";
|
|
43986
|
+
break;
|
|
43987
|
+
case "best_cycle_time":
|
|
43988
|
+
type = "best_cycle_time";
|
|
43989
|
+
severity = "low";
|
|
43990
|
+
description = "Best Cycle Time Performance";
|
|
43991
|
+
break;
|
|
43992
|
+
case "worst_cycle_time":
|
|
43993
|
+
type = "worst_cycle_time";
|
|
43994
|
+
severity = "high";
|
|
43995
|
+
description = "Worst Cycle Time Performance";
|
|
43996
|
+
break;
|
|
43997
|
+
case "cycle_completion":
|
|
43998
|
+
case "completed_cycles":
|
|
43999
|
+
case "completed_cycle":
|
|
44000
|
+
type = "cycle_completion";
|
|
44001
|
+
severity = "low";
|
|
44002
|
+
description = "Cycle Completion";
|
|
44003
|
+
break;
|
|
44004
|
+
case "running_cycle":
|
|
44005
|
+
case "active_cycle":
|
|
44006
|
+
case "production_cycle":
|
|
44007
|
+
type = "running_cycle";
|
|
44008
|
+
severity = "low";
|
|
44009
|
+
description = "Active Production Cycle";
|
|
44010
|
+
break;
|
|
44011
|
+
case "setup_state":
|
|
44012
|
+
case "machine_setup":
|
|
44013
|
+
case "line_setup":
|
|
44014
|
+
type = "setup_state";
|
|
44015
|
+
severity = "medium";
|
|
44016
|
+
description = "Machine Setup Activity";
|
|
44017
|
+
break;
|
|
44018
|
+
case "medium_bottleneck":
|
|
44019
|
+
severity = "medium";
|
|
44020
|
+
description = "Medium Bottleneck Identified";
|
|
44021
|
+
break;
|
|
44022
|
+
case "minor_bottleneck":
|
|
44023
|
+
case "mild_bottleneck":
|
|
44024
|
+
severity = "low";
|
|
44025
|
+
description = "Minor Bottleneck Identified";
|
|
44026
|
+
break;
|
|
44027
|
+
default:
|
|
44028
|
+
if (normalizedViolationType.includes("sop") && normalizedViolationType.includes("deviation")) {
|
|
44029
|
+
type = "missing_quality_check";
|
|
44030
|
+
severity = "high";
|
|
44031
|
+
description = "SOP Deviations";
|
|
44032
|
+
} else if (normalizedViolationType.includes("worst") && normalizedViolationType.includes("cycle")) {
|
|
44033
|
+
type = "worst_cycle_time";
|
|
44034
|
+
severity = "high";
|
|
44035
|
+
description = "Worst Cycle Time Performance";
|
|
44036
|
+
} else if (normalizedViolationType.includes("best") && normalizedViolationType.includes("cycle")) {
|
|
44037
|
+
type = "best_cycle_time";
|
|
44038
|
+
severity = "low";
|
|
44039
|
+
description = "Best Cycle Time Performance";
|
|
44040
|
+
} else if (normalizedViolationType.includes("long") && normalizedViolationType.includes("cycle")) {
|
|
44041
|
+
type = "long_cycle_time";
|
|
44042
|
+
severity = "high";
|
|
44043
|
+
description = "Long Cycle Time Detected";
|
|
44044
|
+
} else if (normalizedViolationType.includes("cycle") && (normalizedViolationType.includes("completion") || normalizedViolationType.includes("complete"))) {
|
|
44045
|
+
type = "cycle_completion";
|
|
44046
|
+
severity = "low";
|
|
44047
|
+
description = "Cycle Completion";
|
|
44048
|
+
} else if (normalizedViolationType.includes("running") && normalizedViolationType.includes("cycle")) {
|
|
44049
|
+
type = "running_cycle";
|
|
44050
|
+
severity = "low";
|
|
44051
|
+
description = "Active Production Cycle";
|
|
44052
|
+
} else if (normalizedViolationType.includes("setup") || normalizedViolationType.includes("machine") && normalizedViolationType.includes("setup")) {
|
|
44053
|
+
type = "setup_state";
|
|
44054
|
+
severity = "medium";
|
|
44055
|
+
description = "Machine Setup Activity";
|
|
44056
|
+
} else {
|
|
44057
|
+
description = `Clip type: ${violationType.replace(/_/g, " ")}`;
|
|
44058
|
+
console.log(`Detected unknown violation type: ${violationType} in URI: ${s3Uri}`);
|
|
44059
|
+
}
|
|
44060
|
+
break;
|
|
44061
|
+
}
|
|
44062
|
+
return { timestamp, severity, description, type, originalUri: s3Uri };
|
|
44063
|
+
} catch (error) {
|
|
44064
|
+
console.error(`Error parsing S3 URI: ${s3Uri}`, error);
|
|
44065
|
+
return null;
|
|
44066
|
+
}
|
|
44067
|
+
}
|
|
44068
|
+
function shuffleArray(array) {
|
|
44069
|
+
const shuffled = [...array];
|
|
44070
|
+
for (let i = shuffled.length - 1; i > 0; i--) {
|
|
44071
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
44072
|
+
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
|
|
44073
|
+
}
|
|
44074
|
+
return shuffled;
|
|
44075
|
+
}
|
|
44076
|
+
|
|
44077
|
+
export { ACTION_NAMES, AIAgentView_default as AIAgentView, AdvancedFilterDialog, AdvancedFilterPanel, AudioService, AuthCallback, AuthCallbackView_default as AuthCallbackView, AuthProvider, AuthenticatedFactoryView, AuthenticatedHelpView, AuthenticatedHomeView, AuthenticatedShiftsView, AuthenticatedTargetsView, AuthenticatedWorkspaceHealthView, BackButton, BackButtonMinimal, BarChart, BaseHistoryCalendar, BottlenecksContent, BreakNotificationPopup, CachePrefetchStatus, Card2 as Card, CardContent2 as CardContent, CardDescription2 as CardDescription, CardFooter2 as CardFooter, CardHeader2 as CardHeader, CardTitle2 as CardTitle, ClipFilterProvider, CompactWorkspaceHealthCard, CongratulationsOverlay, 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_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, EmptyStateMessage, EncouragementOverlay, FactoryView_default as FactoryView, FileManagerFilters, FilterDialogTrigger, FirstTimeLoginDebug, FirstTimeLoginHandler, GaugeChart, GridComponentsPlaceholder, HamburgerButton, Header, HealthStatusGrid, HealthStatusIndicator, HelpView_default as HelpView, HomeView_default as HomeView, HourlyOutputChart2 as HourlyOutputChart, ISTTimer_default as ISTTimer, InlineEditableText, InteractiveOnboardingTour, 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, MainLayout, MetricCard_default as MetricCard, MinimalOnboardingPopup, NewClipsNotification, NoWorkspaceData, OnboardingDemo, OnboardingTour, OptifyeAgentClient, OptifyeLogoLoader_default as OptifyeLogoLoader, OutputProgressChart, PageHeader, PieChart4 as PieChart, 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, SimpleOnboardingPopup, SingleVideoStream_default as SingleVideoStream, Skeleton, SubscriptionManager, SubscriptionManagerProvider, SupabaseProvider, SupervisorDropdown_default as SupervisorDropdown, SupervisorManagementView_default as SupervisorManagementView, SupervisorService, TargetWorkspaceGrid, TargetsView_default as TargetsView, ThreadSidebar, TicketHistory_default as TicketHistory, TicketHistoryService, TimeDisplay_default as TimeDisplay, TimePickerDropdown, Timer_default as Timer, TimezoneProvider, TimezoneService, 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, createLinesService, createStreamProxyHandler, createSupabaseClient, createSupervisorService, createThrottledReload, createUserService, dashboardService, deleteThread, forceRefreshWorkspaceDisplayNames, formatDateInZone, formatDateTimeInZone, formatISTDate, formatIdleTime, formatTimeInZone, fromUrlFriendlyName, getAllLineDisplayNames, getAllThreadMessages, getAllWorkspaceDisplayNamesAsync, getAnonClient, getCameraNumber, getCompanyMetricsTableName, getConfigurableShortWorkspaceDisplayName, getConfigurableWorkspaceDisplayName, getConfiguredLineIds, getCoreSessionRecordingProperties, getCoreSessionReplayUrl, getCurrentShift, getCurrentTimeInZone, getDashboardHeaderTimeInZone, getDaysDifferenceInZone, getDefaultCameraStreamUrl, getDefaultLineId, getDefaultTabForWorkspace, getLineDisplayName, getManufacturingInsights, getMetricsTablePrefix, getOperationalDate, getS3SignedUrl, getS3VideoSrc, getShortWorkspaceDisplayName, getShortWorkspaceDisplayNameAsync, getStoredWorkspaceMappings, getSubscriptionManager, getThreadMessages, getUserThreads, getUserThreadsPaginated, getWorkspaceDisplayName, getWorkspaceDisplayNameAsync, getWorkspaceDisplayNamesMap, getWorkspaceFromUrl, getWorkspaceNavigationParams, identifyCoreUser, initializeCoreMixpanel, isLegacyConfiguration, isPrefetchError, 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, useAllWorkspaceMetrics, useAnalyticsConfig, useAppTimezone, useAudioService, useAuth, useAuthConfig, useCanSaveTargets, useClipFilter, useClipTypes, useClipTypesWithCounts, useComponentOverride, useCustomConfig, useDashboardConfig, useDashboardMetrics, useDatabaseConfig, useDateFormatter, useDateTimeConfig, useEndpointsConfig, useEntityConfig, useFactoryOverviewMetrics, useFeatureFlags, useFormatNumber, useHistoricWorkspaceMetrics, useHlsStream, useHlsStreamWithCropping, useHookOverride, useHourEndTimer, useHourlyTargetAchievements, useHourlyTargetMisses, useLeaderboardMetrics, useLineDetailedMetrics, useLineKPIs, useLineMetrics, useLineWorkspaceMetrics, useMessages, useMetrics, useNavigation, useOverrides, usePageOverride, usePrefetchClipCounts, useRealtimeLineMetrics, useRegistry, useSKUs, useShiftConfig, useShifts, useSubscriptionManager, useSubscriptionManagerSafe, useSupabase, useSupabaseClient, useTargets, useTheme, useThemeConfig, useThreads, useTicketHistory, useTimezoneContext, useVideoConfig, useWorkspaceConfig, useWorkspaceDetailedMetrics, useWorkspaceDisplayName, useWorkspaceDisplayNames, useWorkspaceDisplayNamesMap, useWorkspaceHealth, useWorkspaceHealthById, useWorkspaceMetrics, useWorkspaceNavigation, useWorkspaceOperators, userService, videoPrefetchManager, videoPreloader, whatsappService, withAccessControl, withAuth, withRegistry, withTimezone, workspaceHealthService, workspaceService };
|