@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.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 getAuthToken2();
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
- var S3ClipsService2 = S3ClipsSupabaseService ;
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 S3ClipsService2(dashboardConfig));
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
- const prefetchCacheRef = React20.useRef({});
21910
+ React20.useRef({});
22839
21911
  const handleWorkspaceClick = React20.useCallback((workspace) => {
22840
21912
  const workspaceId = workspace.workspace_uuid || workspace.workspace_name;
22841
- if (dashboardConfig?.s3Config && workspaceId) {
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 getSupabaseClient3 = () => {
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 getAuthToken3 = async () => {
27978
+ var getAuthToken2 = async () => {
28929
27979
  try {
28930
- const supabase = getSupabaseClient3();
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 getAuthToken3();
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 getAuthToken4()}`
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 getAuthToken4 = async () => {
28187
+ const getAuthToken3 = async () => {
29138
28188
  try {
29139
- const { createClient: createClient5 } = await import('@supabase/supabase-js');
29140
- const supabase = createClient5(
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 getAuthToken4()}`
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: USE_SUPABASE_CLIPS2,
29966
- // Only enable for Supabase implementation
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 S3ClipsService3(dashboardConfig);
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: createClient5 } = await import('@supabase/supabase-js');
30271
- const supabase = createClient5(
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 = S3ClipsService;
43840
+ exports.S3ClipsService = S3ClipsSupabaseService;
44629
43841
  exports.S3Service = S3Service;
44630
43842
  exports.SKUManagementView = SKUManagementView;
44631
43843
  exports.SOPComplianceChart = SOPComplianceChart;