@optifye/dashboard-core 6.1.6 → 6.1.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.d.mts CHANGED
@@ -682,6 +682,7 @@ interface SimpleLine {
682
682
  factory_name: string;
683
683
  company_id: string;
684
684
  company_name: string;
685
+ enable: boolean;
685
686
  }
686
687
  interface WorkspaceMonthlyMetric {
687
688
  date: string;
package/dist/index.d.ts CHANGED
@@ -682,6 +682,7 @@ interface SimpleLine {
682
682
  factory_name: string;
683
683
  company_id: string;
684
684
  company_name: string;
685
+ enable: boolean;
685
686
  }
686
687
  interface WorkspaceMonthlyMetric {
687
688
  date: string;
package/dist/index.js CHANGED
@@ -948,8 +948,9 @@ var dashboardService = {
948
948
  factory_id,
949
949
  factories!lines_factory_id_fkey(factory_name),
950
950
  company_id,
951
- companies!lines_company_id_fkey(company_name:name)
952
- `);
951
+ companies!lines_company_id_fkey(company_name:name),
952
+ enable
953
+ `).eq("enable", true);
953
954
  if (companyId) {
954
955
  query = query.eq("company_id", companyId);
955
956
  }
@@ -965,7 +966,9 @@ var dashboardService = {
965
966
  factory_id: line.factory_id,
966
967
  factory_name: line.factories?.factory_name ?? "N/A",
967
968
  company_id: line.company_id,
968
- company_name: line.companies?.company_name ?? "N/A"
969
+ company_name: line.companies?.company_name ?? "N/A",
970
+ enable: line.enable ?? true
971
+ // Default to true if not specified
969
972
  }));
970
973
  return transformedLines;
971
974
  } catch (err) {
@@ -4057,7 +4060,14 @@ var useLeaderboardMetrics = (lineId, topCount = 10) => {
4057
4060
  try {
4058
4061
  const currentShiftDetails = getCurrentShift(defaultTimezone, shiftConfig);
4059
4062
  const operationalDate = getOperationalDate(defaultTimezone, new Date(currentShiftDetails.date));
4060
- const { data, error: fetchError } = await supabase.from(companySpecificMetricsTable).select(`workspace_name,total_output,avg_pph,efficiency,workspace_id`).eq("date", operationalDate).eq("shift_id", currentShiftDetails.shiftId).eq("line_id", lineId).gt("efficiency", 0).order("efficiency", { ascending: false }).limit(topCount);
4063
+ const workspaces = await workspaceService.getWorkspaces(lineId);
4064
+ const enabledWorkspaceIds = workspaces.filter((ws) => ws.enable === true).map((ws) => ws.id);
4065
+ if (enabledWorkspaceIds.length === 0) {
4066
+ setTopPerformers([]);
4067
+ setIsLoading(false);
4068
+ return;
4069
+ }
4070
+ const { data, error: fetchError } = await supabase.from(companySpecificMetricsTable).select(`workspace_name,total_output,avg_pph,efficiency,workspace_id`).eq("date", operationalDate).eq("shift_id", currentShiftDetails.shiftId).eq("line_id", lineId).in("workspace_id", enabledWorkspaceIds).gt("efficiency", 0).order("efficiency", { ascending: false }).limit(topCount);
4061
4071
  if (fetchError) throw fetchError;
4062
4072
  const rankedData = (data || []).map((entry, index) => ({
4063
4073
  workspace_name: entry.workspace_name,
@@ -4610,14 +4620,20 @@ var useRealtimeLineMetrics = ({
4610
4620
  const companyId = entityConfig.companyId;
4611
4621
  const metricsTablePrefix = getMetricsTablePrefix(companyId || "");
4612
4622
  const metricsTable = `${metricsTablePrefix}_${(companyId || "").replace(/-/g, "_")}`;
4613
- const { data: workspaceData, error: workspaceError } = await supabase.from(metricsTable).select(`
4614
- line_id,
4615
- workspace_id,
4616
- workspace_name,
4617
- efficiency,
4618
- total_output,
4619
- total_day_output
4620
- `).in("line_id", targetLineIds).eq("shift_id", shiftId).eq("date", date);
4623
+ let enabledWorkspaceIds = [];
4624
+ for (const lineId2 of targetLineIds) {
4625
+ const workspaces = await workspaceService.getWorkspaces(lineId2);
4626
+ const enabledIds = workspaces.filter((ws) => ws.enable === true).map((ws) => ws.id);
4627
+ enabledWorkspaceIds.push(...enabledIds);
4628
+ }
4629
+ const { data: workspaceData, error: workspaceError } = enabledWorkspaceIds.length > 0 ? await supabase.from(metricsTable).select(`
4630
+ line_id,
4631
+ workspace_id,
4632
+ workspace_name,
4633
+ efficiency,
4634
+ total_output,
4635
+ total_day_output
4636
+ `).in("line_id", targetLineIds).in("workspace_id", enabledWorkspaceIds).eq("shift_id", shiftId).eq("date", date) : { data: [], error: null };
4621
4637
  if (workspaceError) {
4622
4638
  console.error("Error fetching workspace metrics for factory view:", workspaceError);
4623
4639
  }
@@ -4669,13 +4685,15 @@ var useRealtimeLineMetrics = ({
4669
4685
  const companyId = entityConfig.companyId;
4670
4686
  const metricsTablePrefix = getMetricsTablePrefix(companyId || "");
4671
4687
  const metricsTable = `${metricsTablePrefix}_${(companyId || "").replace(/-/g, "_")}`;
4672
- const { data: workspaceData, error: workspaceError } = await supabase.from(metricsTable).select(`
4673
- workspace_id,
4674
- workspace_name,
4675
- efficiency,
4676
- total_output,
4677
- total_day_output
4678
- `).eq("line_id", lineIdRef.current).eq("shift_id", shiftId).eq("date", date);
4688
+ const workspaces = await workspaceService.getWorkspaces(lineIdRef.current);
4689
+ const enabledWorkspaceIds = workspaces.filter((ws) => ws.enable === true).map((ws) => ws.id);
4690
+ const { data: workspaceData, error: workspaceError } = enabledWorkspaceIds.length > 0 ? await supabase.from(metricsTable).select(`
4691
+ workspace_id,
4692
+ workspace_name,
4693
+ efficiency,
4694
+ total_output,
4695
+ total_day_output
4696
+ `).eq("line_id", lineIdRef.current).in("workspace_id", enabledWorkspaceIds).eq("shift_id", shiftId).eq("date", date) : { data: [], error: null };
4679
4697
  if (workspaceError) {
4680
4698
  console.error("Error fetching workspace metrics:", workspaceError);
4681
4699
  }
@@ -6124,6 +6142,19 @@ var useAllWorkspaceMetrics = (options) => {
6124
6142
  queryShiftId,
6125
6143
  metricsTable
6126
6144
  });
6145
+ const configuredLineIds = getConfiguredLineIds(entityConfig);
6146
+ let enabledWorkspaceIds = [];
6147
+ for (const lineId of configuredLineIds) {
6148
+ const workspaces2 = await workspaceService.getWorkspaces(lineId);
6149
+ const enabledIds = workspaces2.filter((ws) => ws.enable === true).map((ws) => ws.id);
6150
+ enabledWorkspaceIds.push(...enabledIds);
6151
+ }
6152
+ if (enabledWorkspaceIds.length === 0) {
6153
+ setWorkspaces([]);
6154
+ setInitialized(true);
6155
+ setLoading(false);
6156
+ return;
6157
+ }
6127
6158
  const { data, error: fetchError } = await supabase.from(metricsTable).select(`
6128
6159
  workspace_name,
6129
6160
  total_output,
@@ -6135,7 +6166,7 @@ var useAllWorkspaceMetrics = (options) => {
6135
6166
  trend_score,
6136
6167
  line_id,
6137
6168
  total_day_output
6138
- `).eq("date", queryDate).eq("shift_id", queryShiftId).order("efficiency", { ascending: false });
6169
+ `).eq("date", queryDate).eq("shift_id", queryShiftId).in("workspace_id", enabledWorkspaceIds).order("efficiency", { ascending: false });
6139
6170
  if (fetchError) throw fetchError;
6140
6171
  const transformedData = (data || []).map((item) => ({
6141
6172
  company_id: entityConfig.companyId || "unknown",
@@ -23064,7 +23095,14 @@ var S3ClipsService = class {
23064
23095
  }
23065
23096
  const limitPerCategory = limit ? Math.min(Math.max(limit, 1), this.maxLimitPerCategory) : this.defaultLimitPerCategory;
23066
23097
  const shouldFetchAll = category === "missing_quality_check" || category === "low_value";
23067
- const initialFetchLimit = shouldFetchAll ? void 0 : category ? Math.min(limitPerCategory * 3, this.maxInitialFetch) : void 0;
23098
+ let initialFetchLimit;
23099
+ if (shouldFetchAll) {
23100
+ initialFetchLimit = void 0;
23101
+ } else if (category) {
23102
+ initialFetchLimit = Math.min(limitPerCategory * 3, this.maxInitialFetch);
23103
+ } else {
23104
+ initialFetchLimit = this.maxInitialFetch;
23105
+ }
23068
23106
  const s3Uris = await this.listS3Clips({ workspaceId, date, shiftId, maxKeys: initialFetchLimit });
23069
23107
  if (s3Uris.length === 0) {
23070
23108
  console.log(`S3ClipsService: No HLS playlists found for workspace ${workspaceId} on date ${date}, shift ${shiftId}`);
@@ -23190,6 +23228,9 @@ var S3ClipsService = class {
23190
23228
  return videos;
23191
23229
  }
23192
23230
  };
23231
+
23232
+ // src/lib/cache/clipsCache.ts
23233
+ var clipsCache = {};
23193
23234
  var BottlenecksContent = ({
23194
23235
  workspaceId,
23195
23236
  workspaceName,
@@ -23209,6 +23250,7 @@ var BottlenecksContent = ({
23209
23250
  const videoRef = React19.useRef(null);
23210
23251
  const fullscreenContainerRef = React19.useRef(null);
23211
23252
  const timestampFilterRef = React19.useRef(null);
23253
+ const hlsRef = React19.useRef(null);
23212
23254
  const [isPlaying, setIsPlaying] = React19.useState(false);
23213
23255
  const [currentTime, setCurrentTime] = React19.useState(0);
23214
23256
  const [duration, setDuration] = React19.useState(0);
@@ -23257,21 +23299,36 @@ var BottlenecksContent = ({
23257
23299
  if (timestampEnd) {
23258
23300
  timestampEndFull = `${operationalDate}T${timestampEnd}:00`;
23259
23301
  }
23260
- const videos = await s3ClipsService.fetchClips({
23302
+ const cacheKey = `${workspaceId}-${operationalDate}-${shift || "all"}-all-meta-1000-${timestampStartFull || ""}-${timestampEndFull || ""}`;
23303
+ const existingEntry = clipsCache[cacheKey];
23304
+ if (existingEntry?.status === "resolved" && existingEntry.data) {
23305
+ setAllVideos(existingEntry.data);
23306
+ return;
23307
+ }
23308
+ if (existingEntry?.status === "pending" && existingEntry.promise) {
23309
+ setIsLoading(true);
23310
+ existingEntry.promise.then((data) => {
23311
+ setAllVideos(data);
23312
+ setIsLoading(false);
23313
+ }).catch(() => setIsLoading(false));
23314
+ return;
23315
+ }
23316
+ setIsLoading(true);
23317
+ const fetchPromise = s3ClipsService.fetchClips({
23261
23318
  workspaceId,
23262
23319
  date: operationalDate,
23263
23320
  shift: shift?.toString(),
23264
- // Pass the shift parameter
23265
23321
  mode: "full",
23266
23322
  includeCycleTime: true,
23267
23323
  includeMetadata: true,
23268
- // Always include metadata for timestamp info
23269
23324
  limit: 1e3,
23270
- // Reasonable limit for UI performance
23271
23325
  timestampStart: timestampStartFull,
23272
23326
  timestampEnd: timestampEndFull
23273
23327
  });
23274
- if (Array.isArray(videos) && videos.length > 0) {
23328
+ clipsCache[cacheKey] = { status: "pending", promise: fetchPromise };
23329
+ const videos = await fetchPromise;
23330
+ clipsCache[cacheKey] = { status: "resolved", data: videos };
23331
+ if (videos.length > 0) {
23275
23332
  preloadVideoUrl2(videos[0].src);
23276
23333
  const firstHigh = videos.find((v) => v.type === "bottleneck" && v.severity === "high");
23277
23334
  const firstMed = videos.find((v) => v.type === "bottleneck" && v.severity === "medium");
@@ -23295,6 +23352,8 @@ var BottlenecksContent = ({
23295
23352
  ].filter(Boolean));
23296
23353
  }
23297
23354
  setAllVideos(videos);
23355
+ setIsLoading(false);
23356
+ return;
23298
23357
  } catch (err) {
23299
23358
  console.error("Error fetching HLS clips from S3:", err);
23300
23359
  setError("Failed to load clips from storage. Please try again.");
@@ -23414,6 +23473,14 @@ var BottlenecksContent = ({
23414
23473
  } else {
23415
23474
  import('hls.js').then(({ default: Hls3 }) => {
23416
23475
  if (Hls3.isSupported()) {
23476
+ if (hlsRef.current) {
23477
+ try {
23478
+ hlsRef.current.destroy();
23479
+ } catch (err) {
23480
+ console.warn("Failed to destroy previous HLS instance", err);
23481
+ }
23482
+ hlsRef.current = null;
23483
+ }
23417
23484
  const hls = new Hls3({
23418
23485
  // Add CORS and error handling configuration
23419
23486
  xhrSetup: (xhr, url) => {
@@ -23456,7 +23523,8 @@ var BottlenecksContent = ({
23456
23523
  testBandwidth: false,
23457
23524
  // Error recovery configuration
23458
23525
  capLevelOnFPSDrop: false,
23459
- capLevelToPlayerSize: false
23526
+ capLevelToPlayerSize: true,
23527
+ abrEwmaDefaultEstimate: 5e5
23460
23528
  });
23461
23529
  hls.on(Hls3.Events.ERROR, (event, data) => {
23462
23530
  console.error("HLS.js error:", data);
@@ -23584,6 +23652,13 @@ var BottlenecksContent = ({
23584
23652
  video.removeEventListener("loadedmetadata", onLoadedMetadata);
23585
23653
  video.removeEventListener("ended", onEnded);
23586
23654
  video.removeEventListener("error", onError);
23655
+ if (hlsRef.current) {
23656
+ try {
23657
+ hlsRef.current.destroy();
23658
+ } catch (e) {
23659
+ }
23660
+ hlsRef.current = null;
23661
+ }
23587
23662
  };
23588
23663
  } else if (videoRef.current) {
23589
23664
  videoRef.current.pause();
@@ -30243,15 +30318,22 @@ var LeaderboardDetailView = React19.memo(({
30243
30318
  className = ""
30244
30319
  }) => {
30245
30320
  const navigation = useNavigation();
30321
+ const entityConfig = useEntityConfig();
30246
30322
  const [sortAscending, setSortAscending] = React19.useState(false);
30323
+ const configuredLineNames = React19.useMemo(() => {
30324
+ return getAllLineDisplayNames(entityConfig);
30325
+ }, [entityConfig]);
30247
30326
  const getLineName = React19.useCallback((lineId2) => {
30327
+ if (configuredLineNames[lineId2]) {
30328
+ return configuredLineNames[lineId2];
30329
+ }
30248
30330
  if (lineNames[lineId2]) {
30249
30331
  return lineNames[lineId2];
30250
30332
  }
30251
30333
  if (lineId2 === line1Id) return "Line 1";
30252
30334
  if (lineId2 === line2Id) return "Line 2";
30253
- return lineId2;
30254
- }, [lineNames, line1Id, line2Id]);
30335
+ return `Line ${lineId2.substring(0, 8)}`;
30336
+ }, [configuredLineNames, lineNames, line1Id, line2Id]);
30255
30337
  const handleSortToggle = React19.useCallback(() => {
30256
30338
  setSortAscending(!sortAscending);
30257
30339
  }, [sortAscending]);
@@ -33385,6 +33467,29 @@ var WorkspaceDetailView = ({
33385
33467
  const isHistoricView = Boolean(date && parsedShiftId !== void 0);
33386
33468
  const initialTab = getInitialTab(sourceType, defaultTab, fromMonthly, date);
33387
33469
  const [activeTab, setActiveTab] = React19.useState(initialTab);
33470
+ const dashboardConfig = useDashboardConfig();
33471
+ React19.useEffect(() => {
33472
+ if (!dashboardConfig?.s3Config) return;
33473
+ const operationalDate2 = date || getOperationalDate();
33474
+ const cacheKey = `${workspaceId}-${operationalDate2}-${shift || "all"}-all-meta-1000--`;
33475
+ if (clipsCache[cacheKey]?.status) return;
33476
+ const service = new S3ClipsService(dashboardConfig);
33477
+ const promise = service.fetchClips({
33478
+ workspaceId,
33479
+ date: operationalDate2,
33480
+ shift: shift?.toString(),
33481
+ mode: "full",
33482
+ includeCycleTime: true,
33483
+ includeMetadata: true,
33484
+ limit: 1e3
33485
+ });
33486
+ clipsCache[cacheKey] = { status: "pending", promise };
33487
+ promise.then((videos) => {
33488
+ clipsCache[cacheKey] = { status: "resolved", data: videos };
33489
+ }).catch((err) => {
33490
+ clipsCache[cacheKey] = { status: "rejected", error: err };
33491
+ });
33492
+ }, [workspaceId, date, shift, dashboardConfig]);
33388
33493
  const [isTransitioning, setIsTransitioning] = React19.useState(false);
33389
33494
  const [usingFallbackData, setUsingFallbackData] = React19.useState(false);
33390
33495
  const [showIdleTime, setShowIdleTime] = React19.useState(false);
package/dist/index.mjs CHANGED
@@ -919,8 +919,9 @@ var dashboardService = {
919
919
  factory_id,
920
920
  factories!lines_factory_id_fkey(factory_name),
921
921
  company_id,
922
- companies!lines_company_id_fkey(company_name:name)
923
- `);
922
+ companies!lines_company_id_fkey(company_name:name),
923
+ enable
924
+ `).eq("enable", true);
924
925
  if (companyId) {
925
926
  query = query.eq("company_id", companyId);
926
927
  }
@@ -936,7 +937,9 @@ var dashboardService = {
936
937
  factory_id: line.factory_id,
937
938
  factory_name: line.factories?.factory_name ?? "N/A",
938
939
  company_id: line.company_id,
939
- company_name: line.companies?.company_name ?? "N/A"
940
+ company_name: line.companies?.company_name ?? "N/A",
941
+ enable: line.enable ?? true
942
+ // Default to true if not specified
940
943
  }));
941
944
  return transformedLines;
942
945
  } catch (err) {
@@ -4028,7 +4031,14 @@ var useLeaderboardMetrics = (lineId, topCount = 10) => {
4028
4031
  try {
4029
4032
  const currentShiftDetails = getCurrentShift(defaultTimezone, shiftConfig);
4030
4033
  const operationalDate = getOperationalDate(defaultTimezone, new Date(currentShiftDetails.date));
4031
- const { data, error: fetchError } = await supabase.from(companySpecificMetricsTable).select(`workspace_name,total_output,avg_pph,efficiency,workspace_id`).eq("date", operationalDate).eq("shift_id", currentShiftDetails.shiftId).eq("line_id", lineId).gt("efficiency", 0).order("efficiency", { ascending: false }).limit(topCount);
4034
+ const workspaces = await workspaceService.getWorkspaces(lineId);
4035
+ const enabledWorkspaceIds = workspaces.filter((ws) => ws.enable === true).map((ws) => ws.id);
4036
+ if (enabledWorkspaceIds.length === 0) {
4037
+ setTopPerformers([]);
4038
+ setIsLoading(false);
4039
+ return;
4040
+ }
4041
+ const { data, error: fetchError } = await supabase.from(companySpecificMetricsTable).select(`workspace_name,total_output,avg_pph,efficiency,workspace_id`).eq("date", operationalDate).eq("shift_id", currentShiftDetails.shiftId).eq("line_id", lineId).in("workspace_id", enabledWorkspaceIds).gt("efficiency", 0).order("efficiency", { ascending: false }).limit(topCount);
4032
4042
  if (fetchError) throw fetchError;
4033
4043
  const rankedData = (data || []).map((entry, index) => ({
4034
4044
  workspace_name: entry.workspace_name,
@@ -4581,14 +4591,20 @@ var useRealtimeLineMetrics = ({
4581
4591
  const companyId = entityConfig.companyId;
4582
4592
  const metricsTablePrefix = getMetricsTablePrefix(companyId || "");
4583
4593
  const metricsTable = `${metricsTablePrefix}_${(companyId || "").replace(/-/g, "_")}`;
4584
- const { data: workspaceData, error: workspaceError } = await supabase.from(metricsTable).select(`
4585
- line_id,
4586
- workspace_id,
4587
- workspace_name,
4588
- efficiency,
4589
- total_output,
4590
- total_day_output
4591
- `).in("line_id", targetLineIds).eq("shift_id", shiftId).eq("date", date);
4594
+ let enabledWorkspaceIds = [];
4595
+ for (const lineId2 of targetLineIds) {
4596
+ const workspaces = await workspaceService.getWorkspaces(lineId2);
4597
+ const enabledIds = workspaces.filter((ws) => ws.enable === true).map((ws) => ws.id);
4598
+ enabledWorkspaceIds.push(...enabledIds);
4599
+ }
4600
+ const { data: workspaceData, error: workspaceError } = enabledWorkspaceIds.length > 0 ? await supabase.from(metricsTable).select(`
4601
+ line_id,
4602
+ workspace_id,
4603
+ workspace_name,
4604
+ efficiency,
4605
+ total_output,
4606
+ total_day_output
4607
+ `).in("line_id", targetLineIds).in("workspace_id", enabledWorkspaceIds).eq("shift_id", shiftId).eq("date", date) : { data: [], error: null };
4592
4608
  if (workspaceError) {
4593
4609
  console.error("Error fetching workspace metrics for factory view:", workspaceError);
4594
4610
  }
@@ -4640,13 +4656,15 @@ var useRealtimeLineMetrics = ({
4640
4656
  const companyId = entityConfig.companyId;
4641
4657
  const metricsTablePrefix = getMetricsTablePrefix(companyId || "");
4642
4658
  const metricsTable = `${metricsTablePrefix}_${(companyId || "").replace(/-/g, "_")}`;
4643
- const { data: workspaceData, error: workspaceError } = await supabase.from(metricsTable).select(`
4644
- workspace_id,
4645
- workspace_name,
4646
- efficiency,
4647
- total_output,
4648
- total_day_output
4649
- `).eq("line_id", lineIdRef.current).eq("shift_id", shiftId).eq("date", date);
4659
+ const workspaces = await workspaceService.getWorkspaces(lineIdRef.current);
4660
+ const enabledWorkspaceIds = workspaces.filter((ws) => ws.enable === true).map((ws) => ws.id);
4661
+ const { data: workspaceData, error: workspaceError } = enabledWorkspaceIds.length > 0 ? await supabase.from(metricsTable).select(`
4662
+ workspace_id,
4663
+ workspace_name,
4664
+ efficiency,
4665
+ total_output,
4666
+ total_day_output
4667
+ `).eq("line_id", lineIdRef.current).in("workspace_id", enabledWorkspaceIds).eq("shift_id", shiftId).eq("date", date) : { data: [], error: null };
4650
4668
  if (workspaceError) {
4651
4669
  console.error("Error fetching workspace metrics:", workspaceError);
4652
4670
  }
@@ -6095,6 +6113,19 @@ var useAllWorkspaceMetrics = (options) => {
6095
6113
  queryShiftId,
6096
6114
  metricsTable
6097
6115
  });
6116
+ const configuredLineIds = getConfiguredLineIds(entityConfig);
6117
+ let enabledWorkspaceIds = [];
6118
+ for (const lineId of configuredLineIds) {
6119
+ const workspaces2 = await workspaceService.getWorkspaces(lineId);
6120
+ const enabledIds = workspaces2.filter((ws) => ws.enable === true).map((ws) => ws.id);
6121
+ enabledWorkspaceIds.push(...enabledIds);
6122
+ }
6123
+ if (enabledWorkspaceIds.length === 0) {
6124
+ setWorkspaces([]);
6125
+ setInitialized(true);
6126
+ setLoading(false);
6127
+ return;
6128
+ }
6098
6129
  const { data, error: fetchError } = await supabase.from(metricsTable).select(`
6099
6130
  workspace_name,
6100
6131
  total_output,
@@ -6106,7 +6137,7 @@ var useAllWorkspaceMetrics = (options) => {
6106
6137
  trend_score,
6107
6138
  line_id,
6108
6139
  total_day_output
6109
- `).eq("date", queryDate).eq("shift_id", queryShiftId).order("efficiency", { ascending: false });
6140
+ `).eq("date", queryDate).eq("shift_id", queryShiftId).in("workspace_id", enabledWorkspaceIds).order("efficiency", { ascending: false });
6110
6141
  if (fetchError) throw fetchError;
6111
6142
  const transformedData = (data || []).map((item) => ({
6112
6143
  company_id: entityConfig.companyId || "unknown",
@@ -23035,7 +23066,14 @@ var S3ClipsService = class {
23035
23066
  }
23036
23067
  const limitPerCategory = limit ? Math.min(Math.max(limit, 1), this.maxLimitPerCategory) : this.defaultLimitPerCategory;
23037
23068
  const shouldFetchAll = category === "missing_quality_check" || category === "low_value";
23038
- const initialFetchLimit = shouldFetchAll ? void 0 : category ? Math.min(limitPerCategory * 3, this.maxInitialFetch) : void 0;
23069
+ let initialFetchLimit;
23070
+ if (shouldFetchAll) {
23071
+ initialFetchLimit = void 0;
23072
+ } else if (category) {
23073
+ initialFetchLimit = Math.min(limitPerCategory * 3, this.maxInitialFetch);
23074
+ } else {
23075
+ initialFetchLimit = this.maxInitialFetch;
23076
+ }
23039
23077
  const s3Uris = await this.listS3Clips({ workspaceId, date, shiftId, maxKeys: initialFetchLimit });
23040
23078
  if (s3Uris.length === 0) {
23041
23079
  console.log(`S3ClipsService: No HLS playlists found for workspace ${workspaceId} on date ${date}, shift ${shiftId}`);
@@ -23161,6 +23199,9 @@ var S3ClipsService = class {
23161
23199
  return videos;
23162
23200
  }
23163
23201
  };
23202
+
23203
+ // src/lib/cache/clipsCache.ts
23204
+ var clipsCache = {};
23164
23205
  var BottlenecksContent = ({
23165
23206
  workspaceId,
23166
23207
  workspaceName,
@@ -23180,6 +23221,7 @@ var BottlenecksContent = ({
23180
23221
  const videoRef = useRef(null);
23181
23222
  const fullscreenContainerRef = useRef(null);
23182
23223
  const timestampFilterRef = useRef(null);
23224
+ const hlsRef = useRef(null);
23183
23225
  const [isPlaying, setIsPlaying] = useState(false);
23184
23226
  const [currentTime, setCurrentTime] = useState(0);
23185
23227
  const [duration, setDuration] = useState(0);
@@ -23228,21 +23270,36 @@ var BottlenecksContent = ({
23228
23270
  if (timestampEnd) {
23229
23271
  timestampEndFull = `${operationalDate}T${timestampEnd}:00`;
23230
23272
  }
23231
- const videos = await s3ClipsService.fetchClips({
23273
+ const cacheKey = `${workspaceId}-${operationalDate}-${shift || "all"}-all-meta-1000-${timestampStartFull || ""}-${timestampEndFull || ""}`;
23274
+ const existingEntry = clipsCache[cacheKey];
23275
+ if (existingEntry?.status === "resolved" && existingEntry.data) {
23276
+ setAllVideos(existingEntry.data);
23277
+ return;
23278
+ }
23279
+ if (existingEntry?.status === "pending" && existingEntry.promise) {
23280
+ setIsLoading(true);
23281
+ existingEntry.promise.then((data) => {
23282
+ setAllVideos(data);
23283
+ setIsLoading(false);
23284
+ }).catch(() => setIsLoading(false));
23285
+ return;
23286
+ }
23287
+ setIsLoading(true);
23288
+ const fetchPromise = s3ClipsService.fetchClips({
23232
23289
  workspaceId,
23233
23290
  date: operationalDate,
23234
23291
  shift: shift?.toString(),
23235
- // Pass the shift parameter
23236
23292
  mode: "full",
23237
23293
  includeCycleTime: true,
23238
23294
  includeMetadata: true,
23239
- // Always include metadata for timestamp info
23240
23295
  limit: 1e3,
23241
- // Reasonable limit for UI performance
23242
23296
  timestampStart: timestampStartFull,
23243
23297
  timestampEnd: timestampEndFull
23244
23298
  });
23245
- if (Array.isArray(videos) && videos.length > 0) {
23299
+ clipsCache[cacheKey] = { status: "pending", promise: fetchPromise };
23300
+ const videos = await fetchPromise;
23301
+ clipsCache[cacheKey] = { status: "resolved", data: videos };
23302
+ if (videos.length > 0) {
23246
23303
  preloadVideoUrl2(videos[0].src);
23247
23304
  const firstHigh = videos.find((v) => v.type === "bottleneck" && v.severity === "high");
23248
23305
  const firstMed = videos.find((v) => v.type === "bottleneck" && v.severity === "medium");
@@ -23266,6 +23323,8 @@ var BottlenecksContent = ({
23266
23323
  ].filter(Boolean));
23267
23324
  }
23268
23325
  setAllVideos(videos);
23326
+ setIsLoading(false);
23327
+ return;
23269
23328
  } catch (err) {
23270
23329
  console.error("Error fetching HLS clips from S3:", err);
23271
23330
  setError("Failed to load clips from storage. Please try again.");
@@ -23385,6 +23444,14 @@ var BottlenecksContent = ({
23385
23444
  } else {
23386
23445
  import('hls.js').then(({ default: Hls3 }) => {
23387
23446
  if (Hls3.isSupported()) {
23447
+ if (hlsRef.current) {
23448
+ try {
23449
+ hlsRef.current.destroy();
23450
+ } catch (err) {
23451
+ console.warn("Failed to destroy previous HLS instance", err);
23452
+ }
23453
+ hlsRef.current = null;
23454
+ }
23388
23455
  const hls = new Hls3({
23389
23456
  // Add CORS and error handling configuration
23390
23457
  xhrSetup: (xhr, url) => {
@@ -23427,7 +23494,8 @@ var BottlenecksContent = ({
23427
23494
  testBandwidth: false,
23428
23495
  // Error recovery configuration
23429
23496
  capLevelOnFPSDrop: false,
23430
- capLevelToPlayerSize: false
23497
+ capLevelToPlayerSize: true,
23498
+ abrEwmaDefaultEstimate: 5e5
23431
23499
  });
23432
23500
  hls.on(Hls3.Events.ERROR, (event, data) => {
23433
23501
  console.error("HLS.js error:", data);
@@ -23555,6 +23623,13 @@ var BottlenecksContent = ({
23555
23623
  video.removeEventListener("loadedmetadata", onLoadedMetadata);
23556
23624
  video.removeEventListener("ended", onEnded);
23557
23625
  video.removeEventListener("error", onError);
23626
+ if (hlsRef.current) {
23627
+ try {
23628
+ hlsRef.current.destroy();
23629
+ } catch (e) {
23630
+ }
23631
+ hlsRef.current = null;
23632
+ }
23558
23633
  };
23559
23634
  } else if (videoRef.current) {
23560
23635
  videoRef.current.pause();
@@ -30214,15 +30289,22 @@ var LeaderboardDetailView = memo(({
30214
30289
  className = ""
30215
30290
  }) => {
30216
30291
  const navigation = useNavigation();
30292
+ const entityConfig = useEntityConfig();
30217
30293
  const [sortAscending, setSortAscending] = useState(false);
30294
+ const configuredLineNames = useMemo(() => {
30295
+ return getAllLineDisplayNames(entityConfig);
30296
+ }, [entityConfig]);
30218
30297
  const getLineName = useCallback((lineId2) => {
30298
+ if (configuredLineNames[lineId2]) {
30299
+ return configuredLineNames[lineId2];
30300
+ }
30219
30301
  if (lineNames[lineId2]) {
30220
30302
  return lineNames[lineId2];
30221
30303
  }
30222
30304
  if (lineId2 === line1Id) return "Line 1";
30223
30305
  if (lineId2 === line2Id) return "Line 2";
30224
- return lineId2;
30225
- }, [lineNames, line1Id, line2Id]);
30306
+ return `Line ${lineId2.substring(0, 8)}`;
30307
+ }, [configuredLineNames, lineNames, line1Id, line2Id]);
30226
30308
  const handleSortToggle = useCallback(() => {
30227
30309
  setSortAscending(!sortAscending);
30228
30310
  }, [sortAscending]);
@@ -33356,6 +33438,29 @@ var WorkspaceDetailView = ({
33356
33438
  const isHistoricView = Boolean(date && parsedShiftId !== void 0);
33357
33439
  const initialTab = getInitialTab(sourceType, defaultTab, fromMonthly, date);
33358
33440
  const [activeTab, setActiveTab] = useState(initialTab);
33441
+ const dashboardConfig = useDashboardConfig();
33442
+ useEffect(() => {
33443
+ if (!dashboardConfig?.s3Config) return;
33444
+ const operationalDate2 = date || getOperationalDate();
33445
+ const cacheKey = `${workspaceId}-${operationalDate2}-${shift || "all"}-all-meta-1000--`;
33446
+ if (clipsCache[cacheKey]?.status) return;
33447
+ const service = new S3ClipsService(dashboardConfig);
33448
+ const promise = service.fetchClips({
33449
+ workspaceId,
33450
+ date: operationalDate2,
33451
+ shift: shift?.toString(),
33452
+ mode: "full",
33453
+ includeCycleTime: true,
33454
+ includeMetadata: true,
33455
+ limit: 1e3
33456
+ });
33457
+ clipsCache[cacheKey] = { status: "pending", promise };
33458
+ promise.then((videos) => {
33459
+ clipsCache[cacheKey] = { status: "resolved", data: videos };
33460
+ }).catch((err) => {
33461
+ clipsCache[cacheKey] = { status: "rejected", error: err };
33462
+ });
33463
+ }, [workspaceId, date, shift, dashboardConfig]);
33359
33464
  const [isTransitioning, setIsTransitioning] = useState(false);
33360
33465
  const [usingFallbackData, setUsingFallbackData] = useState(false);
33361
33466
  const [showIdleTime, setShowIdleTime] = useState(false);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optifye/dashboard-core",
3
- "version": "6.1.6",
3
+ "version": "6.1.8",
4
4
  "description": "Reusable UI & logic for Optifye dashboard",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",