@optifye/dashboard-core 6.6.6 → 6.6.8

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