@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.js
CHANGED
|
@@ -3598,329 +3598,10 @@ var useAudioService = () => {
|
|
|
3598
3598
|
};
|
|
3599
3599
|
|
|
3600
3600
|
// src/lib/utils/dateShiftUtils.ts
|
|
3601
|
-
function isValidDateFormat(date) {
|
|
3602
|
-
return /^\d{4}-\d{2}-\d{2}$/.test(date);
|
|
3603
|
-
}
|
|
3604
3601
|
function isValidShiftId(shiftId) {
|
|
3605
3602
|
const id3 = typeof shiftId === "string" ? parseInt(shiftId, 10) : shiftId;
|
|
3606
3603
|
return id3 === 0 || id3 === 1;
|
|
3607
3604
|
}
|
|
3608
|
-
|
|
3609
|
-
// src/lib/api/s3-clips-parser.ts
|
|
3610
|
-
function parseS3Uri(s3Uri, sopCategories) {
|
|
3611
|
-
const path = new URL(s3Uri).pathname;
|
|
3612
|
-
const parts = path.split("/").filter((p) => p);
|
|
3613
|
-
if (s3Uri.includes("missed_qchecks")) {
|
|
3614
|
-
console.warn(`Skipping missed_qchecks URI in parseS3Uri: ${s3Uri}`);
|
|
3615
|
-
return null;
|
|
3616
|
-
}
|
|
3617
|
-
if (parts.length < 8) {
|
|
3618
|
-
console.warn(`Invalid S3 path structure: ${s3Uri} - Too few parts: ${parts.length}, expected at least 8`);
|
|
3619
|
-
return null;
|
|
3620
|
-
}
|
|
3621
|
-
try {
|
|
3622
|
-
const datePart = parts[2];
|
|
3623
|
-
const shiftPart = parts[3];
|
|
3624
|
-
const violationType = parts[4];
|
|
3625
|
-
let folderName = "";
|
|
3626
|
-
let timestamp = "";
|
|
3627
|
-
for (let i = 5; i < parts.length; i++) {
|
|
3628
|
-
const part = parts[i];
|
|
3629
|
-
if (part && part.includes("_") && /\d{8}_\d{6}/.test(part)) {
|
|
3630
|
-
folderName = part;
|
|
3631
|
-
const timeMatch = folderName.match(/_(\d{8})_(\d{6})_/);
|
|
3632
|
-
if (timeMatch) {
|
|
3633
|
-
timestamp = `${timeMatch[2].substring(0, 2)}:${timeMatch[2].substring(2, 4)}:${timeMatch[2].substring(4, 6)}`;
|
|
3634
|
-
break;
|
|
3635
|
-
}
|
|
3636
|
-
}
|
|
3637
|
-
}
|
|
3638
|
-
if (!timestamp) {
|
|
3639
|
-
console.warn(`Couldn't extract timestamp from any part: ${parts.join("/")}`);
|
|
3640
|
-
timestamp = "00:00:00";
|
|
3641
|
-
}
|
|
3642
|
-
let severity = "low";
|
|
3643
|
-
let type = "bottleneck";
|
|
3644
|
-
let description = "Analysis Clip";
|
|
3645
|
-
const normalizedViolationType = violationType.toLowerCase().trim();
|
|
3646
|
-
if (sopCategories && sopCategories.length > 0) {
|
|
3647
|
-
const matchedCategory = sopCategories.find((category) => {
|
|
3648
|
-
const categoryId = category.id.toLowerCase();
|
|
3649
|
-
const s3FolderName = (category.s3FolderName || category.id).toLowerCase();
|
|
3650
|
-
return categoryId === normalizedViolationType || s3FolderName === normalizedViolationType || // Also check for partial matches for flexibility
|
|
3651
|
-
normalizedViolationType.includes(categoryId) || normalizedViolationType.includes(s3FolderName);
|
|
3652
|
-
});
|
|
3653
|
-
if (matchedCategory) {
|
|
3654
|
-
type = matchedCategory.id;
|
|
3655
|
-
description = matchedCategory.description || matchedCategory.label;
|
|
3656
|
-
if (matchedCategory.color.includes("red")) {
|
|
3657
|
-
severity = "high";
|
|
3658
|
-
} else if (matchedCategory.color.includes("yellow") || matchedCategory.color.includes("orange")) {
|
|
3659
|
-
severity = "medium";
|
|
3660
|
-
} else {
|
|
3661
|
-
severity = "low";
|
|
3662
|
-
}
|
|
3663
|
-
console.log(`Matched SOP category: ${matchedCategory.id} for violation type: ${violationType}`);
|
|
3664
|
-
return { timestamp, severity, description, type, originalUri: s3Uri };
|
|
3665
|
-
}
|
|
3666
|
-
}
|
|
3667
|
-
switch (normalizedViolationType) {
|
|
3668
|
-
case "idle_time":
|
|
3669
|
-
case "idle":
|
|
3670
|
-
case "low_value":
|
|
3671
|
-
case "low value":
|
|
3672
|
-
case "low_value_moment":
|
|
3673
|
-
case "low_value_moments":
|
|
3674
|
-
case "low value moment":
|
|
3675
|
-
case "low value moments":
|
|
3676
|
-
type = "low_value";
|
|
3677
|
-
severity = "low";
|
|
3678
|
-
description = "Idle Time Detected";
|
|
3679
|
-
break;
|
|
3680
|
-
case "sop_deviation":
|
|
3681
|
-
type = "missing_quality_check";
|
|
3682
|
-
severity = "high";
|
|
3683
|
-
description = "SOP Deviations";
|
|
3684
|
-
break;
|
|
3685
|
-
case "long_cycle_time":
|
|
3686
|
-
severity = "high";
|
|
3687
|
-
type = "long_cycle_time";
|
|
3688
|
-
description = "Long Cycle Time Detected";
|
|
3689
|
-
break;
|
|
3690
|
-
case "best_cycle_time":
|
|
3691
|
-
type = "best_cycle_time";
|
|
3692
|
-
severity = "low";
|
|
3693
|
-
description = "Best Cycle Time Performance";
|
|
3694
|
-
break;
|
|
3695
|
-
case "worst_cycle_time":
|
|
3696
|
-
type = "worst_cycle_time";
|
|
3697
|
-
severity = "high";
|
|
3698
|
-
description = "Worst Cycle Time Performance";
|
|
3699
|
-
break;
|
|
3700
|
-
case "cycle_completion":
|
|
3701
|
-
case "completed_cycles":
|
|
3702
|
-
case "completed_cycle":
|
|
3703
|
-
type = "cycle_completion";
|
|
3704
|
-
severity = "low";
|
|
3705
|
-
description = "Cycle Completion";
|
|
3706
|
-
break;
|
|
3707
|
-
case "running_cycle":
|
|
3708
|
-
case "active_cycle":
|
|
3709
|
-
case "production_cycle":
|
|
3710
|
-
type = "running_cycle";
|
|
3711
|
-
severity = "low";
|
|
3712
|
-
description = "Active Production Cycle";
|
|
3713
|
-
break;
|
|
3714
|
-
case "setup_state":
|
|
3715
|
-
case "machine_setup":
|
|
3716
|
-
case "line_setup":
|
|
3717
|
-
type = "setup_state";
|
|
3718
|
-
severity = "medium";
|
|
3719
|
-
description = "Machine Setup Activity";
|
|
3720
|
-
break;
|
|
3721
|
-
case "medium_bottleneck":
|
|
3722
|
-
severity = "medium";
|
|
3723
|
-
description = "Medium Bottleneck Identified";
|
|
3724
|
-
break;
|
|
3725
|
-
case "minor_bottleneck":
|
|
3726
|
-
case "mild_bottleneck":
|
|
3727
|
-
severity = "low";
|
|
3728
|
-
description = "Minor Bottleneck Identified";
|
|
3729
|
-
break;
|
|
3730
|
-
default:
|
|
3731
|
-
if (normalizedViolationType.includes("sop") && normalizedViolationType.includes("deviation")) {
|
|
3732
|
-
type = "missing_quality_check";
|
|
3733
|
-
severity = "high";
|
|
3734
|
-
description = "SOP Deviations";
|
|
3735
|
-
} else if (normalizedViolationType.includes("worst") && normalizedViolationType.includes("cycle")) {
|
|
3736
|
-
type = "worst_cycle_time";
|
|
3737
|
-
severity = "high";
|
|
3738
|
-
description = "Worst Cycle Time Performance";
|
|
3739
|
-
} else if (normalizedViolationType.includes("best") && normalizedViolationType.includes("cycle")) {
|
|
3740
|
-
type = "best_cycle_time";
|
|
3741
|
-
severity = "low";
|
|
3742
|
-
description = "Best Cycle Time Performance";
|
|
3743
|
-
} else if (normalizedViolationType.includes("long") && normalizedViolationType.includes("cycle")) {
|
|
3744
|
-
type = "long_cycle_time";
|
|
3745
|
-
severity = "high";
|
|
3746
|
-
description = "Long Cycle Time Detected";
|
|
3747
|
-
} else if (normalizedViolationType.includes("cycle") && (normalizedViolationType.includes("completion") || normalizedViolationType.includes("complete"))) {
|
|
3748
|
-
type = "cycle_completion";
|
|
3749
|
-
severity = "low";
|
|
3750
|
-
description = "Cycle Completion";
|
|
3751
|
-
} else if (normalizedViolationType.includes("running") && normalizedViolationType.includes("cycle")) {
|
|
3752
|
-
type = "running_cycle";
|
|
3753
|
-
severity = "low";
|
|
3754
|
-
description = "Active Production Cycle";
|
|
3755
|
-
} else if (normalizedViolationType.includes("setup") || normalizedViolationType.includes("machine") && normalizedViolationType.includes("setup")) {
|
|
3756
|
-
type = "setup_state";
|
|
3757
|
-
severity = "medium";
|
|
3758
|
-
description = "Machine Setup Activity";
|
|
3759
|
-
} else {
|
|
3760
|
-
description = `Clip type: ${violationType.replace(/_/g, " ")}`;
|
|
3761
|
-
console.log(`Detected unknown violation type: ${violationType} in URI: ${s3Uri}`);
|
|
3762
|
-
}
|
|
3763
|
-
break;
|
|
3764
|
-
}
|
|
3765
|
-
return { timestamp, severity, description, type, originalUri: s3Uri };
|
|
3766
|
-
} catch (error) {
|
|
3767
|
-
console.error(`Error parsing S3 URI: ${s3Uri}`, error);
|
|
3768
|
-
return null;
|
|
3769
|
-
}
|
|
3770
|
-
}
|
|
3771
|
-
function shuffleArray(array) {
|
|
3772
|
-
const shuffled = [...array];
|
|
3773
|
-
for (let i = shuffled.length - 1; i > 0; i--) {
|
|
3774
|
-
const j = Math.floor(Math.random() * (i + 1));
|
|
3775
|
-
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
|
|
3776
|
-
}
|
|
3777
|
-
return shuffled;
|
|
3778
|
-
}
|
|
3779
|
-
var SmartVideoCache = class extends events.EventEmitter {
|
|
3780
|
-
constructor() {
|
|
3781
|
-
super();
|
|
3782
|
-
this.metrics = {
|
|
3783
|
-
hits: 0,
|
|
3784
|
-
misses: 0,
|
|
3785
|
-
evictions: 0,
|
|
3786
|
-
totalSize: 0,
|
|
3787
|
-
entryCount: 0,
|
|
3788
|
-
hitRate: 0
|
|
3789
|
-
};
|
|
3790
|
-
this.setMaxListeners(50);
|
|
3791
|
-
}
|
|
3792
|
-
/**
|
|
3793
|
-
* DISABLED - Always returns null
|
|
3794
|
-
*/
|
|
3795
|
-
async getSummary(key) {
|
|
3796
|
-
this.metrics.misses++;
|
|
3797
|
-
this.updateHitRate();
|
|
3798
|
-
return null;
|
|
3799
|
-
}
|
|
3800
|
-
/**
|
|
3801
|
-
* DISABLED - Does nothing
|
|
3802
|
-
*/
|
|
3803
|
-
async setSummary(key, summary) {
|
|
3804
|
-
}
|
|
3805
|
-
/**
|
|
3806
|
-
* DISABLED - Always returns null
|
|
3807
|
-
*/
|
|
3808
|
-
async getVideos(key) {
|
|
3809
|
-
this.metrics.misses++;
|
|
3810
|
-
this.updateHitRate();
|
|
3811
|
-
return null;
|
|
3812
|
-
}
|
|
3813
|
-
/**
|
|
3814
|
-
* DISABLED - Does nothing
|
|
3815
|
-
*/
|
|
3816
|
-
async setVideos(key, videos) {
|
|
3817
|
-
}
|
|
3818
|
-
/**
|
|
3819
|
-
* DISABLED - Always returns null
|
|
3820
|
-
*/
|
|
3821
|
-
async getClipCounts(key) {
|
|
3822
|
-
console.log("[SmartVideoCache] DISABLED - Returning null for clip counts");
|
|
3823
|
-
this.metrics.misses++;
|
|
3824
|
-
this.updateHitRate();
|
|
3825
|
-
return null;
|
|
3826
|
-
}
|
|
3827
|
-
/**
|
|
3828
|
-
* DISABLED - Does nothing
|
|
3829
|
-
*/
|
|
3830
|
-
async setClipCounts(key, clipCountsWithIndex, ttlMinutes) {
|
|
3831
|
-
console.log("[SmartVideoCache] DISABLED - Not caching clip counts");
|
|
3832
|
-
}
|
|
3833
|
-
/**
|
|
3834
|
-
* DISABLED - Does nothing
|
|
3835
|
-
*/
|
|
3836
|
-
setPrefetchStatus(key, status) {
|
|
3837
|
-
}
|
|
3838
|
-
/**
|
|
3839
|
-
* DISABLED - Always returns NONE
|
|
3840
|
-
*/
|
|
3841
|
-
getPrefetchStatus(key) {
|
|
3842
|
-
return "none" /* NONE */;
|
|
3843
|
-
}
|
|
3844
|
-
/**
|
|
3845
|
-
* DISABLED - Always returns false
|
|
3846
|
-
*/
|
|
3847
|
-
isPrefetchReady(key) {
|
|
3848
|
-
return false;
|
|
3849
|
-
}
|
|
3850
|
-
/**
|
|
3851
|
-
* DISABLED - Returns empty cleanup function
|
|
3852
|
-
*/
|
|
3853
|
-
subscribeToPrefetchStatus(key, callback) {
|
|
3854
|
-
return () => {
|
|
3855
|
-
};
|
|
3856
|
-
}
|
|
3857
|
-
/**
|
|
3858
|
-
* DISABLED - Always returns null
|
|
3859
|
-
*/
|
|
3860
|
-
getSignedUrl(s3Uri) {
|
|
3861
|
-
this.metrics.misses++;
|
|
3862
|
-
this.updateHitRate();
|
|
3863
|
-
return null;
|
|
3864
|
-
}
|
|
3865
|
-
/**
|
|
3866
|
-
* DISABLED - Does nothing
|
|
3867
|
-
*/
|
|
3868
|
-
setSignedUrl(s3Uri, url, ttlSeconds = 3600) {
|
|
3869
|
-
}
|
|
3870
|
-
/**
|
|
3871
|
-
* DISABLED - Always returns empty array
|
|
3872
|
-
*/
|
|
3873
|
-
async getVideosByWorkspace(workspaceId) {
|
|
3874
|
-
return [];
|
|
3875
|
-
}
|
|
3876
|
-
/**
|
|
3877
|
-
* DISABLED - Does nothing
|
|
3878
|
-
*/
|
|
3879
|
-
async warmup(entries) {
|
|
3880
|
-
}
|
|
3881
|
-
/**
|
|
3882
|
-
* Get cache statistics (mostly zeros since cache is disabled)
|
|
3883
|
-
*/
|
|
3884
|
-
getStats() {
|
|
3885
|
-
return {
|
|
3886
|
-
...this.metrics,
|
|
3887
|
-
summaryStats: { hits: 0, misses: this.metrics.misses, evictions: 0, totalSize: 0 },
|
|
3888
|
-
videoStats: { hits: 0, misses: this.metrics.misses, evictions: 0, totalSize: 0 },
|
|
3889
|
-
clipCountsStats: { hits: 0, misses: this.metrics.misses, evictions: 0, totalSize: 0 },
|
|
3890
|
-
urlCacheSize: 0
|
|
3891
|
-
};
|
|
3892
|
-
}
|
|
3893
|
-
/**
|
|
3894
|
-
* DISABLED - Does nothing
|
|
3895
|
-
*/
|
|
3896
|
-
clear(type) {
|
|
3897
|
-
this.removeAllListeners();
|
|
3898
|
-
}
|
|
3899
|
-
/**
|
|
3900
|
-
* Update hit rate metric
|
|
3901
|
-
*/
|
|
3902
|
-
updateHitRate() {
|
|
3903
|
-
const total = this.metrics.hits + this.metrics.misses;
|
|
3904
|
-
this.metrics.hitRate = total > 0 ? this.metrics.hits / total : 0;
|
|
3905
|
-
}
|
|
3906
|
-
/**
|
|
3907
|
-
* Export cache state for debugging (returns empty state)
|
|
3908
|
-
*/
|
|
3909
|
-
exportState() {
|
|
3910
|
-
return {
|
|
3911
|
-
summaries: [],
|
|
3912
|
-
videos: [],
|
|
3913
|
-
urls: [],
|
|
3914
|
-
metrics: this.getStats()
|
|
3915
|
-
};
|
|
3916
|
-
}
|
|
3917
|
-
};
|
|
3918
|
-
var smartVideoCache = new SmartVideoCache();
|
|
3919
|
-
if (typeof window !== "undefined") {
|
|
3920
|
-
window.addEventListener("beforeunload", () => {
|
|
3921
|
-
console.log("[SmartVideoCache] Cache disabled - no cleanup needed");
|
|
3922
|
-
});
|
|
3923
|
-
}
|
|
3924
3605
|
var getSupabaseClient = () => {
|
|
3925
3606
|
const url = process.env.NEXT_PUBLIC_SUPABASE_URL;
|
|
3926
3607
|
const key = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
|
|
@@ -3933,616 +3614,6 @@ var getAuthToken = async () => {
|
|
|
3933
3614
|
try {
|
|
3934
3615
|
const supabase = getSupabaseClient();
|
|
3935
3616
|
const { data: { session } } = await supabase.auth.getSession();
|
|
3936
|
-
return session?.access_token || null;
|
|
3937
|
-
} catch (error) {
|
|
3938
|
-
console.error("[S3ClipsAPIClient] Error getting auth token:", error);
|
|
3939
|
-
return null;
|
|
3940
|
-
}
|
|
3941
|
-
};
|
|
3942
|
-
var S3ClipsAPIClient = class {
|
|
3943
|
-
constructor(sopCategories) {
|
|
3944
|
-
this.baseUrl = "/api/clips";
|
|
3945
|
-
this.requestCache = /* @__PURE__ */ new Map();
|
|
3946
|
-
this.sopCategories = sopCategories;
|
|
3947
|
-
console.log("[S3ClipsAPIClient] \u2705 Initialized - Using secure API routes (no direct S3 access)");
|
|
3948
|
-
}
|
|
3949
|
-
/**
|
|
3950
|
-
* Fetch with authentication and error handling
|
|
3951
|
-
*/
|
|
3952
|
-
async fetchWithAuth(endpoint, body) {
|
|
3953
|
-
const token = await getAuthToken();
|
|
3954
|
-
if (!token) {
|
|
3955
|
-
throw new Error("Authentication required");
|
|
3956
|
-
}
|
|
3957
|
-
const response = await fetch(endpoint, {
|
|
3958
|
-
method: "POST",
|
|
3959
|
-
headers: {
|
|
3960
|
-
"Authorization": `Bearer ${token}`,
|
|
3961
|
-
"Content-Type": "application/json"
|
|
3962
|
-
},
|
|
3963
|
-
body: JSON.stringify(body)
|
|
3964
|
-
});
|
|
3965
|
-
if (!response.ok) {
|
|
3966
|
-
const error = await response.json().catch(() => ({ error: "Request failed" }));
|
|
3967
|
-
throw new Error(error.error || `API error: ${response.status}`);
|
|
3968
|
-
}
|
|
3969
|
-
return response.json();
|
|
3970
|
-
}
|
|
3971
|
-
/**
|
|
3972
|
-
* Deduplicate requests to prevent multiple API calls
|
|
3973
|
-
*/
|
|
3974
|
-
async deduplicate(key, factory) {
|
|
3975
|
-
if (this.requestCache.has(key)) {
|
|
3976
|
-
console.log(`[S3ClipsAPIClient] Deduplicating request: ${key}`);
|
|
3977
|
-
return this.requestCache.get(key);
|
|
3978
|
-
}
|
|
3979
|
-
const promise = factory().finally(() => {
|
|
3980
|
-
this.requestCache.delete(key);
|
|
3981
|
-
});
|
|
3982
|
-
this.requestCache.set(key, promise);
|
|
3983
|
-
return promise;
|
|
3984
|
-
}
|
|
3985
|
-
/**
|
|
3986
|
-
* List S3 clips
|
|
3987
|
-
*/
|
|
3988
|
-
async listS3Clips(params) {
|
|
3989
|
-
const cacheKey = `list:${JSON.stringify(params)}`;
|
|
3990
|
-
return this.deduplicate(cacheKey, async () => {
|
|
3991
|
-
const response = await this.fetchWithAuth(this.baseUrl, {
|
|
3992
|
-
action: "list",
|
|
3993
|
-
workspaceId: params.workspaceId,
|
|
3994
|
-
date: params.date,
|
|
3995
|
-
shift: params.shiftId,
|
|
3996
|
-
maxKeys: params.maxKeys
|
|
3997
|
-
});
|
|
3998
|
-
return response.clips.map((clip) => clip.originalUri);
|
|
3999
|
-
});
|
|
4000
|
-
}
|
|
4001
|
-
/**
|
|
4002
|
-
* Get clip counts
|
|
4003
|
-
*/
|
|
4004
|
-
async getClipCounts(workspaceId, date, shiftId) {
|
|
4005
|
-
const cacheKey = `counts:${workspaceId}:${date}:${shiftId}`;
|
|
4006
|
-
return this.deduplicate(cacheKey, async () => {
|
|
4007
|
-
const response = await this.fetchWithAuth(this.baseUrl, {
|
|
4008
|
-
action: "count",
|
|
4009
|
-
workspaceId,
|
|
4010
|
-
date,
|
|
4011
|
-
shift: shiftId.toString()
|
|
4012
|
-
});
|
|
4013
|
-
return response.counts;
|
|
4014
|
-
});
|
|
4015
|
-
}
|
|
4016
|
-
/**
|
|
4017
|
-
* Get clip counts with index (for compatibility)
|
|
4018
|
-
*/
|
|
4019
|
-
async getClipCountsWithIndex(workspaceId, date, shiftId) {
|
|
4020
|
-
const counts = await this.getClipCounts(workspaceId, date, shiftId.toString());
|
|
4021
|
-
const videoIndex = {
|
|
4022
|
-
byCategory: /* @__PURE__ */ new Map(),
|
|
4023
|
-
allVideos: [],
|
|
4024
|
-
counts,
|
|
4025
|
-
workspaceId,
|
|
4026
|
-
date,
|
|
4027
|
-
shiftId: shiftId.toString(),
|
|
4028
|
-
lastUpdated: /* @__PURE__ */ new Date()
|
|
4029
|
-
};
|
|
4030
|
-
return { counts, videoIndex };
|
|
4031
|
-
}
|
|
4032
|
-
/**
|
|
4033
|
-
* Get metadata for a video
|
|
4034
|
-
*/
|
|
4035
|
-
async getMetadata(playlistUri) {
|
|
4036
|
-
const cacheKey = `metadata:${playlistUri}`;
|
|
4037
|
-
return this.deduplicate(cacheKey, async () => {
|
|
4038
|
-
const response = await this.fetchWithAuth(this.baseUrl, {
|
|
4039
|
-
action: "metadata",
|
|
4040
|
-
playlistUri
|
|
4041
|
-
});
|
|
4042
|
-
return response.metadata;
|
|
4043
|
-
});
|
|
4044
|
-
}
|
|
4045
|
-
/**
|
|
4046
|
-
* Get metadata cycle time
|
|
4047
|
-
*/
|
|
4048
|
-
async getMetadataCycleTime(playlistUri) {
|
|
4049
|
-
const metadata = await this.getMetadata(playlistUri);
|
|
4050
|
-
return metadata?.cycle_time_seconds || null;
|
|
4051
|
-
}
|
|
4052
|
-
/**
|
|
4053
|
-
* Get first clip for category
|
|
4054
|
-
*/
|
|
4055
|
-
async getFirstClipForCategory(workspaceId, date, shiftId, category) {
|
|
4056
|
-
const cacheKey = `first:${workspaceId}:${date}:${shiftId}:${category}`;
|
|
4057
|
-
return this.deduplicate(cacheKey, async () => {
|
|
4058
|
-
const response = await this.fetchWithAuth(this.baseUrl, {
|
|
4059
|
-
action: "first",
|
|
4060
|
-
workspaceId,
|
|
4061
|
-
date,
|
|
4062
|
-
shift: shiftId.toString(),
|
|
4063
|
-
category,
|
|
4064
|
-
sopCategories: this.sopCategories
|
|
4065
|
-
});
|
|
4066
|
-
return response.video;
|
|
4067
|
-
});
|
|
4068
|
-
}
|
|
4069
|
-
/**
|
|
4070
|
-
* Get clip by index
|
|
4071
|
-
*/
|
|
4072
|
-
async getClipByIndex(workspaceId, date, shiftId, category, index, includeCycleTime = true, includeMetadata = false) {
|
|
4073
|
-
const cacheKey = `by-index:${workspaceId}:${date}:${shiftId}:${category}:${index}`;
|
|
4074
|
-
return this.deduplicate(cacheKey, async () => {
|
|
4075
|
-
const response = await this.fetchWithAuth(this.baseUrl, {
|
|
4076
|
-
action: "by-index",
|
|
4077
|
-
workspaceId,
|
|
4078
|
-
date,
|
|
4079
|
-
shift: shiftId.toString(),
|
|
4080
|
-
category,
|
|
4081
|
-
index,
|
|
4082
|
-
sopCategories: this.sopCategories
|
|
4083
|
-
});
|
|
4084
|
-
const video = response.video;
|
|
4085
|
-
if (video && includeMetadata && video.originalUri) {
|
|
4086
|
-
try {
|
|
4087
|
-
const metadata = await this.getMetadata(video.originalUri);
|
|
4088
|
-
if (metadata) {
|
|
4089
|
-
video.cycle_time_seconds = metadata.cycle_time_seconds;
|
|
4090
|
-
video.creation_timestamp = metadata.creation_timestamp;
|
|
4091
|
-
}
|
|
4092
|
-
} catch (error) {
|
|
4093
|
-
console.warn("[S3ClipsAPIClient] Failed to fetch metadata:", error);
|
|
4094
|
-
}
|
|
4095
|
-
}
|
|
4096
|
-
return video;
|
|
4097
|
-
});
|
|
4098
|
-
}
|
|
4099
|
-
/**
|
|
4100
|
-
* Get videos page with pagination
|
|
4101
|
-
*/
|
|
4102
|
-
async getVideosPage(workspaceId, date, shiftId, category, pageSize = 5, startAfter) {
|
|
4103
|
-
const cacheKey = `page:${workspaceId}:${date}:${shiftId}:${category}:${pageSize}:${startAfter || "first"}`;
|
|
4104
|
-
return this.deduplicate(cacheKey, async () => {
|
|
4105
|
-
const response = await this.fetchWithAuth(this.baseUrl, {
|
|
4106
|
-
action: "page",
|
|
4107
|
-
workspaceId,
|
|
4108
|
-
date,
|
|
4109
|
-
shift: shiftId.toString(),
|
|
4110
|
-
category,
|
|
4111
|
-
pageSize,
|
|
4112
|
-
startAfter,
|
|
4113
|
-
sopCategories: this.sopCategories
|
|
4114
|
-
});
|
|
4115
|
-
return {
|
|
4116
|
-
videos: response.videos,
|
|
4117
|
-
nextToken: response.nextToken,
|
|
4118
|
-
hasMore: response.hasMore
|
|
4119
|
-
};
|
|
4120
|
-
});
|
|
4121
|
-
}
|
|
4122
|
-
/**
|
|
4123
|
-
* Batch fetch multiple videos in parallel
|
|
4124
|
-
*/
|
|
4125
|
-
async batchFetchVideos(workspaceId, date, shiftId, requests) {
|
|
4126
|
-
const batchKey = `batch:${workspaceId}:${date}:${shiftId}:${requests.length}`;
|
|
4127
|
-
return this.deduplicate(batchKey, async () => {
|
|
4128
|
-
const response = await this.fetchWithAuth("/api/clips/batch", {
|
|
4129
|
-
workspaceId,
|
|
4130
|
-
date,
|
|
4131
|
-
shift: shiftId.toString(),
|
|
4132
|
-
requests,
|
|
4133
|
-
sopCategories: this.sopCategories
|
|
4134
|
-
});
|
|
4135
|
-
console.log(`[S3ClipsAPIClient] Batch fetched ${response.videos.length} videos in ${response.performance.duration}ms`);
|
|
4136
|
-
return response.videos;
|
|
4137
|
-
});
|
|
4138
|
-
}
|
|
4139
|
-
/**
|
|
4140
|
-
* Convert S3 URI to CloudFront URL
|
|
4141
|
-
* In the API client, URLs are already signed from the server
|
|
4142
|
-
*/
|
|
4143
|
-
s3UriToCloudfront(s3Uri) {
|
|
4144
|
-
return s3Uri;
|
|
4145
|
-
}
|
|
4146
|
-
/**
|
|
4147
|
-
* Clean up resources
|
|
4148
|
-
*/
|
|
4149
|
-
dispose() {
|
|
4150
|
-
this.requestCache.clear();
|
|
4151
|
-
}
|
|
4152
|
-
/**
|
|
4153
|
-
* Get service statistics
|
|
4154
|
-
*/
|
|
4155
|
-
getStats() {
|
|
4156
|
-
return {
|
|
4157
|
-
requestCache: {
|
|
4158
|
-
pendingCount: this.requestCache.size,
|
|
4159
|
-
maxSize: 1e3
|
|
4160
|
-
}
|
|
4161
|
-
};
|
|
4162
|
-
}
|
|
4163
|
-
};
|
|
4164
|
-
|
|
4165
|
-
// src/lib/api/s3-clips-secure.ts
|
|
4166
|
-
var S3ClipsService = class {
|
|
4167
|
-
constructor(config) {
|
|
4168
|
-
// Flags for compatibility
|
|
4169
|
-
this.isIndexBuilding = false;
|
|
4170
|
-
this.isPrefetching = false;
|
|
4171
|
-
this.currentMetadataFetches = 0;
|
|
4172
|
-
this.MAX_CONCURRENT_METADATA = 3;
|
|
4173
|
-
this.config = config;
|
|
4174
|
-
if (!config.s3Config) {
|
|
4175
|
-
throw new Error("S3 configuration is required");
|
|
4176
|
-
}
|
|
4177
|
-
const sopCategories = config.s3Config.sopCategories?.default;
|
|
4178
|
-
this.apiClient = new S3ClipsAPIClient(sopCategories);
|
|
4179
|
-
const processing = config.s3Config.processing || {};
|
|
4180
|
-
this.defaultLimitPerCategory = processing.defaultLimitPerCategory || 30;
|
|
4181
|
-
this.maxLimitPerCategory = processing.maxLimitPerCategory || 1e3;
|
|
4182
|
-
this.concurrencyLimit = processing.concurrencyLimit || 10;
|
|
4183
|
-
this.maxInitialFetch = processing.maxInitialFetch || 60;
|
|
4184
|
-
console.log("[S3ClipsService] \u2705 Initialized with secure API client - AWS credentials are now server-side only!");
|
|
4185
|
-
}
|
|
4186
|
-
/**
|
|
4187
|
-
* Lists S3 clips using API
|
|
4188
|
-
*/
|
|
4189
|
-
async listS3Clips(params) {
|
|
4190
|
-
const { workspaceId, date, shiftId } = params;
|
|
4191
|
-
if (!isValidShiftId(shiftId)) {
|
|
4192
|
-
console.error(`[S3ClipsService] Invalid shift ID: ${shiftId}`);
|
|
4193
|
-
return [];
|
|
4194
|
-
}
|
|
4195
|
-
console.log(`[S3ClipsService] Listing clips via API for workspace: ${workspaceId}`);
|
|
4196
|
-
try {
|
|
4197
|
-
return await this.apiClient.listS3Clips(params);
|
|
4198
|
-
} catch (error) {
|
|
4199
|
-
console.error("[S3ClipsService] Error listing clips:", error);
|
|
4200
|
-
return [];
|
|
4201
|
-
}
|
|
4202
|
-
}
|
|
4203
|
-
/**
|
|
4204
|
-
* Get metadata cycle time
|
|
4205
|
-
*/
|
|
4206
|
-
async getMetadataCycleTime(playlistUri) {
|
|
4207
|
-
try {
|
|
4208
|
-
return await this.apiClient.getMetadataCycleTime(playlistUri);
|
|
4209
|
-
} catch (error) {
|
|
4210
|
-
console.error("[S3ClipsService] Error fetching metadata cycle time:", error);
|
|
4211
|
-
return null;
|
|
4212
|
-
}
|
|
4213
|
-
}
|
|
4214
|
-
/**
|
|
4215
|
-
* Control prefetch mode
|
|
4216
|
-
*/
|
|
4217
|
-
setPrefetchMode(enabled) {
|
|
4218
|
-
this.isPrefetching = enabled;
|
|
4219
|
-
console.log(`[S3ClipsService] Prefetch mode ${enabled ? "enabled" : "disabled"}`);
|
|
4220
|
-
}
|
|
4221
|
-
/**
|
|
4222
|
-
* Get full metadata
|
|
4223
|
-
*/
|
|
4224
|
-
async getFullMetadata(playlistUri) {
|
|
4225
|
-
if (this.isIndexBuilding || this.isPrefetching) {
|
|
4226
|
-
console.warn("[S3ClipsService] Skipping metadata - operation in progress");
|
|
4227
|
-
return null;
|
|
4228
|
-
}
|
|
4229
|
-
if (this.currentMetadataFetches >= this.MAX_CONCURRENT_METADATA) {
|
|
4230
|
-
console.warn("[S3ClipsService] Skipping metadata - max concurrent fetches reached");
|
|
4231
|
-
return null;
|
|
4232
|
-
}
|
|
4233
|
-
this.currentMetadataFetches++;
|
|
4234
|
-
try {
|
|
4235
|
-
return await this.apiClient.getMetadata(playlistUri);
|
|
4236
|
-
} catch (error) {
|
|
4237
|
-
console.error("[S3ClipsService] Error fetching metadata:", error);
|
|
4238
|
-
return null;
|
|
4239
|
-
} finally {
|
|
4240
|
-
this.currentMetadataFetches--;
|
|
4241
|
-
}
|
|
4242
|
-
}
|
|
4243
|
-
/**
|
|
4244
|
-
* Convert S3 URI to CloudFront URL
|
|
4245
|
-
* URLs from API are already signed
|
|
4246
|
-
*/
|
|
4247
|
-
s3UriToCloudfront(s3Uri) {
|
|
4248
|
-
if (s3Uri.startsWith("http")) {
|
|
4249
|
-
return s3Uri;
|
|
4250
|
-
}
|
|
4251
|
-
console.warn("[S3ClipsService] Unexpected S3 URI in secure mode:", s3Uri);
|
|
4252
|
-
return s3Uri;
|
|
4253
|
-
}
|
|
4254
|
-
/**
|
|
4255
|
-
* Get SOP categories for workspace
|
|
4256
|
-
*/
|
|
4257
|
-
getSOPCategories(workspaceId) {
|
|
4258
|
-
const sopConfig = this.config.s3Config?.sopCategories;
|
|
4259
|
-
if (!sopConfig) return void 0;
|
|
4260
|
-
if (sopConfig.workspaceOverrides && sopConfig.workspaceOverrides[workspaceId]) {
|
|
4261
|
-
return sopConfig.workspaceOverrides[workspaceId];
|
|
4262
|
-
}
|
|
4263
|
-
return sopConfig.default;
|
|
4264
|
-
}
|
|
4265
|
-
async getClipCounts(workspaceId, date, shiftId, buildIndex) {
|
|
4266
|
-
if (!isValidShiftId(shiftId)) {
|
|
4267
|
-
console.error(`[S3ClipsService] Invalid shift ID: ${shiftId}`);
|
|
4268
|
-
return buildIndex ? { counts: {}, videoIndex: this.createEmptyVideoIndex(workspaceId, date, shiftId.toString()) } : {};
|
|
4269
|
-
}
|
|
4270
|
-
try {
|
|
4271
|
-
if (buildIndex) {
|
|
4272
|
-
const result = await this.apiClient.getClipCountsWithIndex(workspaceId, date, shiftId);
|
|
4273
|
-
const cacheKey = `clip-counts:${workspaceId}:${date}:${shiftId}`;
|
|
4274
|
-
await smartVideoCache.setClipCounts(cacheKey, result);
|
|
4275
|
-
return result;
|
|
4276
|
-
} else {
|
|
4277
|
-
const counts = await this.apiClient.getClipCounts(workspaceId, date, shiftId);
|
|
4278
|
-
return counts;
|
|
4279
|
-
}
|
|
4280
|
-
} catch (error) {
|
|
4281
|
-
console.error("[S3ClipsService] Error fetching clip counts:", error);
|
|
4282
|
-
return buildIndex ? { counts: {}, videoIndex: this.createEmptyVideoIndex(workspaceId, date, shiftId.toString()) } : {};
|
|
4283
|
-
}
|
|
4284
|
-
}
|
|
4285
|
-
async getClipCountsCacheFirst(workspaceId, date, shiftId, buildIndex) {
|
|
4286
|
-
const cacheKey = `clip-counts:${workspaceId}:${date}:${shiftId}`;
|
|
4287
|
-
const cachedResult = await smartVideoCache.getClipCounts(cacheKey);
|
|
4288
|
-
if (cachedResult) {
|
|
4289
|
-
console.log("[S3ClipsService] Using cached clip counts");
|
|
4290
|
-
return buildIndex ? cachedResult : cachedResult.counts;
|
|
4291
|
-
}
|
|
4292
|
-
console.log("[S3ClipsService] Cache miss - fetching from API");
|
|
4293
|
-
return buildIndex ? this.getClipCounts(workspaceId, date, shiftId, true) : this.getClipCounts(workspaceId, date, shiftId);
|
|
4294
|
-
}
|
|
4295
|
-
/**
|
|
4296
|
-
* Get first clip for category
|
|
4297
|
-
*/
|
|
4298
|
-
async getFirstClipForCategory(workspaceId, date, shiftId, category) {
|
|
4299
|
-
if (!isValidShiftId(shiftId)) {
|
|
4300
|
-
console.error(`[S3ClipsService] Invalid shift ID: ${shiftId}`);
|
|
4301
|
-
return null;
|
|
4302
|
-
}
|
|
4303
|
-
try {
|
|
4304
|
-
return await this.apiClient.getFirstClipForCategory(workspaceId, date, shiftId, category);
|
|
4305
|
-
} catch (error) {
|
|
4306
|
-
console.error("[S3ClipsService] Error fetching first clip:", error);
|
|
4307
|
-
return null;
|
|
4308
|
-
}
|
|
4309
|
-
}
|
|
4310
|
-
/**
|
|
4311
|
-
* Get video from index (for compatibility)
|
|
4312
|
-
*/
|
|
4313
|
-
async getVideoFromIndex(videoIndex, category, index, includeCycleTime = true, includeMetadata = true) {
|
|
4314
|
-
const { workspaceId, date, shiftId } = videoIndex;
|
|
4315
|
-
return this.getClipByIndex(
|
|
4316
|
-
workspaceId,
|
|
4317
|
-
date,
|
|
4318
|
-
shiftId,
|
|
4319
|
-
category,
|
|
4320
|
-
index,
|
|
4321
|
-
includeCycleTime,
|
|
4322
|
-
includeMetadata
|
|
4323
|
-
);
|
|
4324
|
-
}
|
|
4325
|
-
/**
|
|
4326
|
-
* Get clip by index
|
|
4327
|
-
*/
|
|
4328
|
-
async getClipByIndex(workspaceId, date, shiftId, category, index, includeCycleTime = true, includeMetadata = false) {
|
|
4329
|
-
try {
|
|
4330
|
-
return await this.apiClient.getClipByIndex(
|
|
4331
|
-
workspaceId,
|
|
4332
|
-
date,
|
|
4333
|
-
shiftId,
|
|
4334
|
-
category,
|
|
4335
|
-
index,
|
|
4336
|
-
includeCycleTime,
|
|
4337
|
-
includeMetadata
|
|
4338
|
-
);
|
|
4339
|
-
} catch (error) {
|
|
4340
|
-
console.error("[S3ClipsService] Error fetching clip by index:", error);
|
|
4341
|
-
return null;
|
|
4342
|
-
}
|
|
4343
|
-
}
|
|
4344
|
-
/**
|
|
4345
|
-
* Process full video (for compatibility)
|
|
4346
|
-
*/
|
|
4347
|
-
async processFullVideo(uri, index, workspaceId, date, shiftId, includeCycleTime, includeMetadata = false) {
|
|
4348
|
-
const sopCategories = this.getSOPCategories(workspaceId);
|
|
4349
|
-
const parsedInfo = parseS3Uri(uri, sopCategories);
|
|
4350
|
-
if (!parsedInfo) {
|
|
4351
|
-
console.warn(`Skipping URI due to parsing failure: ${uri}`);
|
|
4352
|
-
return null;
|
|
4353
|
-
}
|
|
4354
|
-
const video = {
|
|
4355
|
-
id: `${workspaceId}-${date}-${shiftId}-${index}`,
|
|
4356
|
-
src: uri,
|
|
4357
|
-
// Already signed from API
|
|
4358
|
-
...parsedInfo,
|
|
4359
|
-
originalUri: uri
|
|
4360
|
-
};
|
|
4361
|
-
if (includeMetadata) {
|
|
4362
|
-
const metadata = await this.getFullMetadata(uri);
|
|
4363
|
-
if (metadata) {
|
|
4364
|
-
video.cycle_time_seconds = metadata.original_task_metadata?.cycle_time;
|
|
4365
|
-
video.creation_timestamp = metadata.upload_timestamp || metadata.original_task_metadata?.timestamp || metadata.creation_timestamp;
|
|
4366
|
-
}
|
|
4367
|
-
}
|
|
4368
|
-
return video;
|
|
4369
|
-
}
|
|
4370
|
-
/**
|
|
4371
|
-
* Fetch clips (main method for compatibility)
|
|
4372
|
-
*/
|
|
4373
|
-
async fetchClips(params) {
|
|
4374
|
-
const {
|
|
4375
|
-
workspaceId,
|
|
4376
|
-
date: inputDate,
|
|
4377
|
-
shift,
|
|
4378
|
-
category,
|
|
4379
|
-
limit,
|
|
4380
|
-
offset = 0,
|
|
4381
|
-
mode
|
|
4382
|
-
} = params;
|
|
4383
|
-
if (!workspaceId) {
|
|
4384
|
-
throw new Error("Valid Workspace ID is required");
|
|
4385
|
-
}
|
|
4386
|
-
const date = inputDate || getOperationalDate(
|
|
4387
|
-
this.config.dateTimeConfig?.defaultTimezone || "Asia/Kolkata",
|
|
4388
|
-
/* @__PURE__ */ new Date(),
|
|
4389
|
-
this.config.shiftConfig?.dayShift?.startTime || "06:00"
|
|
4390
|
-
);
|
|
4391
|
-
if (!isValidDateFormat(date)) {
|
|
4392
|
-
throw new Error("Invalid date format. Use YYYY-MM-DD.");
|
|
4393
|
-
}
|
|
4394
|
-
let shiftId;
|
|
4395
|
-
if (shift !== void 0) {
|
|
4396
|
-
if (!isValidShiftId(shift)) {
|
|
4397
|
-
throw new Error("Invalid shift value. Must be 0 (day) or 1 (night).");
|
|
4398
|
-
}
|
|
4399
|
-
shiftId = parseInt(shift, 10);
|
|
4400
|
-
} else {
|
|
4401
|
-
const { shiftId: currentShiftId } = getCurrentShift(
|
|
4402
|
-
this.config.dateTimeConfig?.defaultTimezone || "Asia/Kolkata",
|
|
4403
|
-
this.config.shiftConfig
|
|
4404
|
-
);
|
|
4405
|
-
shiftId = currentShiftId;
|
|
4406
|
-
}
|
|
4407
|
-
console.log(`[S3ClipsService] Fetching clips for workspace ${workspaceId}`);
|
|
4408
|
-
if (mode === "summary") {
|
|
4409
|
-
const counts = await this.getClipCounts(workspaceId, date, shiftId.toString());
|
|
4410
|
-
return { counts, samples: {} };
|
|
4411
|
-
}
|
|
4412
|
-
const effectiveLimit = limit || this.defaultLimitPerCategory;
|
|
4413
|
-
const result = await this.getVideosPage(
|
|
4414
|
-
workspaceId,
|
|
4415
|
-
date,
|
|
4416
|
-
shiftId,
|
|
4417
|
-
category || "all",
|
|
4418
|
-
effectiveLimit
|
|
4419
|
-
);
|
|
4420
|
-
return result.videos;
|
|
4421
|
-
}
|
|
4422
|
-
/**
|
|
4423
|
-
* Batch fetch multiple videos in parallel
|
|
4424
|
-
*/
|
|
4425
|
-
async batchFetchVideos(workspaceId, date, shiftId, requests) {
|
|
4426
|
-
try {
|
|
4427
|
-
const results = await this.apiClient.batchFetchVideos(
|
|
4428
|
-
workspaceId,
|
|
4429
|
-
date,
|
|
4430
|
-
shiftId,
|
|
4431
|
-
requests
|
|
4432
|
-
);
|
|
4433
|
-
return results.map((r2) => r2.video).filter((v) => v !== null);
|
|
4434
|
-
} catch (error) {
|
|
4435
|
-
console.error("[S3ClipsService] Error batch fetching videos:", error);
|
|
4436
|
-
return [];
|
|
4437
|
-
}
|
|
4438
|
-
}
|
|
4439
|
-
/**
|
|
4440
|
-
* Get videos page using pagination API
|
|
4441
|
-
*/
|
|
4442
|
-
async getVideosPage(workspaceId, date, shiftId, category, pageSize = 5, startAfter) {
|
|
4443
|
-
try {
|
|
4444
|
-
return await this.apiClient.getVideosPage(
|
|
4445
|
-
workspaceId,
|
|
4446
|
-
date,
|
|
4447
|
-
shiftId,
|
|
4448
|
-
category,
|
|
4449
|
-
pageSize,
|
|
4450
|
-
startAfter
|
|
4451
|
-
);
|
|
4452
|
-
} catch (error) {
|
|
4453
|
-
console.error("[S3ClipsService] Error fetching videos page:", error);
|
|
4454
|
-
return { videos: [], hasMore: false };
|
|
4455
|
-
}
|
|
4456
|
-
}
|
|
4457
|
-
/**
|
|
4458
|
-
* Create empty video index for compatibility
|
|
4459
|
-
*/
|
|
4460
|
-
createEmptyVideoIndex(workspaceId, date, shiftId) {
|
|
4461
|
-
return {
|
|
4462
|
-
byCategory: /* @__PURE__ */ new Map(),
|
|
4463
|
-
allVideos: [],
|
|
4464
|
-
counts: {},
|
|
4465
|
-
workspaceId,
|
|
4466
|
-
date,
|
|
4467
|
-
shiftId,
|
|
4468
|
-
lastUpdated: /* @__PURE__ */ new Date(),
|
|
4469
|
-
_debugId: `empty_${Date.now()}`
|
|
4470
|
-
};
|
|
4471
|
-
}
|
|
4472
|
-
/**
|
|
4473
|
-
* Get clip by ID
|
|
4474
|
-
*/
|
|
4475
|
-
async getClipById(clipId, sopCategories) {
|
|
4476
|
-
console.log(`[S3ClipsService] Getting clip by ID: ${clipId} (Note: This is a fallback implementation)`);
|
|
4477
|
-
try {
|
|
4478
|
-
const parts = clipId.split("-");
|
|
4479
|
-
if (parts.length >= 5) {
|
|
4480
|
-
const workspaceId = parts[0];
|
|
4481
|
-
const date = parts[1];
|
|
4482
|
-
const shift = parts[2];
|
|
4483
|
-
const category = parts[3];
|
|
4484
|
-
const index = parseInt(parts[4], 10);
|
|
4485
|
-
if (!isNaN(index)) {
|
|
4486
|
-
return await this.getClipByIndex(workspaceId, date, shift, category, index, true, false);
|
|
4487
|
-
}
|
|
4488
|
-
}
|
|
4489
|
-
console.warn(`[S3ClipsService] Could not parse clipId: ${clipId}`);
|
|
4490
|
-
return null;
|
|
4491
|
-
} catch (error) {
|
|
4492
|
-
console.error("[S3ClipsService] Error getting clip by ID:", error);
|
|
4493
|
-
return null;
|
|
4494
|
-
}
|
|
4495
|
-
}
|
|
4496
|
-
/**
|
|
4497
|
-
* Get neighboring clips
|
|
4498
|
-
*/
|
|
4499
|
-
async getNeighboringClips(workspaceId, date, shiftId, category, currentClipId, sopCategories) {
|
|
4500
|
-
console.log(`[S3ClipsService] Getting neighboring clips for ID: ${currentClipId} (Note: This is a fallback implementation)`);
|
|
4501
|
-
try {
|
|
4502
|
-
const parts = currentClipId.split("-");
|
|
4503
|
-
if (parts.length >= 5) {
|
|
4504
|
-
const index = parseInt(parts[4], 10);
|
|
4505
|
-
if (!isNaN(index)) {
|
|
4506
|
-
const [previous, next] = await Promise.all([
|
|
4507
|
-
index > 0 ? this.getClipByIndex(workspaceId, date, shiftId, category, index - 1, true, false) : null,
|
|
4508
|
-
this.getClipByIndex(workspaceId, date, shiftId, category, index + 1, true, false)
|
|
4509
|
-
]);
|
|
4510
|
-
return { previous, next };
|
|
4511
|
-
}
|
|
4512
|
-
}
|
|
4513
|
-
console.warn(`[S3ClipsService] Could not parse currentClipId: ${currentClipId}`);
|
|
4514
|
-
return { previous: null, next: null };
|
|
4515
|
-
} catch (error) {
|
|
4516
|
-
console.error("[S3ClipsService] Error getting neighboring clips:", error);
|
|
4517
|
-
return { previous: null, next: null };
|
|
4518
|
-
}
|
|
4519
|
-
}
|
|
4520
|
-
/**
|
|
4521
|
-
* Cleanup
|
|
4522
|
-
*/
|
|
4523
|
-
dispose() {
|
|
4524
|
-
console.log("[S3ClipsService] Disposing service");
|
|
4525
|
-
this.apiClient.dispose();
|
|
4526
|
-
}
|
|
4527
|
-
/**
|
|
4528
|
-
* Get statistics
|
|
4529
|
-
*/
|
|
4530
|
-
getStats() {
|
|
4531
|
-
return this.apiClient.getStats();
|
|
4532
|
-
}
|
|
4533
|
-
};
|
|
4534
|
-
var getSupabaseClient2 = () => {
|
|
4535
|
-
const url = process.env.NEXT_PUBLIC_SUPABASE_URL;
|
|
4536
|
-
const key = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
|
|
4537
|
-
if (!url || !key) {
|
|
4538
|
-
throw new Error("Supabase configuration missing");
|
|
4539
|
-
}
|
|
4540
|
-
return supabaseJs.createClient(url, key);
|
|
4541
|
-
};
|
|
4542
|
-
var getAuthToken2 = async () => {
|
|
4543
|
-
try {
|
|
4544
|
-
const supabase = getSupabaseClient2();
|
|
4545
|
-
const { data: { session } } = await supabase.auth.getSession();
|
|
4546
3617
|
console.log("[S3ClipsSupabase] Auth session exists:", !!session, "has token:", !!session?.access_token);
|
|
4547
3618
|
return session?.access_token || null;
|
|
4548
3619
|
} catch (error) {
|
|
@@ -4573,7 +3644,7 @@ var S3ClipsSupabaseService = class {
|
|
|
4573
3644
|
* Fetch with authentication and error handling
|
|
4574
3645
|
*/
|
|
4575
3646
|
async fetchWithAuth(endpoint, body) {
|
|
4576
|
-
const token = await
|
|
3647
|
+
const token = await getAuthToken();
|
|
4577
3648
|
if (!token) {
|
|
4578
3649
|
throw new Error("Authentication required");
|
|
4579
3650
|
}
|
|
@@ -5036,7 +4107,8 @@ var S3ClipsSupabaseService = class {
|
|
|
5036
4107
|
return descriptions[clipType] || "Analysis Clip";
|
|
5037
4108
|
}
|
|
5038
4109
|
};
|
|
5039
|
-
|
|
4110
|
+
|
|
4111
|
+
// src/lib/services/videoPrefetchManager.ts
|
|
5040
4112
|
var VideoPrefetchManager = class extends events.EventEmitter {
|
|
5041
4113
|
constructor() {
|
|
5042
4114
|
super();
|
|
@@ -5050,7 +4122,7 @@ var VideoPrefetchManager = class extends events.EventEmitter {
|
|
|
5050
4122
|
getS3Service(dashboardConfig) {
|
|
5051
4123
|
const configKey = JSON.stringify(dashboardConfig.s3Config);
|
|
5052
4124
|
if (!this.s3Services.has(configKey)) {
|
|
5053
|
-
this.s3Services.set(configKey, new
|
|
4125
|
+
this.s3Services.set(configKey, new S3ClipsSupabaseService(dashboardConfig));
|
|
5054
4126
|
}
|
|
5055
4127
|
return this.s3Services.get(configKey);
|
|
5056
4128
|
}
|
|
@@ -22835,32 +21907,10 @@ var VideoGridView = React20__namespace.default.memo(({
|
|
|
22835
21907
|
observerRef.current?.disconnect();
|
|
22836
21908
|
};
|
|
22837
21909
|
}, [filteredWorkspaces]);
|
|
22838
|
-
|
|
21910
|
+
React20.useRef({});
|
|
22839
21911
|
const handleWorkspaceClick = React20.useCallback((workspace) => {
|
|
22840
21912
|
const workspaceId = workspace.workspace_uuid || workspace.workspace_name;
|
|
22841
|
-
|
|
22842
|
-
const operationalDate = getOperationalDate(dashboardConfig.dateTimeConfig?.defaultTimezone);
|
|
22843
|
-
const fullKey = `${workspaceId}-${operationalDate}-all-all-meta-1000--`;
|
|
22844
|
-
if (!prefetchCacheRef.current[fullKey]?.status) {
|
|
22845
|
-
const clipsService = new S3ClipsService(dashboardConfig);
|
|
22846
|
-
const fullPromise = clipsService.fetchClips({
|
|
22847
|
-
workspaceId,
|
|
22848
|
-
date: operationalDate,
|
|
22849
|
-
mode: "full",
|
|
22850
|
-
includeCycleTime: true,
|
|
22851
|
-
includeMetadata: true,
|
|
22852
|
-
limit: 1e3
|
|
22853
|
-
});
|
|
22854
|
-
prefetchCacheRef.current[fullKey] = { status: "pending", promise: fullPromise };
|
|
22855
|
-
fullPromise.then((data) => {
|
|
22856
|
-
prefetchCacheRef.current[fullKey] = { status: "resolved", data };
|
|
22857
|
-
console.log(`Prefetched full clips data for workspace ${workspaceId}`);
|
|
22858
|
-
}).catch((error) => {
|
|
22859
|
-
prefetchCacheRef.current[fullKey] = { status: "rejected", error };
|
|
22860
|
-
console.warn(`Failed to prefetch full clips for workspace ${workspaceId}:`, error);
|
|
22861
|
-
});
|
|
22862
|
-
}
|
|
22863
|
-
}
|
|
21913
|
+
console.log(`[VideoGridView] Prefetching disabled for workspace ${workspaceId}`);
|
|
22864
21914
|
trackCoreEvent("Workspace Detail Clicked", {
|
|
22865
21915
|
workspace_name: workspace.workspace_name,
|
|
22866
21916
|
workspace_id: workspaceId,
|
|
@@ -28917,7 +27967,7 @@ var NewClipsNotification = ({
|
|
|
28917
27967
|
}
|
|
28918
27968
|
);
|
|
28919
27969
|
};
|
|
28920
|
-
var
|
|
27970
|
+
var getSupabaseClient2 = () => {
|
|
28921
27971
|
const url = process.env.NEXT_PUBLIC_SUPABASE_URL;
|
|
28922
27972
|
const key = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
|
|
28923
27973
|
if (!url || !key) {
|
|
@@ -28925,9 +27975,9 @@ var getSupabaseClient3 = () => {
|
|
|
28925
27975
|
}
|
|
28926
27976
|
return supabaseJs.createClient(url, key);
|
|
28927
27977
|
};
|
|
28928
|
-
var
|
|
27978
|
+
var getAuthToken2 = async () => {
|
|
28929
27979
|
try {
|
|
28930
|
-
const supabase =
|
|
27980
|
+
const supabase = getSupabaseClient2();
|
|
28931
27981
|
const { data: { session } } = await supabase.auth.getSession();
|
|
28932
27982
|
return session?.access_token || null;
|
|
28933
27983
|
} catch (error) {
|
|
@@ -28948,7 +27998,7 @@ function useWorkspaceCrop(workspaceId) {
|
|
|
28948
27998
|
setIsLoading(true);
|
|
28949
27999
|
setError(null);
|
|
28950
28000
|
try {
|
|
28951
|
-
const token = await
|
|
28001
|
+
const token = await getAuthToken2();
|
|
28952
28002
|
if (!token) {
|
|
28953
28003
|
throw new Error("Authentication required");
|
|
28954
28004
|
}
|
|
@@ -29101,7 +28151,7 @@ var FileManagerFilters = ({
|
|
|
29101
28151
|
method: "POST",
|
|
29102
28152
|
headers: {
|
|
29103
28153
|
"Content-Type": "application/json",
|
|
29104
|
-
"Authorization": `Bearer ${await
|
|
28154
|
+
"Authorization": `Bearer ${await getAuthToken3()}`
|
|
29105
28155
|
},
|
|
29106
28156
|
body: JSON.stringify({
|
|
29107
28157
|
action: "clip-metadata",
|
|
@@ -29134,10 +28184,10 @@ var FileManagerFilters = ({
|
|
|
29134
28184
|
});
|
|
29135
28185
|
}
|
|
29136
28186
|
}, [workspaceId, date, shift]);
|
|
29137
|
-
const
|
|
28187
|
+
const getAuthToken3 = async () => {
|
|
29138
28188
|
try {
|
|
29139
|
-
const { createClient:
|
|
29140
|
-
const supabase =
|
|
28189
|
+
const { createClient: createClient4 } = await import('@supabase/supabase-js');
|
|
28190
|
+
const supabase = createClient4(
|
|
29141
28191
|
process.env.NEXT_PUBLIC_SUPABASE_URL || "",
|
|
29142
28192
|
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || ""
|
|
29143
28193
|
);
|
|
@@ -29160,7 +28210,7 @@ var FileManagerFilters = ({
|
|
|
29160
28210
|
method: "POST",
|
|
29161
28211
|
headers: {
|
|
29162
28212
|
"Content-Type": "application/json",
|
|
29163
|
-
"Authorization": `Bearer ${await
|
|
28213
|
+
"Authorization": `Bearer ${await getAuthToken3()}`
|
|
29164
28214
|
},
|
|
29165
28215
|
body: JSON.stringify({
|
|
29166
28216
|
action: "percentile-clips",
|
|
@@ -29903,8 +28953,6 @@ function useClipsRealtimeUpdates({
|
|
|
29903
28953
|
hasNewClips: newClipsNotification !== null && newClipsNotification.count > 0
|
|
29904
28954
|
};
|
|
29905
28955
|
}
|
|
29906
|
-
var USE_SUPABASE_CLIPS2 = true;
|
|
29907
|
-
var S3ClipsService3 = S3ClipsSupabaseService ;
|
|
29908
28956
|
var BottlenecksContent = ({
|
|
29909
28957
|
workspaceId,
|
|
29910
28958
|
workspaceName,
|
|
@@ -29962,8 +29010,8 @@ var BottlenecksContent = ({
|
|
|
29962
29010
|
workspaceId,
|
|
29963
29011
|
date: date || getOperationalDate(),
|
|
29964
29012
|
shiftId: effectiveShift,
|
|
29965
|
-
enabled:
|
|
29966
|
-
//
|
|
29013
|
+
enabled: true,
|
|
29014
|
+
// Supabase implementation
|
|
29967
29015
|
onNewClips: (notification) => {
|
|
29968
29016
|
console.log(`[BottlenecksContent] New clips detected:`, notification);
|
|
29969
29017
|
if (notification.clips.length > 0) {
|
|
@@ -29988,7 +29036,7 @@ var BottlenecksContent = ({
|
|
|
29988
29036
|
console.warn("S3 configuration not found in dashboard config");
|
|
29989
29037
|
return null;
|
|
29990
29038
|
}
|
|
29991
|
-
return new
|
|
29039
|
+
return new S3ClipsSupabaseService(dashboardConfig);
|
|
29992
29040
|
}, [dashboardConfig]);
|
|
29993
29041
|
const {
|
|
29994
29042
|
clipTypes,
|
|
@@ -30009,8 +29057,7 @@ var BottlenecksContent = ({
|
|
|
30009
29057
|
dynamicCounts,
|
|
30010
29058
|
workspaceId,
|
|
30011
29059
|
date: date || getOperationalDate(),
|
|
30012
|
-
shift: shift || "0"
|
|
30013
|
-
USE_SUPABASE_CLIPS: USE_SUPABASE_CLIPS2
|
|
29060
|
+
shift: shift || "0"
|
|
30014
29061
|
});
|
|
30015
29062
|
React20.useEffect(() => {
|
|
30016
29063
|
if (clipTypes.length > 0 && !initialFilter) {
|
|
@@ -30118,12 +29165,8 @@ var BottlenecksContent = ({
|
|
|
30118
29165
|
operationalDate,
|
|
30119
29166
|
shiftStr,
|
|
30120
29167
|
targetCategory,
|
|
30121
|
-
0
|
|
29168
|
+
0
|
|
30122
29169
|
// First video (index 0)
|
|
30123
|
-
true,
|
|
30124
|
-
// includeCycleTime
|
|
30125
|
-
true
|
|
30126
|
-
// includeMetadata
|
|
30127
29170
|
);
|
|
30128
29171
|
if (firstVideo && isMountedRef.current) {
|
|
30129
29172
|
console.log(`[BottlenecksContent] Successfully loaded first video via index`);
|
|
@@ -30267,8 +29310,8 @@ var BottlenecksContent = ({
|
|
|
30267
29310
|
if (isPercentileCategory(categoryId)) {
|
|
30268
29311
|
console.log(`[BottlenecksContent] Loading percentile category: ${categoryId}`);
|
|
30269
29312
|
const percentileType = categoryId === "fast-cycles" ? "fast-cycles" : categoryId === "slow-cycles" ? "slow-cycles" : "idle-times";
|
|
30270
|
-
const { createClient:
|
|
30271
|
-
const supabase =
|
|
29313
|
+
const { createClient: createClient4 } = await import('@supabase/supabase-js');
|
|
29314
|
+
const supabase = createClient4(
|
|
30272
29315
|
process.env.NEXT_PUBLIC_SUPABASE_URL || "",
|
|
30273
29316
|
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || ""
|
|
30274
29317
|
);
|
|
@@ -30340,9 +29383,7 @@ var BottlenecksContent = ({
|
|
|
30340
29383
|
operationalDate,
|
|
30341
29384
|
shiftStr,
|
|
30342
29385
|
categoryId,
|
|
30343
|
-
clipIndex
|
|
30344
|
-
true,
|
|
30345
|
-
true
|
|
29386
|
+
clipIndex
|
|
30346
29387
|
);
|
|
30347
29388
|
if (video?.id) {
|
|
30348
29389
|
await loadAndPlayClipById(video.id, categoryId);
|
|
@@ -44506,6 +43547,177 @@ var streamProxyConfig = {
|
|
|
44506
43547
|
}
|
|
44507
43548
|
};
|
|
44508
43549
|
|
|
43550
|
+
// src/lib/api/s3-clips-parser.ts
|
|
43551
|
+
function parseS3Uri(s3Uri, sopCategories) {
|
|
43552
|
+
const path = new URL(s3Uri).pathname;
|
|
43553
|
+
const parts = path.split("/").filter((p) => p);
|
|
43554
|
+
if (s3Uri.includes("missed_qchecks")) {
|
|
43555
|
+
console.warn(`Skipping missed_qchecks URI in parseS3Uri: ${s3Uri}`);
|
|
43556
|
+
return null;
|
|
43557
|
+
}
|
|
43558
|
+
if (parts.length < 8) {
|
|
43559
|
+
console.warn(`Invalid S3 path structure: ${s3Uri} - Too few parts: ${parts.length}, expected at least 8`);
|
|
43560
|
+
return null;
|
|
43561
|
+
}
|
|
43562
|
+
try {
|
|
43563
|
+
const datePart = parts[2];
|
|
43564
|
+
const shiftPart = parts[3];
|
|
43565
|
+
const violationType = parts[4];
|
|
43566
|
+
let folderName = "";
|
|
43567
|
+
let timestamp = "";
|
|
43568
|
+
for (let i = 5; i < parts.length; i++) {
|
|
43569
|
+
const part = parts[i];
|
|
43570
|
+
if (part && part.includes("_") && /\d{8}_\d{6}/.test(part)) {
|
|
43571
|
+
folderName = part;
|
|
43572
|
+
const timeMatch = folderName.match(/_(\d{8})_(\d{6})_/);
|
|
43573
|
+
if (timeMatch) {
|
|
43574
|
+
timestamp = `${timeMatch[2].substring(0, 2)}:${timeMatch[2].substring(2, 4)}:${timeMatch[2].substring(4, 6)}`;
|
|
43575
|
+
break;
|
|
43576
|
+
}
|
|
43577
|
+
}
|
|
43578
|
+
}
|
|
43579
|
+
if (!timestamp) {
|
|
43580
|
+
console.warn(`Couldn't extract timestamp from any part: ${parts.join("/")}`);
|
|
43581
|
+
timestamp = "00:00:00";
|
|
43582
|
+
}
|
|
43583
|
+
let severity = "low";
|
|
43584
|
+
let type = "bottleneck";
|
|
43585
|
+
let description = "Analysis Clip";
|
|
43586
|
+
const normalizedViolationType = violationType.toLowerCase().trim();
|
|
43587
|
+
if (sopCategories && sopCategories.length > 0) {
|
|
43588
|
+
const matchedCategory = sopCategories.find((category) => {
|
|
43589
|
+
const categoryId = category.id.toLowerCase();
|
|
43590
|
+
const s3FolderName = (category.s3FolderName || category.id).toLowerCase();
|
|
43591
|
+
return categoryId === normalizedViolationType || s3FolderName === normalizedViolationType || // Also check for partial matches for flexibility
|
|
43592
|
+
normalizedViolationType.includes(categoryId) || normalizedViolationType.includes(s3FolderName);
|
|
43593
|
+
});
|
|
43594
|
+
if (matchedCategory) {
|
|
43595
|
+
type = matchedCategory.id;
|
|
43596
|
+
description = matchedCategory.description || matchedCategory.label;
|
|
43597
|
+
if (matchedCategory.color.includes("red")) {
|
|
43598
|
+
severity = "high";
|
|
43599
|
+
} else if (matchedCategory.color.includes("yellow") || matchedCategory.color.includes("orange")) {
|
|
43600
|
+
severity = "medium";
|
|
43601
|
+
} else {
|
|
43602
|
+
severity = "low";
|
|
43603
|
+
}
|
|
43604
|
+
console.log(`Matched SOP category: ${matchedCategory.id} for violation type: ${violationType}`);
|
|
43605
|
+
return { timestamp, severity, description, type, originalUri: s3Uri };
|
|
43606
|
+
}
|
|
43607
|
+
}
|
|
43608
|
+
switch (normalizedViolationType) {
|
|
43609
|
+
case "idle_time":
|
|
43610
|
+
case "idle":
|
|
43611
|
+
case "low_value":
|
|
43612
|
+
case "low value":
|
|
43613
|
+
case "low_value_moment":
|
|
43614
|
+
case "low_value_moments":
|
|
43615
|
+
case "low value moment":
|
|
43616
|
+
case "low value moments":
|
|
43617
|
+
type = "low_value";
|
|
43618
|
+
severity = "low";
|
|
43619
|
+
description = "Idle Time Detected";
|
|
43620
|
+
break;
|
|
43621
|
+
case "sop_deviation":
|
|
43622
|
+
type = "missing_quality_check";
|
|
43623
|
+
severity = "high";
|
|
43624
|
+
description = "SOP Deviations";
|
|
43625
|
+
break;
|
|
43626
|
+
case "long_cycle_time":
|
|
43627
|
+
severity = "high";
|
|
43628
|
+
type = "long_cycle_time";
|
|
43629
|
+
description = "Long Cycle Time Detected";
|
|
43630
|
+
break;
|
|
43631
|
+
case "best_cycle_time":
|
|
43632
|
+
type = "best_cycle_time";
|
|
43633
|
+
severity = "low";
|
|
43634
|
+
description = "Best Cycle Time Performance";
|
|
43635
|
+
break;
|
|
43636
|
+
case "worst_cycle_time":
|
|
43637
|
+
type = "worst_cycle_time";
|
|
43638
|
+
severity = "high";
|
|
43639
|
+
description = "Worst Cycle Time Performance";
|
|
43640
|
+
break;
|
|
43641
|
+
case "cycle_completion":
|
|
43642
|
+
case "completed_cycles":
|
|
43643
|
+
case "completed_cycle":
|
|
43644
|
+
type = "cycle_completion";
|
|
43645
|
+
severity = "low";
|
|
43646
|
+
description = "Cycle Completion";
|
|
43647
|
+
break;
|
|
43648
|
+
case "running_cycle":
|
|
43649
|
+
case "active_cycle":
|
|
43650
|
+
case "production_cycle":
|
|
43651
|
+
type = "running_cycle";
|
|
43652
|
+
severity = "low";
|
|
43653
|
+
description = "Active Production Cycle";
|
|
43654
|
+
break;
|
|
43655
|
+
case "setup_state":
|
|
43656
|
+
case "machine_setup":
|
|
43657
|
+
case "line_setup":
|
|
43658
|
+
type = "setup_state";
|
|
43659
|
+
severity = "medium";
|
|
43660
|
+
description = "Machine Setup Activity";
|
|
43661
|
+
break;
|
|
43662
|
+
case "medium_bottleneck":
|
|
43663
|
+
severity = "medium";
|
|
43664
|
+
description = "Medium Bottleneck Identified";
|
|
43665
|
+
break;
|
|
43666
|
+
case "minor_bottleneck":
|
|
43667
|
+
case "mild_bottleneck":
|
|
43668
|
+
severity = "low";
|
|
43669
|
+
description = "Minor Bottleneck Identified";
|
|
43670
|
+
break;
|
|
43671
|
+
default:
|
|
43672
|
+
if (normalizedViolationType.includes("sop") && normalizedViolationType.includes("deviation")) {
|
|
43673
|
+
type = "missing_quality_check";
|
|
43674
|
+
severity = "high";
|
|
43675
|
+
description = "SOP Deviations";
|
|
43676
|
+
} else if (normalizedViolationType.includes("worst") && normalizedViolationType.includes("cycle")) {
|
|
43677
|
+
type = "worst_cycle_time";
|
|
43678
|
+
severity = "high";
|
|
43679
|
+
description = "Worst Cycle Time Performance";
|
|
43680
|
+
} else if (normalizedViolationType.includes("best") && normalizedViolationType.includes("cycle")) {
|
|
43681
|
+
type = "best_cycle_time";
|
|
43682
|
+
severity = "low";
|
|
43683
|
+
description = "Best Cycle Time Performance";
|
|
43684
|
+
} else if (normalizedViolationType.includes("long") && normalizedViolationType.includes("cycle")) {
|
|
43685
|
+
type = "long_cycle_time";
|
|
43686
|
+
severity = "high";
|
|
43687
|
+
description = "Long Cycle Time Detected";
|
|
43688
|
+
} else if (normalizedViolationType.includes("cycle") && (normalizedViolationType.includes("completion") || normalizedViolationType.includes("complete"))) {
|
|
43689
|
+
type = "cycle_completion";
|
|
43690
|
+
severity = "low";
|
|
43691
|
+
description = "Cycle Completion";
|
|
43692
|
+
} else if (normalizedViolationType.includes("running") && normalizedViolationType.includes("cycle")) {
|
|
43693
|
+
type = "running_cycle";
|
|
43694
|
+
severity = "low";
|
|
43695
|
+
description = "Active Production Cycle";
|
|
43696
|
+
} else if (normalizedViolationType.includes("setup") || normalizedViolationType.includes("machine") && normalizedViolationType.includes("setup")) {
|
|
43697
|
+
type = "setup_state";
|
|
43698
|
+
severity = "medium";
|
|
43699
|
+
description = "Machine Setup Activity";
|
|
43700
|
+
} else {
|
|
43701
|
+
description = `Clip type: ${violationType.replace(/_/g, " ")}`;
|
|
43702
|
+
console.log(`Detected unknown violation type: ${violationType} in URI: ${s3Uri}`);
|
|
43703
|
+
}
|
|
43704
|
+
break;
|
|
43705
|
+
}
|
|
43706
|
+
return { timestamp, severity, description, type, originalUri: s3Uri };
|
|
43707
|
+
} catch (error) {
|
|
43708
|
+
console.error(`Error parsing S3 URI: ${s3Uri}`, error);
|
|
43709
|
+
return null;
|
|
43710
|
+
}
|
|
43711
|
+
}
|
|
43712
|
+
function shuffleArray(array) {
|
|
43713
|
+
const shuffled = [...array];
|
|
43714
|
+
for (let i = shuffled.length - 1; i > 0; i--) {
|
|
43715
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
43716
|
+
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
|
|
43717
|
+
}
|
|
43718
|
+
return shuffled;
|
|
43719
|
+
}
|
|
43720
|
+
|
|
44509
43721
|
exports.ACTION_NAMES = ACTION_NAMES;
|
|
44510
43722
|
exports.AIAgentView = AIAgentView_default;
|
|
44511
43723
|
exports.AdvancedFilterDialog = AdvancedFilterDialog;
|
|
@@ -44625,7 +43837,7 @@ exports.PrefetchStatus = PrefetchStatus;
|
|
|
44625
43837
|
exports.PrefetchTimeoutError = PrefetchTimeoutError;
|
|
44626
43838
|
exports.ProfileView = ProfileView_default;
|
|
44627
43839
|
exports.RegistryProvider = RegistryProvider;
|
|
44628
|
-
exports.S3ClipsService =
|
|
43840
|
+
exports.S3ClipsService = S3ClipsSupabaseService;
|
|
44629
43841
|
exports.S3Service = S3Service;
|
|
44630
43842
|
exports.SKUManagementView = SKUManagementView;
|
|
44631
43843
|
exports.SOPComplianceChart = SOPComplianceChart;
|