@optifye/dashboard-core 6.6.6 → 6.6.7
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 +116 -273
- package/dist/index.d.ts +116 -273
- package/dist/index.js +195 -983
- package/dist/index.mjs +195 -983
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -3568,329 +3568,10 @@ var useAudioService = () => {
|
|
|
3568
3568
|
};
|
|
3569
3569
|
|
|
3570
3570
|
// src/lib/utils/dateShiftUtils.ts
|
|
3571
|
-
function isValidDateFormat(date) {
|
|
3572
|
-
return /^\d{4}-\d{2}-\d{2}$/.test(date);
|
|
3573
|
-
}
|
|
3574
3571
|
function isValidShiftId(shiftId) {
|
|
3575
3572
|
const id3 = typeof shiftId === "string" ? parseInt(shiftId, 10) : shiftId;
|
|
3576
3573
|
return id3 === 0 || id3 === 1;
|
|
3577
3574
|
}
|
|
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
3575
|
var getSupabaseClient = () => {
|
|
3895
3576
|
const url = process.env.NEXT_PUBLIC_SUPABASE_URL;
|
|
3896
3577
|
const key = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
|
|
@@ -3903,616 +3584,6 @@ var getAuthToken = async () => {
|
|
|
3903
3584
|
try {
|
|
3904
3585
|
const supabase = getSupabaseClient();
|
|
3905
3586
|
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
3587
|
console.log("[S3ClipsSupabase] Auth session exists:", !!session, "has token:", !!session?.access_token);
|
|
4517
3588
|
return session?.access_token || null;
|
|
4518
3589
|
} catch (error) {
|
|
@@ -4543,7 +3614,7 @@ var S3ClipsSupabaseService = class {
|
|
|
4543
3614
|
* Fetch with authentication and error handling
|
|
4544
3615
|
*/
|
|
4545
3616
|
async fetchWithAuth(endpoint, body) {
|
|
4546
|
-
const token = await
|
|
3617
|
+
const token = await getAuthToken();
|
|
4547
3618
|
if (!token) {
|
|
4548
3619
|
throw new Error("Authentication required");
|
|
4549
3620
|
}
|
|
@@ -5006,7 +4077,8 @@ var S3ClipsSupabaseService = class {
|
|
|
5006
4077
|
return descriptions[clipType] || "Analysis Clip";
|
|
5007
4078
|
}
|
|
5008
4079
|
};
|
|
5009
|
-
|
|
4080
|
+
|
|
4081
|
+
// src/lib/services/videoPrefetchManager.ts
|
|
5010
4082
|
var VideoPrefetchManager = class extends EventEmitter {
|
|
5011
4083
|
constructor() {
|
|
5012
4084
|
super();
|
|
@@ -5020,7 +4092,7 @@ var VideoPrefetchManager = class extends EventEmitter {
|
|
|
5020
4092
|
getS3Service(dashboardConfig) {
|
|
5021
4093
|
const configKey = JSON.stringify(dashboardConfig.s3Config);
|
|
5022
4094
|
if (!this.s3Services.has(configKey)) {
|
|
5023
|
-
this.s3Services.set(configKey, new
|
|
4095
|
+
this.s3Services.set(configKey, new S3ClipsSupabaseService(dashboardConfig));
|
|
5024
4096
|
}
|
|
5025
4097
|
return this.s3Services.get(configKey);
|
|
5026
4098
|
}
|
|
@@ -22805,32 +21877,10 @@ var VideoGridView = React20__default.memo(({
|
|
|
22805
21877
|
observerRef.current?.disconnect();
|
|
22806
21878
|
};
|
|
22807
21879
|
}, [filteredWorkspaces]);
|
|
22808
|
-
|
|
21880
|
+
useRef({});
|
|
22809
21881
|
const handleWorkspaceClick = useCallback((workspace) => {
|
|
22810
21882
|
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
|
-
}
|
|
21883
|
+
console.log(`[VideoGridView] Prefetching disabled for workspace ${workspaceId}`);
|
|
22834
21884
|
trackCoreEvent("Workspace Detail Clicked", {
|
|
22835
21885
|
workspace_name: workspace.workspace_name,
|
|
22836
21886
|
workspace_id: workspaceId,
|
|
@@ -28887,7 +27937,7 @@ var NewClipsNotification = ({
|
|
|
28887
27937
|
}
|
|
28888
27938
|
);
|
|
28889
27939
|
};
|
|
28890
|
-
var
|
|
27940
|
+
var getSupabaseClient2 = () => {
|
|
28891
27941
|
const url = process.env.NEXT_PUBLIC_SUPABASE_URL;
|
|
28892
27942
|
const key = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
|
|
28893
27943
|
if (!url || !key) {
|
|
@@ -28895,9 +27945,9 @@ var getSupabaseClient3 = () => {
|
|
|
28895
27945
|
}
|
|
28896
27946
|
return createClient(url, key);
|
|
28897
27947
|
};
|
|
28898
|
-
var
|
|
27948
|
+
var getAuthToken2 = async () => {
|
|
28899
27949
|
try {
|
|
28900
|
-
const supabase =
|
|
27950
|
+
const supabase = getSupabaseClient2();
|
|
28901
27951
|
const { data: { session } } = await supabase.auth.getSession();
|
|
28902
27952
|
return session?.access_token || null;
|
|
28903
27953
|
} catch (error) {
|
|
@@ -28918,7 +27968,7 @@ function useWorkspaceCrop(workspaceId) {
|
|
|
28918
27968
|
setIsLoading(true);
|
|
28919
27969
|
setError(null);
|
|
28920
27970
|
try {
|
|
28921
|
-
const token = await
|
|
27971
|
+
const token = await getAuthToken2();
|
|
28922
27972
|
if (!token) {
|
|
28923
27973
|
throw new Error("Authentication required");
|
|
28924
27974
|
}
|
|
@@ -29071,7 +28121,7 @@ var FileManagerFilters = ({
|
|
|
29071
28121
|
method: "POST",
|
|
29072
28122
|
headers: {
|
|
29073
28123
|
"Content-Type": "application/json",
|
|
29074
|
-
"Authorization": `Bearer ${await
|
|
28124
|
+
"Authorization": `Bearer ${await getAuthToken3()}`
|
|
29075
28125
|
},
|
|
29076
28126
|
body: JSON.stringify({
|
|
29077
28127
|
action: "clip-metadata",
|
|
@@ -29104,10 +28154,10 @@ var FileManagerFilters = ({
|
|
|
29104
28154
|
});
|
|
29105
28155
|
}
|
|
29106
28156
|
}, [workspaceId, date, shift]);
|
|
29107
|
-
const
|
|
28157
|
+
const getAuthToken3 = async () => {
|
|
29108
28158
|
try {
|
|
29109
|
-
const { createClient:
|
|
29110
|
-
const supabase =
|
|
28159
|
+
const { createClient: createClient4 } = await import('@supabase/supabase-js');
|
|
28160
|
+
const supabase = createClient4(
|
|
29111
28161
|
process.env.NEXT_PUBLIC_SUPABASE_URL || "",
|
|
29112
28162
|
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || ""
|
|
29113
28163
|
);
|
|
@@ -29130,7 +28180,7 @@ var FileManagerFilters = ({
|
|
|
29130
28180
|
method: "POST",
|
|
29131
28181
|
headers: {
|
|
29132
28182
|
"Content-Type": "application/json",
|
|
29133
|
-
"Authorization": `Bearer ${await
|
|
28183
|
+
"Authorization": `Bearer ${await getAuthToken3()}`
|
|
29134
28184
|
},
|
|
29135
28185
|
body: JSON.stringify({
|
|
29136
28186
|
action: "percentile-clips",
|
|
@@ -29873,8 +28923,6 @@ function useClipsRealtimeUpdates({
|
|
|
29873
28923
|
hasNewClips: newClipsNotification !== null && newClipsNotification.count > 0
|
|
29874
28924
|
};
|
|
29875
28925
|
}
|
|
29876
|
-
var USE_SUPABASE_CLIPS2 = true;
|
|
29877
|
-
var S3ClipsService3 = S3ClipsSupabaseService ;
|
|
29878
28926
|
var BottlenecksContent = ({
|
|
29879
28927
|
workspaceId,
|
|
29880
28928
|
workspaceName,
|
|
@@ -29932,8 +28980,8 @@ var BottlenecksContent = ({
|
|
|
29932
28980
|
workspaceId,
|
|
29933
28981
|
date: date || getOperationalDate(),
|
|
29934
28982
|
shiftId: effectiveShift,
|
|
29935
|
-
enabled:
|
|
29936
|
-
//
|
|
28983
|
+
enabled: true,
|
|
28984
|
+
// Supabase implementation
|
|
29937
28985
|
onNewClips: (notification) => {
|
|
29938
28986
|
console.log(`[BottlenecksContent] New clips detected:`, notification);
|
|
29939
28987
|
if (notification.clips.length > 0) {
|
|
@@ -29958,7 +29006,7 @@ var BottlenecksContent = ({
|
|
|
29958
29006
|
console.warn("S3 configuration not found in dashboard config");
|
|
29959
29007
|
return null;
|
|
29960
29008
|
}
|
|
29961
|
-
return new
|
|
29009
|
+
return new S3ClipsSupabaseService(dashboardConfig);
|
|
29962
29010
|
}, [dashboardConfig]);
|
|
29963
29011
|
const {
|
|
29964
29012
|
clipTypes,
|
|
@@ -29979,8 +29027,7 @@ var BottlenecksContent = ({
|
|
|
29979
29027
|
dynamicCounts,
|
|
29980
29028
|
workspaceId,
|
|
29981
29029
|
date: date || getOperationalDate(),
|
|
29982
|
-
shift: shift || "0"
|
|
29983
|
-
USE_SUPABASE_CLIPS: USE_SUPABASE_CLIPS2
|
|
29030
|
+
shift: shift || "0"
|
|
29984
29031
|
});
|
|
29985
29032
|
useEffect(() => {
|
|
29986
29033
|
if (clipTypes.length > 0 && !initialFilter) {
|
|
@@ -30088,12 +29135,8 @@ var BottlenecksContent = ({
|
|
|
30088
29135
|
operationalDate,
|
|
30089
29136
|
shiftStr,
|
|
30090
29137
|
targetCategory,
|
|
30091
|
-
0
|
|
29138
|
+
0
|
|
30092
29139
|
// First video (index 0)
|
|
30093
|
-
true,
|
|
30094
|
-
// includeCycleTime
|
|
30095
|
-
true
|
|
30096
|
-
// includeMetadata
|
|
30097
29140
|
);
|
|
30098
29141
|
if (firstVideo && isMountedRef.current) {
|
|
30099
29142
|
console.log(`[BottlenecksContent] Successfully loaded first video via index`);
|
|
@@ -30237,8 +29280,8 @@ var BottlenecksContent = ({
|
|
|
30237
29280
|
if (isPercentileCategory(categoryId)) {
|
|
30238
29281
|
console.log(`[BottlenecksContent] Loading percentile category: ${categoryId}`);
|
|
30239
29282
|
const percentileType = categoryId === "fast-cycles" ? "fast-cycles" : categoryId === "slow-cycles" ? "slow-cycles" : "idle-times";
|
|
30240
|
-
const { createClient:
|
|
30241
|
-
const supabase =
|
|
29283
|
+
const { createClient: createClient4 } = await import('@supabase/supabase-js');
|
|
29284
|
+
const supabase = createClient4(
|
|
30242
29285
|
process.env.NEXT_PUBLIC_SUPABASE_URL || "",
|
|
30243
29286
|
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || ""
|
|
30244
29287
|
);
|
|
@@ -30310,9 +29353,7 @@ var BottlenecksContent = ({
|
|
|
30310
29353
|
operationalDate,
|
|
30311
29354
|
shiftStr,
|
|
30312
29355
|
categoryId,
|
|
30313
|
-
clipIndex
|
|
30314
|
-
true,
|
|
30315
|
-
true
|
|
29356
|
+
clipIndex
|
|
30316
29357
|
);
|
|
30317
29358
|
if (video?.id) {
|
|
30318
29359
|
await loadAndPlayClipById(video.id, categoryId);
|
|
@@ -44476,4 +43517,175 @@ var streamProxyConfig = {
|
|
|
44476
43517
|
}
|
|
44477
43518
|
};
|
|
44478
43519
|
|
|
44479
|
-
|
|
43520
|
+
// src/lib/api/s3-clips-parser.ts
|
|
43521
|
+
function parseS3Uri(s3Uri, sopCategories) {
|
|
43522
|
+
const path = new URL(s3Uri).pathname;
|
|
43523
|
+
const parts = path.split("/").filter((p) => p);
|
|
43524
|
+
if (s3Uri.includes("missed_qchecks")) {
|
|
43525
|
+
console.warn(`Skipping missed_qchecks URI in parseS3Uri: ${s3Uri}`);
|
|
43526
|
+
return null;
|
|
43527
|
+
}
|
|
43528
|
+
if (parts.length < 8) {
|
|
43529
|
+
console.warn(`Invalid S3 path structure: ${s3Uri} - Too few parts: ${parts.length}, expected at least 8`);
|
|
43530
|
+
return null;
|
|
43531
|
+
}
|
|
43532
|
+
try {
|
|
43533
|
+
const datePart = parts[2];
|
|
43534
|
+
const shiftPart = parts[3];
|
|
43535
|
+
const violationType = parts[4];
|
|
43536
|
+
let folderName = "";
|
|
43537
|
+
let timestamp = "";
|
|
43538
|
+
for (let i = 5; i < parts.length; i++) {
|
|
43539
|
+
const part = parts[i];
|
|
43540
|
+
if (part && part.includes("_") && /\d{8}_\d{6}/.test(part)) {
|
|
43541
|
+
folderName = part;
|
|
43542
|
+
const timeMatch = folderName.match(/_(\d{8})_(\d{6})_/);
|
|
43543
|
+
if (timeMatch) {
|
|
43544
|
+
timestamp = `${timeMatch[2].substring(0, 2)}:${timeMatch[2].substring(2, 4)}:${timeMatch[2].substring(4, 6)}`;
|
|
43545
|
+
break;
|
|
43546
|
+
}
|
|
43547
|
+
}
|
|
43548
|
+
}
|
|
43549
|
+
if (!timestamp) {
|
|
43550
|
+
console.warn(`Couldn't extract timestamp from any part: ${parts.join("/")}`);
|
|
43551
|
+
timestamp = "00:00:00";
|
|
43552
|
+
}
|
|
43553
|
+
let severity = "low";
|
|
43554
|
+
let type = "bottleneck";
|
|
43555
|
+
let description = "Analysis Clip";
|
|
43556
|
+
const normalizedViolationType = violationType.toLowerCase().trim();
|
|
43557
|
+
if (sopCategories && sopCategories.length > 0) {
|
|
43558
|
+
const matchedCategory = sopCategories.find((category) => {
|
|
43559
|
+
const categoryId = category.id.toLowerCase();
|
|
43560
|
+
const s3FolderName = (category.s3FolderName || category.id).toLowerCase();
|
|
43561
|
+
return categoryId === normalizedViolationType || s3FolderName === normalizedViolationType || // Also check for partial matches for flexibility
|
|
43562
|
+
normalizedViolationType.includes(categoryId) || normalizedViolationType.includes(s3FolderName);
|
|
43563
|
+
});
|
|
43564
|
+
if (matchedCategory) {
|
|
43565
|
+
type = matchedCategory.id;
|
|
43566
|
+
description = matchedCategory.description || matchedCategory.label;
|
|
43567
|
+
if (matchedCategory.color.includes("red")) {
|
|
43568
|
+
severity = "high";
|
|
43569
|
+
} else if (matchedCategory.color.includes("yellow") || matchedCategory.color.includes("orange")) {
|
|
43570
|
+
severity = "medium";
|
|
43571
|
+
} else {
|
|
43572
|
+
severity = "low";
|
|
43573
|
+
}
|
|
43574
|
+
console.log(`Matched SOP category: ${matchedCategory.id} for violation type: ${violationType}`);
|
|
43575
|
+
return { timestamp, severity, description, type, originalUri: s3Uri };
|
|
43576
|
+
}
|
|
43577
|
+
}
|
|
43578
|
+
switch (normalizedViolationType) {
|
|
43579
|
+
case "idle_time":
|
|
43580
|
+
case "idle":
|
|
43581
|
+
case "low_value":
|
|
43582
|
+
case "low value":
|
|
43583
|
+
case "low_value_moment":
|
|
43584
|
+
case "low_value_moments":
|
|
43585
|
+
case "low value moment":
|
|
43586
|
+
case "low value moments":
|
|
43587
|
+
type = "low_value";
|
|
43588
|
+
severity = "low";
|
|
43589
|
+
description = "Idle Time Detected";
|
|
43590
|
+
break;
|
|
43591
|
+
case "sop_deviation":
|
|
43592
|
+
type = "missing_quality_check";
|
|
43593
|
+
severity = "high";
|
|
43594
|
+
description = "SOP Deviations";
|
|
43595
|
+
break;
|
|
43596
|
+
case "long_cycle_time":
|
|
43597
|
+
severity = "high";
|
|
43598
|
+
type = "long_cycle_time";
|
|
43599
|
+
description = "Long Cycle Time Detected";
|
|
43600
|
+
break;
|
|
43601
|
+
case "best_cycle_time":
|
|
43602
|
+
type = "best_cycle_time";
|
|
43603
|
+
severity = "low";
|
|
43604
|
+
description = "Best Cycle Time Performance";
|
|
43605
|
+
break;
|
|
43606
|
+
case "worst_cycle_time":
|
|
43607
|
+
type = "worst_cycle_time";
|
|
43608
|
+
severity = "high";
|
|
43609
|
+
description = "Worst Cycle Time Performance";
|
|
43610
|
+
break;
|
|
43611
|
+
case "cycle_completion":
|
|
43612
|
+
case "completed_cycles":
|
|
43613
|
+
case "completed_cycle":
|
|
43614
|
+
type = "cycle_completion";
|
|
43615
|
+
severity = "low";
|
|
43616
|
+
description = "Cycle Completion";
|
|
43617
|
+
break;
|
|
43618
|
+
case "running_cycle":
|
|
43619
|
+
case "active_cycle":
|
|
43620
|
+
case "production_cycle":
|
|
43621
|
+
type = "running_cycle";
|
|
43622
|
+
severity = "low";
|
|
43623
|
+
description = "Active Production Cycle";
|
|
43624
|
+
break;
|
|
43625
|
+
case "setup_state":
|
|
43626
|
+
case "machine_setup":
|
|
43627
|
+
case "line_setup":
|
|
43628
|
+
type = "setup_state";
|
|
43629
|
+
severity = "medium";
|
|
43630
|
+
description = "Machine Setup Activity";
|
|
43631
|
+
break;
|
|
43632
|
+
case "medium_bottleneck":
|
|
43633
|
+
severity = "medium";
|
|
43634
|
+
description = "Medium Bottleneck Identified";
|
|
43635
|
+
break;
|
|
43636
|
+
case "minor_bottleneck":
|
|
43637
|
+
case "mild_bottleneck":
|
|
43638
|
+
severity = "low";
|
|
43639
|
+
description = "Minor Bottleneck Identified";
|
|
43640
|
+
break;
|
|
43641
|
+
default:
|
|
43642
|
+
if (normalizedViolationType.includes("sop") && normalizedViolationType.includes("deviation")) {
|
|
43643
|
+
type = "missing_quality_check";
|
|
43644
|
+
severity = "high";
|
|
43645
|
+
description = "SOP Deviations";
|
|
43646
|
+
} else if (normalizedViolationType.includes("worst") && normalizedViolationType.includes("cycle")) {
|
|
43647
|
+
type = "worst_cycle_time";
|
|
43648
|
+
severity = "high";
|
|
43649
|
+
description = "Worst Cycle Time Performance";
|
|
43650
|
+
} else if (normalizedViolationType.includes("best") && normalizedViolationType.includes("cycle")) {
|
|
43651
|
+
type = "best_cycle_time";
|
|
43652
|
+
severity = "low";
|
|
43653
|
+
description = "Best Cycle Time Performance";
|
|
43654
|
+
} else if (normalizedViolationType.includes("long") && normalizedViolationType.includes("cycle")) {
|
|
43655
|
+
type = "long_cycle_time";
|
|
43656
|
+
severity = "high";
|
|
43657
|
+
description = "Long Cycle Time Detected";
|
|
43658
|
+
} else if (normalizedViolationType.includes("cycle") && (normalizedViolationType.includes("completion") || normalizedViolationType.includes("complete"))) {
|
|
43659
|
+
type = "cycle_completion";
|
|
43660
|
+
severity = "low";
|
|
43661
|
+
description = "Cycle Completion";
|
|
43662
|
+
} else if (normalizedViolationType.includes("running") && normalizedViolationType.includes("cycle")) {
|
|
43663
|
+
type = "running_cycle";
|
|
43664
|
+
severity = "low";
|
|
43665
|
+
description = "Active Production Cycle";
|
|
43666
|
+
} else if (normalizedViolationType.includes("setup") || normalizedViolationType.includes("machine") && normalizedViolationType.includes("setup")) {
|
|
43667
|
+
type = "setup_state";
|
|
43668
|
+
severity = "medium";
|
|
43669
|
+
description = "Machine Setup Activity";
|
|
43670
|
+
} else {
|
|
43671
|
+
description = `Clip type: ${violationType.replace(/_/g, " ")}`;
|
|
43672
|
+
console.log(`Detected unknown violation type: ${violationType} in URI: ${s3Uri}`);
|
|
43673
|
+
}
|
|
43674
|
+
break;
|
|
43675
|
+
}
|
|
43676
|
+
return { timestamp, severity, description, type, originalUri: s3Uri };
|
|
43677
|
+
} catch (error) {
|
|
43678
|
+
console.error(`Error parsing S3 URI: ${s3Uri}`, error);
|
|
43679
|
+
return null;
|
|
43680
|
+
}
|
|
43681
|
+
}
|
|
43682
|
+
function shuffleArray(array) {
|
|
43683
|
+
const shuffled = [...array];
|
|
43684
|
+
for (let i = shuffled.length - 1; i > 0; i--) {
|
|
43685
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
43686
|
+
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
|
|
43687
|
+
}
|
|
43688
|
+
return shuffled;
|
|
43689
|
+
}
|
|
43690
|
+
|
|
43691
|
+
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, 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, 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, useVideoConfig, useWorkspaceConfig, useWorkspaceDetailedMetrics, useWorkspaceDisplayName, useWorkspaceDisplayNames, useWorkspaceDisplayNamesMap, useWorkspaceHealth, useWorkspaceHealthById, useWorkspaceMetrics, useWorkspaceNavigation, useWorkspaceOperators, userService, videoPrefetchManager, videoPreloader, whatsappService, withAccessControl, withAuth, withRegistry, workspaceHealthService, workspaceService };
|