@optifye/dashboard-core 6.10.30 → 6.10.31

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
@@ -10,7 +10,7 @@ import { createClient, REALTIME_SUBSCRIBE_STATES } from '@supabase/supabase-js';
10
10
  import Hls, { Events, ErrorTypes } from 'hls.js';
11
11
  import useSWR from 'swr';
12
12
  import { memo, noop, warning, invariant, progress, secondsToMilliseconds, millisecondsToSeconds } from 'motion-utils';
13
- import { Camera, ChevronDown, ChevronUp, Check, Map as Map$1, Video, ShieldCheck, Star, Award, Filter, X, Coffee, Plus, ArrowLeft, Clock, Calendar, Save, AlertCircle, Loader2, Minus, ArrowDown, ArrowUp, ChevronLeft, ChevronRight, TrendingUp, Sparkles, Pause, Play, Wrench, XCircle, Package, UserX, Zap, HelpCircle, AlertTriangle, Tag, Palette, CheckCircle2, RefreshCw, TrendingDown, FolderOpen, Folder, Sliders, Activity, Layers, Search, Edit2, ArrowRight, CheckCircle, User, Users, Shield, Building2, Mail, Lock, Info, Share2, Trophy, Target, Download, Sun, Moon, MousePointer, UserPlus, UserCog, Trash2, Eye, MoreVertical, BarChart3, Pencil, UserCheck, LogOut, MessageSquare, Menu, Send, Copy, Settings, LifeBuoy, EyeOff, UserCircle, Flame, Crown, Medal } from 'lucide-react';
13
+ import { Camera, AlertTriangle, ChevronDown, ChevronUp, Check, Map as Map$1, Video, ShieldCheck, Star, Award, Filter, X, Coffee, Plus, ArrowLeft, Clock, Calendar, Save, AlertCircle, Loader2, Minus, ArrowDown, ArrowUp, ChevronLeft, ChevronRight, TrendingUp, Sparkles, Pause, Play, Wrench, XCircle, Package, UserX, Zap, HelpCircle, Tag, Palette, CheckCircle2, RefreshCw, TrendingDown, FolderOpen, Folder, Sliders, Activity, Layers, Search, Edit2, ArrowRight, CheckCircle, User, Users, Shield, Building2, Mail, Lock, Info, Share2, Trophy, Target, Download, Sun, Moon, MousePointer, UserPlus, UserCog, Trash2, Eye, MoreVertical, BarChart3, Pencil, UserCheck, LogOut, MessageSquare, Menu, Send, Copy, Settings, LifeBuoy, EyeOff, UserCircle, Flame, Crown, Medal } from 'lucide-react';
14
14
  import { toast } from 'sonner';
15
15
  import { BarChart as BarChart$1, CartesianGrid, XAxis, YAxis, ReferenceLine, Tooltip, Legend, Bar, LabelList, ResponsiveContainer, LineChart as LineChart$1, Line, PieChart, Pie, Cell, ComposedChart, Area, ScatterChart, Scatter } from 'recharts';
16
16
  import { Slot } from '@radix-ui/react-slot';
@@ -2540,6 +2540,7 @@ var workspaceService = {
2540
2540
  }
2541
2541
  }
2542
2542
  };
2543
+ var DATA_PROCESSING_DELAY_MINUTES = 5;
2543
2544
  var WorkspaceHealthService = class _WorkspaceHealthService {
2544
2545
  constructor() {
2545
2546
  this.cache = /* @__PURE__ */ new Map();
@@ -2595,9 +2596,11 @@ var WorkspaceHealthService = class _WorkspaceHealthService {
2595
2596
  const totalMinutes = getShiftDurationMinutes(shiftStartStr, shiftEndStr);
2596
2597
  const shiftEndDate = addMinutes(shiftStartDate, totalMinutes);
2597
2598
  const now4 = /* @__PURE__ */ new Date();
2598
- let completedMinutes = differenceInMinutes(now4 < shiftEndDate ? now4 : shiftEndDate, shiftStartDate);
2599
- if (completedMinutes < 0) completedMinutes = 0;
2600
- if (completedMinutes > totalMinutes) completedMinutes = totalMinutes;
2599
+ let rawCompletedMinutes = differenceInMinutes(now4 < shiftEndDate ? now4 : shiftEndDate, shiftStartDate);
2600
+ if (rawCompletedMinutes < 0) rawCompletedMinutes = 0;
2601
+ if (rawCompletedMinutes > totalMinutes) rawCompletedMinutes = totalMinutes;
2602
+ const isShiftComplete = shiftEndDate <= now4;
2603
+ const completedMinutes = isShiftComplete ? rawCompletedMinutes : Math.max(0, rawCompletedMinutes - DATA_PROCESSING_DELAY_MINUTES);
2601
2604
  const pendingMinutes = Math.max(0, totalMinutes - completedMinutes);
2602
2605
  return {
2603
2606
  shiftId,
@@ -2632,7 +2635,8 @@ var WorkspaceHealthService = class _WorkspaceHealthService {
2632
2635
  } else if (shiftStartDate > now4) {
2633
2636
  completedMinutes = 0;
2634
2637
  } else {
2635
- completedMinutes = Math.floor((now4.getTime() - shiftStartDate.getTime()) / (1e3 * 60));
2638
+ const rawCompleted = Math.floor((now4.getTime() - shiftStartDate.getTime()) / (1e3 * 60));
2639
+ completedMinutes = Math.max(0, rawCompleted - DATA_PROCESSING_DELAY_MINUTES);
2636
2640
  }
2637
2641
  const pendingMinutes = totalMinutes - completedMinutes;
2638
2642
  return {
@@ -11658,6 +11662,8 @@ var FAILURE_EXPIRY_MS = 5 * 60 * 1e3;
11658
11662
  var LIVE_RELOAD_MIN_INTERVAL_MS = 15 * 1e3;
11659
11663
  var DEFAULT_LIVE_OFFSET_SECONDS = 120;
11660
11664
  var DEFAULT_MAX_MANIFEST_AGE_MS = 10 * 60 * 1e3;
11665
+ var SEGMENT_MAX_AGE_MS = 10 * 60 * 1e3;
11666
+ var SEGMENT_TIMESTAMP_REGEX = /(\d{8}T\d{6}Z)(?=\.ts(?:$|[?#]))/;
11661
11667
  var STALE_MANIFEST_POLL_INITIAL_DELAY_MS = 15 * 1e3;
11662
11668
  var STALE_MANIFEST_POLL_MAX_DELAY_MS = 60 * 1e3;
11663
11669
  function resetFailedUrl(url) {
@@ -11693,6 +11699,8 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
11693
11699
  });
11694
11700
  };
11695
11701
  const [restartKey, setRestartKey] = useState(0);
11702
+ const [isStale, setIsStale] = useState(false);
11703
+ const [staleReason, setStaleReason] = useState(null);
11696
11704
  const hlsRef = useRef(null);
11697
11705
  const stallCheckIntervalRef = useRef(null);
11698
11706
  const noProgressTimerRef = useRef(null);
@@ -11704,6 +11712,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
11704
11712
  const playRetryTimerRef = useRef(null);
11705
11713
  const playRetryCountRef = useRef(0);
11706
11714
  const manifestWatchdogRef = useRef(null);
11715
+ const nativeFreshnessIntervalRef = useRef(null);
11707
11716
  const lastHiddenAtRef = useRef(null);
11708
11717
  const manifestRetryTimerRef = useRef(null);
11709
11718
  const manifestRetryDelayRef = useRef(5e3);
@@ -11728,6 +11737,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
11728
11737
  const staleManifestEndSnRef = useRef(null);
11729
11738
  const staleManifestPollTimerRef = useRef(null);
11730
11739
  const staleManifestPollDelayRef = useRef(STALE_MANIFEST_POLL_INITIAL_DELAY_MS);
11740
+ const lastSegmentTimestampMsRef = useRef(null);
11731
11741
  const authTokenRef = useRef(null);
11732
11742
  const proxyEnabled = process.env.NEXT_PUBLIC_HLS_PROXY_ENABLED === "true";
11733
11743
  const proxyBaseUrl = (process.env.NEXT_PUBLIC_HLS_PROXY_URL || "/api/stream").replace(/\/$/, "");
@@ -11737,6 +11747,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
11737
11747
  const parsed = raw ? Number(raw) : NaN;
11738
11748
  return Number.isFinite(parsed) ? parsed : DEFAULT_MAX_MANIFEST_AGE_MS;
11739
11749
  })();
11750
+ const manifestStaleThresholdMs = maxManifestAgeMs > 0 ? Math.min(maxManifestAgeMs, SEGMENT_MAX_AGE_MS) : SEGMENT_MAX_AGE_MS;
11740
11751
  const debugLog = (...args) => {
11741
11752
  if (debugEnabled) {
11742
11753
  console.log(...args);
@@ -11751,10 +11762,49 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
11751
11762
  }
11752
11763
  return null;
11753
11764
  };
11765
+ const parseSegmentTimestampMs = (value) => {
11766
+ const match = value.match(SEGMENT_TIMESTAMP_REGEX);
11767
+ if (!match) return null;
11768
+ const stamp = match[1];
11769
+ const year = Number(stamp.slice(0, 4));
11770
+ const month = Number(stamp.slice(4, 6));
11771
+ const day = Number(stamp.slice(6, 8));
11772
+ const hour = Number(stamp.slice(9, 11));
11773
+ const minute = Number(stamp.slice(11, 13));
11774
+ const second = Number(stamp.slice(13, 15));
11775
+ if (!Number.isFinite(year) || !Number.isFinite(month) || !Number.isFinite(day) || !Number.isFinite(hour) || !Number.isFinite(minute) || !Number.isFinite(second) || month < 1 || month > 12 || day < 1 || day > 31 || hour < 0 || hour > 23 || minute < 0 || minute > 59 || second < 0 || second > 59) {
11776
+ return null;
11777
+ }
11778
+ const timestampMs = Date.UTC(year, month - 1, day, hour, minute, second);
11779
+ return Number.isNaN(timestampMs) ? null : timestampMs;
11780
+ };
11781
+ const getFragmentTimestampMs = (fragment, enforceSegmentAge = false) => {
11782
+ if (fragment?.relurl) {
11783
+ const parsed = parseSegmentTimestampMs(fragment.relurl);
11784
+ if (parsed !== null) return parsed;
11785
+ }
11786
+ if (fragment?.url) {
11787
+ const parsed = parseSegmentTimestampMs(fragment.url);
11788
+ if (parsed !== null) return parsed;
11789
+ }
11790
+ if (enforceSegmentAge) return null;
11791
+ return getProgramDateTimeMs(fragment?.programDateTime);
11792
+ };
11793
+ const getLatestFragmentTimestampMs = (fragments, enforceSegmentAge = false) => {
11794
+ if (!Array.isArray(fragments) || fragments.length === 0) return null;
11795
+ for (let i = fragments.length - 1; i >= 0; i -= 1) {
11796
+ const timestampMs = getFragmentTimestampMs(fragments[i], enforceSegmentAge);
11797
+ if (timestampMs !== null) {
11798
+ return timestampMs;
11799
+ }
11800
+ }
11801
+ return null;
11802
+ };
11754
11803
  const parseManifestStatus = (manifestText) => {
11755
11804
  let mediaSequence = null;
11756
11805
  let segmentCount = 0;
11757
11806
  let lastProgramDateTimeMs = null;
11807
+ let lastSegmentTimestampMs = null;
11758
11808
  const lines = manifestText.split(/\r?\n/);
11759
11809
  for (const rawLine of lines) {
11760
11810
  const line = rawLine.trim();
@@ -11772,13 +11822,60 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
11772
11822
  }
11773
11823
  } else if (line.startsWith("#EXTINF:")) {
11774
11824
  segmentCount += 1;
11825
+ } else if (!line.startsWith("#")) {
11826
+ const segmentTimestampMs = parseSegmentTimestampMs(line);
11827
+ if (segmentTimestampMs !== null) {
11828
+ lastSegmentTimestampMs = segmentTimestampMs;
11829
+ }
11775
11830
  }
11776
11831
  }
11777
11832
  let lastSequenceNumber = null;
11778
11833
  if (mediaSequence !== null && segmentCount > 0) {
11779
11834
  lastSequenceNumber = mediaSequence + segmentCount - 1;
11780
11835
  }
11781
- return { lastProgramDateTimeMs, lastSequenceNumber };
11836
+ return { lastProgramDateTimeMs, lastSequenceNumber, lastSegmentTimestampMs };
11837
+ };
11838
+ const evaluateSegmentFreshness = ({
11839
+ segmentTimestampMs,
11840
+ fallbackTimestampMs,
11841
+ enforceSegmentAge
11842
+ }) => {
11843
+ if (segmentTimestampMs !== null) {
11844
+ const ageMs = Date.now() - segmentTimestampMs;
11845
+ return {
11846
+ isFresh: ageMs <= SEGMENT_MAX_AGE_MS,
11847
+ ageMs,
11848
+ reason: `segment age ${Math.round(ageMs / 1e3)}s`
11849
+ };
11850
+ }
11851
+ if (enforceSegmentAge) {
11852
+ return { isFresh: false, ageMs: null, reason: "segment timestamp missing" };
11853
+ }
11854
+ if (fallbackTimestampMs !== null) {
11855
+ const ageMs = Date.now() - fallbackTimestampMs;
11856
+ return {
11857
+ isFresh: ageMs <= SEGMENT_MAX_AGE_MS,
11858
+ ageMs,
11859
+ reason: `program date time age ${Math.round(ageMs / 1e3)}s`
11860
+ };
11861
+ }
11862
+ return { isFresh: true, ageMs: null, reason: null };
11863
+ };
11864
+ const fetchManifestStatus = async (manifestUrl) => {
11865
+ const headers = {};
11866
+ if (authTokenRef.current) {
11867
+ headers.Authorization = `Bearer ${authTokenRef.current}`;
11868
+ }
11869
+ const response = await fetch(buildCacheBustedUrl(manifestUrl), {
11870
+ method: "GET",
11871
+ cache: "no-store",
11872
+ headers
11873
+ });
11874
+ if (!response.ok) {
11875
+ throw new Error(`Manifest fetch failed: ${response.status}`);
11876
+ }
11877
+ const manifestText = await response.text();
11878
+ return parseManifestStatus(manifestText);
11782
11879
  };
11783
11880
  const stopStaleManifestPolling = () => {
11784
11881
  if (staleManifestPollTimerRef.current) {
@@ -11789,6 +11886,8 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
11789
11886
  staleManifestUrlRef.current = null;
11790
11887
  staleManifestEndSnRef.current = null;
11791
11888
  staleManifestPollDelayRef.current = STALE_MANIFEST_POLL_INITIAL_DELAY_MS;
11889
+ setIsStale(false);
11890
+ setStaleReason(null);
11792
11891
  };
11793
11892
  const pollStaleManifestOnce = async () => {
11794
11893
  if (!staleManifestTriggeredRef.current) return;
@@ -11802,25 +11901,24 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
11802
11901
  return;
11803
11902
  }
11804
11903
  try {
11805
- const headers = {};
11806
- if (authTokenRef.current) {
11807
- headers.Authorization = `Bearer ${authTokenRef.current}`;
11808
- }
11809
- const response = await fetch(buildCacheBustedUrl(manifestUrl), {
11810
- method: "GET",
11811
- cache: "no-store",
11812
- headers
11904
+ const {
11905
+ lastProgramDateTimeMs,
11906
+ lastSequenceNumber,
11907
+ lastSegmentTimestampMs
11908
+ } = await fetchManifestStatus(manifestUrl);
11909
+ if (lastSegmentTimestampMs !== null) {
11910
+ lastSegmentTimestampMsRef.current = lastSegmentTimestampMs;
11911
+ }
11912
+ const enforceSegmentAge = isR2StreamRef.current;
11913
+ const hasAnyTimestamp = lastSegmentTimestampMs !== null || lastProgramDateTimeMs !== null;
11914
+ const freshness = evaluateSegmentFreshness({
11915
+ segmentTimestampMs: lastSegmentTimestampMs,
11916
+ fallbackTimestampMs: lastProgramDateTimeMs,
11917
+ enforceSegmentAge
11813
11918
  });
11814
- if (!response.ok) {
11815
- throw new Error(`Manifest poll failed: ${response.status}`);
11816
- }
11817
- const manifestText = await response.text();
11818
- const { lastProgramDateTimeMs, lastSequenceNumber } = parseManifestStatus(manifestText);
11819
- const now4 = Date.now();
11820
- const isFreshByProgramDateTime = lastProgramDateTimeMs !== null && now4 - lastProgramDateTimeMs <= maxManifestAgeMs;
11821
11919
  const priorEndSn = staleManifestEndSnRef.current;
11822
11920
  const isSequenceAdvanced = typeof lastSequenceNumber === "number" && (priorEndSn === null || lastSequenceNumber > priorEndSn);
11823
- if (isFreshByProgramDateTime || isSequenceAdvanced) {
11921
+ if (freshness.isFresh || !enforceSegmentAge && !hasAnyTimestamp && isSequenceAdvanced) {
11824
11922
  stopStaleManifestPolling();
11825
11923
  if (hlsRef.current) {
11826
11924
  hlsRef.current.startLoad(-1);
@@ -11855,6 +11953,8 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
11855
11953
  staleManifestUrlRef.current = activeStreamUrlRef.current || latestSrcRef.current;
11856
11954
  staleManifestEndSnRef.current = lastManifestEndSnRef.current;
11857
11955
  staleManifestPollDelayRef.current = STALE_MANIFEST_POLL_INITIAL_DELAY_MS;
11956
+ setIsStale(true);
11957
+ setStaleReason(reason);
11858
11958
  const hls = hlsRef.current;
11859
11959
  if (hls) {
11860
11960
  hls.stopLoad();
@@ -11912,6 +12012,10 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
11912
12012
  clearInterval(manifestWatchdogRef.current);
11913
12013
  manifestWatchdogRef.current = null;
11914
12014
  }
12015
+ if (nativeFreshnessIntervalRef.current) {
12016
+ clearInterval(nativeFreshnessIntervalRef.current);
12017
+ nativeFreshnessIntervalRef.current = null;
12018
+ }
11915
12019
  if (manifestRetryTimerRef.current) {
11916
12020
  clearTimeout(manifestRetryTimerRef.current);
11917
12021
  manifestRetryTimerRef.current = null;
@@ -11936,6 +12040,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
11936
12040
  lastManifestEndSnRef.current = null;
11937
12041
  lastManifestEndSnUpdatedAtRef.current = null;
11938
12042
  staleManifestTriggeredRef.current = false;
12043
+ lastSegmentTimestampMsRef.current = null;
11939
12044
  manifestRetryDelayRef.current = 5e3;
11940
12045
  playRetryCountRef.current = 0;
11941
12046
  if (hlsRef.current) {
@@ -12055,6 +12160,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
12055
12160
  const attemptPlay = (reason) => {
12056
12161
  const video = videoRef.current;
12057
12162
  if (!video || !shouldPlayRef.current) return;
12163
+ if (staleManifestTriggeredRef.current) return;
12058
12164
  if (!video.paused || video.seeking) return;
12059
12165
  if (video.readyState < 2) return;
12060
12166
  video.play().then(() => {
@@ -12080,7 +12186,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
12080
12186
  lastHiddenAtRef.current = null;
12081
12187
  if (!lastHiddenAt) return;
12082
12188
  if (Date.now() - lastHiddenAt < 3e4) return;
12083
- refreshLiveStream("tab visible after idle");
12189
+ void refreshLiveStream("tab visible after idle");
12084
12190
  attemptPlay();
12085
12191
  };
12086
12192
  const startManifestWatchdog = () => {
@@ -12156,7 +12262,38 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
12156
12262
  return `${url}${separator}ts=${Date.now()}`;
12157
12263
  }
12158
12264
  };
12159
- const refreshLiveStream = (reason) => {
12265
+ const ensureManifestFreshness = async (manifestUrl, enforceSegmentAge, reason) => {
12266
+ try {
12267
+ const status = await fetchManifestStatus(manifestUrl);
12268
+ if (status.lastSegmentTimestampMs !== null) {
12269
+ lastSegmentTimestampMsRef.current = status.lastSegmentTimestampMs;
12270
+ }
12271
+ const freshness = evaluateSegmentFreshness({
12272
+ segmentTimestampMs: status.lastSegmentTimestampMs,
12273
+ fallbackTimestampMs: status.lastProgramDateTimeMs,
12274
+ enforceSegmentAge
12275
+ });
12276
+ if (!freshness.isFresh) {
12277
+ markStaleStream(freshness.reason || reason);
12278
+ return false;
12279
+ }
12280
+ return true;
12281
+ } catch (error) {
12282
+ debugLog("[HLS] Manifest freshness check failed", error);
12283
+ {
12284
+ markStaleStream("manifest freshness check failed");
12285
+ return false;
12286
+ }
12287
+ }
12288
+ };
12289
+ const startNativeFreshnessMonitor = (manifestUrl) => {
12290
+ if (nativeFreshnessIntervalRef.current) return;
12291
+ nativeFreshnessIntervalRef.current = setInterval(() => {
12292
+ if (staleManifestTriggeredRef.current) return;
12293
+ void ensureManifestFreshness(manifestUrl, true, "native freshness check");
12294
+ }, 3e4);
12295
+ };
12296
+ const refreshLiveStream = async (reason) => {
12160
12297
  if (!isR2StreamRef.current) return;
12161
12298
  const now4 = Date.now();
12162
12299
  if (now4 - lastLiveReloadRef.current < LIVE_RELOAD_MIN_INTERVAL_MS) {
@@ -12172,6 +12309,10 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
12172
12309
  }
12173
12310
  if (video && nativeStreamUrlRef.current) {
12174
12311
  console.log(`[HLS] Native live reload (${reason})`);
12312
+ const isFresh = await ensureManifestFreshness(nativeStreamUrlRef.current, true, "native live reload");
12313
+ if (!isFresh) {
12314
+ return;
12315
+ }
12175
12316
  const refreshedUrl = buildCacheBustedUrl(nativeStreamUrlRef.current);
12176
12317
  video.src = refreshedUrl;
12177
12318
  video.load();
@@ -12263,7 +12404,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
12263
12404
  };
12264
12405
  const handleEnded = () => {
12265
12406
  if (isNativeHlsRef.current) {
12266
- refreshLiveStream("ended");
12407
+ void refreshLiveStream("ended");
12267
12408
  return;
12268
12409
  }
12269
12410
  if (isR2StreamRef.current) {
@@ -12277,7 +12418,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
12277
12418
  if (isNativeHlsRef.current) {
12278
12419
  if (!isR2StreamRef.current) return;
12279
12420
  waitingTimerRef.current = setTimeout(() => {
12280
- refreshLiveStream("native waiting timeout");
12421
+ void refreshLiveStream("native waiting timeout");
12281
12422
  }, getWaitingTimeoutMs());
12282
12423
  return;
12283
12424
  }
@@ -12400,12 +12541,17 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
12400
12541
  nativeStreamUrlRef.current = resolvedSrc;
12401
12542
  activeStreamUrlRef.current = resolvedSrc;
12402
12543
  console.log("[HLS] Using proxy playlist for Safari R2 stream");
12544
+ const isFresh = await ensureManifestFreshness(resolvedSrc, true, "native proxy start");
12545
+ if (!isFresh) {
12546
+ return;
12547
+ }
12403
12548
  video.src = resolvedSrc;
12404
12549
  video.addEventListener("waiting", handleWaiting);
12405
12550
  video.addEventListener("loadedmetadata", handleLoadedMetadata);
12406
12551
  video.addEventListener("canplay", handleCanPlay);
12407
12552
  video.addEventListener("ended", handleEnded);
12408
12553
  video.addEventListener("error", handleNativeError);
12554
+ startNativeFreshnessMonitor(resolvedSrc);
12409
12555
  attemptPlay();
12410
12556
  return;
12411
12557
  }
@@ -12573,20 +12719,29 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
12573
12719
  }
12574
12720
  }
12575
12721
  }
12576
- if (maxManifestAgeMs > 0 && !details.endList) {
12722
+ if (!details.endList) {
12577
12723
  const now4 = Date.now();
12578
12724
  const fragments = Array.isArray(details.fragments) ? details.fragments : [];
12579
12725
  const lastFragment = fragments.length ? fragments[fragments.length - 1] : void 0;
12580
- const programDateTimeMs = getProgramDateTimeMs(lastFragment?.programDateTime);
12581
- if (programDateTimeMs && now4 - programDateTimeMs > maxManifestAgeMs) {
12582
- markStaleStream(`segment age ${Math.round((now4 - programDateTimeMs) / 1e3)}s`);
12726
+ const segmentTimestampMs = getLatestFragmentTimestampMs(fragments, true);
12727
+ const enforceSegmentAge = isR2StreamRef.current;
12728
+ if (segmentTimestampMs !== null) {
12729
+ lastSegmentTimestampMsRef.current = segmentTimestampMs;
12730
+ }
12731
+ const freshness = evaluateSegmentFreshness({
12732
+ segmentTimestampMs,
12733
+ fallbackTimestampMs: getProgramDateTimeMs(lastFragment?.programDateTime),
12734
+ enforceSegmentAge
12735
+ });
12736
+ if (!freshness.isFresh) {
12737
+ markStaleStream(freshness.reason || "segment stale");
12583
12738
  return;
12584
12739
  }
12585
12740
  const endSn = typeof details.endSN === "number" ? details.endSN : lastFragment?.sn;
12586
12741
  if (typeof endSn === "number") {
12587
12742
  if (lastManifestEndSnRef.current === endSn) {
12588
12743
  const lastUpdatedAt = lastManifestEndSnUpdatedAtRef.current;
12589
- if (lastUpdatedAt && now4 - lastUpdatedAt > maxManifestAgeMs) {
12744
+ if (lastUpdatedAt && now4 - lastUpdatedAt > manifestStaleThresholdMs) {
12590
12745
  markStaleStream(`sequence stalled at ${endSn}`);
12591
12746
  return;
12592
12747
  }
@@ -12611,6 +12766,27 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
12611
12766
  lastManifestLoadRef.current = Date.now();
12612
12767
  resetManifestRetry();
12613
12768
  });
12769
+ hls.on(Hls.Events.FRAG_LOADING, (_event, data) => {
12770
+ if (staleManifestTriggeredRef.current) return;
12771
+ const frag = data?.frag;
12772
+ if (!frag || frag.sn === "initSegment") return;
12773
+ const enforceSegmentAge = isR2StreamRef.current;
12774
+ const segmentTimestampMs = getFragmentTimestampMs(frag, true);
12775
+ if (segmentTimestampMs !== null) {
12776
+ lastSegmentTimestampMsRef.current = segmentTimestampMs;
12777
+ }
12778
+ const freshness = evaluateSegmentFreshness({
12779
+ segmentTimestampMs,
12780
+ fallbackTimestampMs: getProgramDateTimeMs(frag.programDateTime),
12781
+ enforceSegmentAge
12782
+ });
12783
+ if (!freshness.isFresh) {
12784
+ if (frag.loader?.abort) {
12785
+ frag.loader.abort();
12786
+ }
12787
+ markStaleStream(freshness.reason || "segment stale");
12788
+ }
12789
+ });
12614
12790
  hls.on(Hls.Events.FRAG_LOADED, (_event, data) => {
12615
12791
  if (!isR2Stream) return;
12616
12792
  lastFragLoadRef.current = Date.now();
@@ -12635,9 +12811,15 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
12635
12811
  if (canUseNative) {
12636
12812
  isNativeHlsRef.current = true;
12637
12813
  console.log("[HLS] Using native HLS");
12638
- video.src = resolvedSrc;
12639
12814
  nativeStreamUrlRef.current = resolvedSrc;
12640
12815
  activeStreamUrlRef.current = resolvedSrc;
12816
+ if (isR2Stream) {
12817
+ const isFresh = await ensureManifestFreshness(resolvedSrc, true, "native start");
12818
+ if (!isFresh) {
12819
+ return;
12820
+ }
12821
+ }
12822
+ video.src = resolvedSrc;
12641
12823
  video.addEventListener("waiting", handleWaiting);
12642
12824
  video.addEventListener("loadedmetadata", handleLoadedMetadata);
12643
12825
  video.addEventListener("canplay", handleCanPlay);
@@ -12645,6 +12827,9 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
12645
12827
  video.addEventListener("error", handleNativeError);
12646
12828
  startPlaybackGovernor();
12647
12829
  startManifestWatchdog();
12830
+ if (isR2Stream) {
12831
+ startNativeFreshnessMonitor(resolvedSrc);
12832
+ }
12648
12833
  attemptPlay();
12649
12834
  } else {
12650
12835
  console.error("[HLS] HLS not supported");
@@ -12658,7 +12843,10 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
12658
12843
  }, [src, shouldPlay, restartKey, isPermanentlyFailed]);
12659
12844
  return {
12660
12845
  restartKey,
12661
- isNativeHls: isNativeHlsRef.current
12846
+ isNativeHls: isNativeHlsRef.current,
12847
+ isStale,
12848
+ staleReason,
12849
+ lastSegmentTimestampMs: lastSegmentTimestampMsRef.current
12662
12850
  };
12663
12851
  }
12664
12852
  function useHlsStreamWithCropping(videoRef, canvasRef, options) {
@@ -14794,7 +14982,77 @@ function getNextUpdateInterval(timestamp) {
14794
14982
  }
14795
14983
  }
14796
14984
 
14797
- // src/lib/hooks/useWorkspaceHealthStatus.ts
14985
+ // src/lib/hooks/useWorkspaceHealthLastSeen.ts
14986
+ var DEFAULT_REFRESH_INTERVAL_MS = 3e4;
14987
+ var useWorkspaceHealthLastSeen = (workspaceIds, options = {}) => {
14988
+ const supabase = useSupabase();
14989
+ const databaseConfig = useDatabaseConfig();
14990
+ const refreshInterval = options.refreshInterval ?? DEFAULT_REFRESH_INTERVAL_MS;
14991
+ const workspaceIdsKey = useMemo(() => {
14992
+ const ids = Array.from(new Set(workspaceIds.filter(Boolean)));
14993
+ return ids.sort().join(",");
14994
+ }, [workspaceIds]);
14995
+ const [lastSeenByWorkspaceId, setLastSeenByWorkspaceId] = useState({});
14996
+ const [isLoading, setIsLoading] = useState(Boolean(workspaceIdsKey));
14997
+ const [error, setError] = useState(null);
14998
+ const isFetchingRef = useRef(false);
14999
+ const refreshIntervalRef = useRef(null);
15000
+ const fetchLastSeen = useCallback(async () => {
15001
+ if (!supabase || !workspaceIdsKey || isFetchingRef.current) return;
15002
+ const healthTable = databaseConfig?.tables?.workspace_health || "workspace_health_status";
15003
+ try {
15004
+ isFetchingRef.current = true;
15005
+ setIsLoading(true);
15006
+ setError(null);
15007
+ const workspaceIdsList = workspaceIdsKey.split(",").filter(Boolean);
15008
+ if (!workspaceIdsList.length) {
15009
+ setLastSeenByWorkspaceId({});
15010
+ return;
15011
+ }
15012
+ const { data, error: fetchError } = await supabase.from(healthTable).select("workspace_id,last_heartbeat,is_healthy").in("workspace_id", workspaceIdsList);
15013
+ if (fetchError) throw fetchError;
15014
+ const nextMap = {};
15015
+ (data || []).forEach((row) => {
15016
+ if (!row?.workspace_id) return;
15017
+ const lastHeartbeat = row.last_heartbeat || null;
15018
+ nextMap[row.workspace_id] = {
15019
+ lastHeartbeat,
15020
+ timeSinceLastUpdate: formatRelativeTime(lastHeartbeat),
15021
+ isHealthy: Boolean(row.is_healthy)
15022
+ };
15023
+ });
15024
+ setLastSeenByWorkspaceId(nextMap);
15025
+ } catch (err) {
15026
+ console.error("[useWorkspaceHealthLastSeen] Error fetching workspace health:", err);
15027
+ setError(err?.message || "Failed to load workspace health");
15028
+ setLastSeenByWorkspaceId({});
15029
+ } finally {
15030
+ setIsLoading(false);
15031
+ isFetchingRef.current = false;
15032
+ }
15033
+ }, [supabase, workspaceIdsKey, databaseConfig?.tables?.workspace_health]);
15034
+ useEffect(() => {
15035
+ fetchLastSeen();
15036
+ }, [fetchLastSeen]);
15037
+ useEffect(() => {
15038
+ if (!refreshInterval || !workspaceIdsKey) return;
15039
+ refreshIntervalRef.current = setInterval(() => {
15040
+ fetchLastSeen();
15041
+ }, refreshInterval);
15042
+ return () => {
15043
+ if (refreshIntervalRef.current) {
15044
+ clearInterval(refreshIntervalRef.current);
15045
+ refreshIntervalRef.current = null;
15046
+ }
15047
+ };
15048
+ }, [fetchLastSeen, refreshInterval, workspaceIdsKey]);
15049
+ return {
15050
+ lastSeenByWorkspaceId,
15051
+ isLoading,
15052
+ error,
15053
+ refetch: fetchLastSeen
15054
+ };
15055
+ };
14798
15056
  var useWorkspaceHealthStatus = (workspaceId) => {
14799
15057
  const supabase = useSupabase();
14800
15058
  const databaseConfig = useDatabaseConfig();
@@ -29759,13 +30017,14 @@ var VideoCard = React25__default.memo(({
29759
30017
  className = "",
29760
30018
  compact = false,
29761
30019
  displayName,
30020
+ lastSeenLabel,
29762
30021
  onMouseEnter,
29763
30022
  onMouseLeave
29764
30023
  }) => {
29765
30024
  const videoRef = useRef(null);
29766
30025
  const canvasRef = useRef(null);
29767
30026
  const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;
29768
- useHlsStreamWithCropping(videoRef, canvasRef, {
30027
+ const { isStale: isStreamStale } = useHlsStreamWithCropping(videoRef, canvasRef, {
29769
30028
  src: hlsUrl,
29770
30029
  shouldPlay,
29771
30030
  cropping,
@@ -29773,6 +30032,8 @@ var VideoCard = React25__default.memo(({
29773
30032
  useRAF,
29774
30033
  onFatalError: onFatalError ?? (() => throttledReloadDashboard())
29775
30034
  });
30035
+ const showOffline = Boolean(isStreamStale);
30036
+ const lastSeenText = lastSeenLabel || "Unknown";
29776
30037
  const workspaceDisplayName = displayName || getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
29777
30038
  const efficiencyColor = getEfficiencyColor(workspace.efficiency, effectiveLegend);
29778
30039
  const efficiencyOverlayClass = efficiencyColor === "green" ? "bg-[#00D654]/25" : efficiencyColor === "yellow" ? "bg-[#FFD700]/30" : "bg-[#FF2D0A]/30";
@@ -29842,6 +30103,14 @@ var VideoCard = React25__default.memo(({
29842
30103
  )
29843
30104
  ] }),
29844
30105
  /* @__PURE__ */ jsx("div", { className: `absolute inset-0 z-20 pointer-events-none ${efficiencyOverlayClass}` }),
30106
+ showOffline && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 z-40 flex items-center justify-center bg-black/70 px-2 text-center", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-1", children: [
30107
+ /* @__PURE__ */ jsx(AlertTriangle, { className: `${compact ? "w-4 h-4" : "w-5 h-5"} text-amber-300` }),
30108
+ /* @__PURE__ */ jsx("span", { className: `font-semibold text-white ${compact ? "text-[11px]" : "text-xs"}`, children: "Not streaming" }),
30109
+ /* @__PURE__ */ jsxs("span", { className: `text-gray-200 ${compact ? "text-[10px]" : "text-[11px]"}`, children: [
30110
+ "Last seen: ",
30111
+ lastSeenText
30112
+ ] })
30113
+ ] }) }),
29845
30114
  /* @__PURE__ */ jsxs("div", { className: `absolute ${compact ? "top-1 right-1" : "top-2 right-2"} z-30 bg-black/70 backdrop-blur-sm rounded ${compact ? "px-1.5 py-0.5" : "px-2 py-0.5"} text-white ${compact ? "text-[10px]" : "text-xs"} font-semibold border border-white/10`, children: [
29846
30115
  Math.round(workspace.efficiency),
29847
30116
  "%"
@@ -29868,8 +30137,13 @@ var VideoCard = React25__default.memo(({
29868
30137
  children: trendInfo.arrow
29869
30138
  }
29870
30139
  ),
29871
- /* @__PURE__ */ jsx("div", { className: `${compact ? "w-1 h-1" : "w-1.5 h-1.5"} rounded-full bg-green-500` }),
29872
- /* @__PURE__ */ jsx("span", { className: `text-white text-[11px] sm:${compact ? "text-[10px]" : "text-xs"}`, children: "Live" })
30140
+ /* @__PURE__ */ jsx(
30141
+ "div",
30142
+ {
30143
+ className: `${compact ? "w-1 h-1" : "w-1.5 h-1.5"} rounded-full ${showOffline ? "bg-red-500" : "bg-green-500"}`
30144
+ }
30145
+ ),
30146
+ /* @__PURE__ */ jsx("span", { className: `text-white text-[11px] sm:${compact ? "text-[10px]" : "text-xs"}`, children: showOffline ? "Offline" : "Live" })
29873
30147
  ] })
29874
30148
  ] })
29875
30149
  ]
@@ -29885,6 +30159,9 @@ var VideoCard = React25__default.memo(({
29885
30159
  if (prevProps.displayName !== nextProps.displayName) {
29886
30160
  return false;
29887
30161
  }
30162
+ if (prevProps.lastSeenLabel !== nextProps.lastSeenLabel) {
30163
+ return false;
30164
+ }
29888
30165
  if (prevProps.legend !== nextProps.legend) {
29889
30166
  return false;
29890
30167
  }
@@ -29942,6 +30219,16 @@ var VideoGridView = React25__default.memo(({
29942
30219
  const supabase = useSupabase();
29943
30220
  const { cropping, canvasConfig, hlsUrls } = videoConfig;
29944
30221
  const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;
30222
+ const workspaceHealthIds = useMemo(() => {
30223
+ const ids = /* @__PURE__ */ new Set();
30224
+ for (const workspace of workspaces) {
30225
+ if (workspace.workspace_uuid) {
30226
+ ids.add(workspace.workspace_uuid);
30227
+ }
30228
+ }
30229
+ return Array.from(ids);
30230
+ }, [workspaces]);
30231
+ const { lastSeenByWorkspaceId } = useWorkspaceHealthLastSeen(workspaceHealthIds);
29945
30232
  useEffect(() => {
29946
30233
  const sample = workspaces.slice(0, 3).map((workspace) => ({
29947
30234
  id: workspace.workspace_uuid || workspace.workspace_name,
@@ -30193,6 +30480,7 @@ var VideoGridView = React25__default.memo(({
30193
30480
  const isVeryLowEfficiency = workspace.show_exclamation ?? (workspace.efficiency < 50 && workspace.efficiency >= 10);
30194
30481
  const workspaceCropping = getWorkspaceCropping(workspaceId, workspace.workspace_name);
30195
30482
  const workspaceStream = videoStreamsByWorkspaceId?.[workspaceId];
30483
+ const lastSeenLabel = workspace.workspace_uuid ? lastSeenByWorkspaceId[workspace.workspace_uuid]?.timeSinceLastUpdate : void 0;
30196
30484
  const r2Url = workspaceStream?.hls_url;
30197
30485
  const fallbackUrl = getWorkspaceHlsUrl(workspace.workspace_name, workspace.line_id);
30198
30486
  const hasR2Stream = Boolean(r2Url);
@@ -30210,7 +30498,8 @@ var VideoGridView = React25__default.memo(({
30210
30498
  fallbackUrl,
30211
30499
  hlsUrl,
30212
30500
  isR2Stream,
30213
- shouldPlay
30501
+ shouldPlay,
30502
+ lastSeenLabel
30214
30503
  };
30215
30504
  });
30216
30505
  const croppedActiveCount = workspaceCards.reduce((count, card) => {
@@ -30248,6 +30537,7 @@ var VideoGridView = React25__default.memo(({
30248
30537
  displayNames[`${card.workspace.line_id}_${card.workspace.workspace_name}`] || // Always pass line_id to fallback to ensure correct mapping per line
30249
30538
  getWorkspaceDisplayName(card.workspace.workspace_name, card.workspace.line_id)
30250
30539
  ),
30540
+ lastSeenLabel: card.lastSeenLabel,
30251
30541
  useRAF: effectiveUseRAF,
30252
30542
  compact: !selectedLine,
30253
30543
  onMouseEnter: onWorkspaceHover ? () => onWorkspaceHover(card.workspaceId) : void 0,
@@ -66221,4 +66511,4 @@ var streamProxyConfig = {
66221
66511
  }
66222
66512
  };
66223
66513
 
66224
- export { ACTION_NAMES, AIAgentView_default as AIAgentView, AcceptInvite, AcceptInviteView_default as AcceptInviteView, AdvancedFilterDialog, AdvancedFilterPanel, AudioService, AuthCallback, AuthCallbackView_default as AuthCallbackView, AuthProvider, AuthService, AuthenticatedBottleneckClipsView, AuthenticatedFactoryView, AuthenticatedHelpView, AuthenticatedHomeView, AuthenticatedShiftsView, AuthenticatedTargetsView, AuthenticatedTicketsView, AuthenticatedWorkspaceHealthView, AvatarUpload, AxelNotificationPopup, AxelOrb, BackButton, BackButtonMinimal, BarChart, BaseHistoryCalendar, BottleneckClipsModal, BottleneckClipsView_default as BottleneckClipsView, BottlenecksContent, BreakNotificationPopup, CachePrefetchStatus, Card2 as Card, CardContent2 as CardContent, CardDescription2 as CardDescription, CardFooter2 as CardFooter, CardHeader2 as CardHeader, CardTitle2 as CardTitle, ChangeRoleDialog, ClipFilterProvider, CompactWorkspaceHealthCard, ConfirmRemoveUserDialog, CongratulationsOverlay, CroppedHlsVideoPlayer, 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_HOME_VIEW_CONFIG, DEFAULT_MAP_VIEW_CONFIG, DEFAULT_SHIFT_CONFIG, DEFAULT_SHIFT_DATA, 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, DiagnosisVideoModal, EmptyStateMessage, EncouragementOverlay, FactoryAssignmentDropdown, FactoryView_default as FactoryView, FileManagerFilters, FilterDialogTrigger, FirstTimeLoginDebug, FirstTimeLoginHandler, FittingTitle, GaugeChart, GridComponentsPlaceholder, HamburgerButton, Header, HealthDateShiftSelector, HealthStatusGrid, HealthStatusIndicator, HelpView_default as HelpView, HlsVideoPlayer, HomeView_default as HomeView, HourlyOutputChart2 as HourlyOutputChart, ISTTimer_default as ISTTimer, ImprovementCenterView_default as ImprovementCenterView, InlineEditableText, InteractiveOnboardingTour, InvitationService, InvitationsTable, InviteUserDialog, 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, LineAssignmentDropdown, 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, Logo, MainLayout, MapGridView, MetricCard_default as MetricCard, MinimalOnboardingPopup, MobileMenuProvider, NewClipsNotification, NoWorkspaceData, OnboardingDemo, OnboardingTour, OptifyeAgentClient, OptifyeLogoLoader_default as OptifyeLogoLoader, OutputProgressChart, PageHeader, PieChart4 as PieChart, PlayPauseIndicator, PrefetchConfigurationError, PrefetchError, PrefetchEvents, PrefetchStatus, PrefetchTimeoutError, ProfileView_default as ProfileView, RegistryProvider, RoleBadge, S3ClipsSupabaseService as S3ClipsService, S3Service, SKUManagementView, SOPComplianceChart, SSEChatClient, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, SessionTracker, SessionTrackingContext, SessionTrackingProvider, SettingsPopup, ShiftDisplay_default as ShiftDisplay, ShiftsView_default as ShiftsView, SideNavBar, SignupWithInvitation, SilentErrorBoundary, SimpleOnboardingPopup, SingleVideoStream_default as SingleVideoStream, Skeleton, SubscriptionManager, SubscriptionManagerProvider, SupabaseProvider, SupervisorDropdown_default as SupervisorDropdown, SupervisorManagementView_default as SupervisorManagementView, SupervisorService, TargetWorkspaceGrid, TargetsView_default as TargetsView, TeamManagementView_default as TeamManagementView, TeamUsagePdfGenerator, ThreadSidebar, TicketHistory_default as TicketHistory, TicketHistoryService, TicketsView_default as TicketsView, TimeDisplay_default as TimeDisplay, TimePickerDropdown, Timer_default as Timer, TimezoneProvider, TimezoneService, UserAvatar, UserManagementService, UserManagementTable, UserService, UserUsageDetailModal, UserUsageStats, 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, aggregateKPIsFromLineMetricsRows, apiUtils, areAllLinesOnSameShift, authCoreService, authOTPService, authRateLimitService, awardsService, buildDateKey, buildKPIsFromLineMetricsRow, buildShiftGroupsKey, checkRateLimit2 as checkRateLimit, clearAllRateLimits2 as clearAllRateLimits, clearRateLimit2 as clearRateLimit, clearS3VideoCache, clearS3VideoFromCache, clearSentryContext, clearWorkspaceDisplayNamesCache, cn, createDefaultKPIs, createInvitationService, createLinesService, createSessionTracker, createStorageService, createStreamProxyHandler, createSupabaseClient, createSupervisorService, createThrottledReload, createUserManagementService, createUserService, dashboardService, deleteThread, fetchIdleTimeReasons, filterDataByDateKeyRange, forceRefreshWorkspaceDisplayNames, formatAwardMonth, formatDateInZone, formatDateKeyForDisplay, formatDateTimeInZone, formatDuration, formatISTDate, formatIdleTime, formatRangeLabel, formatReasonLabel, formatRelativeTime, formatTimeInZone, fromUrlFriendlyName, getAllLineDisplayNames, getAllThreadMessages, getAllWorkspaceDisplayNamesAsync, getAllWorkspaceDisplayNamesSnapshot, getAnonClient, getAvailableShiftIds, getAwardBadgeType, getAwardDescription, getAwardTitle, getBrowserName, getCameraNumber, getCompanyMetricsTableName, getConfigurableShortWorkspaceDisplayName, getConfigurableWorkspaceDisplayName, getConfiguredLineIds, getCoreSessionRecordingProperties, getCoreSessionReplayUrl, getCurrentShift, getCurrentShiftForLine, getCurrentTimeInZone, getDashboardHeaderTimeInZone, getDateKeyFromDate, getDaysDifferenceInZone, getDefaultCameraStreamUrl, getDefaultLineId, getDefaultTabForWorkspace, getInitials, getLineDisplayName, getManufacturingInsights, getMetricsTablePrefix, getMonthKeyBounds, getMonthWeekRanges, getNextUpdateInterval, getOperationalDate, getReasonColor, getS3SignedUrl, getS3VideoSrc, getShiftData, getShiftNameById, getShiftWorkDurationSeconds, getShortShiftName, getShortWorkspaceDisplayName, getShortWorkspaceDisplayNameAsync, getStoredWorkspaceMappings, getSubscriptionManager, getThreadMessages, getUniformShiftGroup, getUserThreads, getUserThreadsPaginated, getWorkspaceDisplayName, getWorkspaceDisplayNameAsync, getWorkspaceDisplayNamesMap, getWorkspaceFromUrl, getWorkspaceNavigationParams, groupLinesByShift, hasAnyShiftData, identifyCoreUser, initializeCoreMixpanel, isFullMonthRange, isLegacyConfiguration, isPrefetchError, isSafari, isTransitionPeriod, isUrlPermanentlyFailed, isValidFactoryViewConfiguration, isValidLineInfoPayload, isValidPrefetchParams, isValidPrefetchStatus, isValidWorkspaceDetailedMetricsPayload, isValidWorkspaceMetricsPayload, isWorkspaceDisplayNamesLoaded, isWorkspaceDisplayNamesLoading, linesService, mergeWithDefaultConfig, migrateLegacyConfiguration, normalizeDateKeyRange, optifyeAgentClient, parseDateKeyToDate, parseS3Uri, preInitializeWorkspaceDisplayNames, preloadS3Video, preloadS3VideoUrl, preloadS3VideosUrl, preloadVideoUrl, preloadVideosUrl, qualityService, realtimeService, refreshWorkspaceDisplayNames, resetCoreMixpanel, resetFailedUrl, resetSubscriptionManager, s3VideoPreloader, setSentryUserContext, setSentryWorkspaceContext, shuffleArray, simulateApiDelay, skuService, startCoreSessionRecording, stopCoreSessionRecording, storeWorkspaceMapping, streamProxyConfig, subscribeWorkspaceDisplayNames, throttledReloadDashboard, toUrlFriendlyName, trackCoreEvent, trackCorePageView, transformToChartData, updateThreadTitle, upsertWorkspaceDisplayNameInCache, useAccessControl, useActiveBreaks, useActiveLineId, useAllWorkspaceMetrics, useAnalyticsConfig, useAppTimezone, useAudioService, useAuth, useAuthConfig, useAxelNotifications, useCanSaveTargets, useClipFilter, useClipTypes, useClipTypesWithCounts, useClipsInit, useCompanyUsersUsage, useComponentOverride, useCustomConfig, useDashboardConfig, useDashboardMetrics, useDatabaseConfig, useDateFormatter, useDateTimeConfig, useDynamicShiftConfig, useEndpointsConfig, useEntityConfig, useFactoryOverviewMetrics, useFeatureFlags, useFormatNumber, useHasLineAccess, useHideMobileHeader, useHistoricWorkspaceMetrics, useHlsStream, useHlsStreamWithCropping, useHookOverride, useHourEndTimer, useHourlyTargetAchievements, useHourlyTargetMisses, useIdleTimeClipClassifications, useIdleTimeReasons, useLeaderboardMetrics, useLineDetailedMetrics, useLineKPIs, useLineMetrics, useLineShiftConfig, useLineSupervisor, useLineWorkspaceMetrics, useLines, useMessages, useMetrics, useMobileMenu, useMultiLineShiftConfigs, useNavigation, useOperationalShiftKey, useOptionalSupabase, useOverrides, usePageOverride, usePrefetchClipCounts, useRealtimeLineMetrics, useRegistry, useSKUs, useSessionKeepAlive, useSessionTracking, useSessionTrackingContext, useShiftConfig, useShiftGroups, useShifts, useSubscriptionManager, useSubscriptionManagerSafe, useSupabase, useSupabaseClient, useSupervisorsByLineIds, useTargets, useTeamManagementPermissions, useTheme, useThemeConfig, useThreads, useTicketHistory, useTimezoneContext, useUserLineAccess, useUserUsage, useVideoConfig, useWorkspaceConfig, useWorkspaceDetailedMetrics, useWorkspaceDisplayName, useWorkspaceDisplayNames, useWorkspaceDisplayNamesMap, useWorkspaceHealthById, useWorkspaceHealthStatus, useWorkspaceMetrics, useWorkspaceNavigation, useWorkspaceOperators, useWorkspaceUptimeTimeline, useWorkspaceVideoStreams, userService, videoPrefetchManager, videoPreloader, weeklyTopPerformerService, whatsappService, withAccessControl, withAuth, withRegistry, withTimezone, workspaceHealthService, workspaceService };
66514
+ export { ACTION_NAMES, AIAgentView_default as AIAgentView, AcceptInvite, AcceptInviteView_default as AcceptInviteView, AdvancedFilterDialog, AdvancedFilterPanel, AudioService, AuthCallback, AuthCallbackView_default as AuthCallbackView, AuthProvider, AuthService, AuthenticatedBottleneckClipsView, AuthenticatedFactoryView, AuthenticatedHelpView, AuthenticatedHomeView, AuthenticatedShiftsView, AuthenticatedTargetsView, AuthenticatedTicketsView, AuthenticatedWorkspaceHealthView, AvatarUpload, AxelNotificationPopup, AxelOrb, BackButton, BackButtonMinimal, BarChart, BaseHistoryCalendar, BottleneckClipsModal, BottleneckClipsView_default as BottleneckClipsView, BottlenecksContent, BreakNotificationPopup, CachePrefetchStatus, Card2 as Card, CardContent2 as CardContent, CardDescription2 as CardDescription, CardFooter2 as CardFooter, CardHeader2 as CardHeader, CardTitle2 as CardTitle, ChangeRoleDialog, ClipFilterProvider, CompactWorkspaceHealthCard, ConfirmRemoveUserDialog, CongratulationsOverlay, CroppedHlsVideoPlayer, 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_HOME_VIEW_CONFIG, DEFAULT_MAP_VIEW_CONFIG, DEFAULT_SHIFT_CONFIG, DEFAULT_SHIFT_DATA, 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, DiagnosisVideoModal, EmptyStateMessage, EncouragementOverlay, FactoryAssignmentDropdown, FactoryView_default as FactoryView, FileManagerFilters, FilterDialogTrigger, FirstTimeLoginDebug, FirstTimeLoginHandler, FittingTitle, GaugeChart, GridComponentsPlaceholder, HamburgerButton, Header, HealthDateShiftSelector, HealthStatusGrid, HealthStatusIndicator, HelpView_default as HelpView, HlsVideoPlayer, HomeView_default as HomeView, HourlyOutputChart2 as HourlyOutputChart, ISTTimer_default as ISTTimer, ImprovementCenterView_default as ImprovementCenterView, InlineEditableText, InteractiveOnboardingTour, InvitationService, InvitationsTable, InviteUserDialog, 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, LineAssignmentDropdown, 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, Logo, MainLayout, MapGridView, MetricCard_default as MetricCard, MinimalOnboardingPopup, MobileMenuProvider, NewClipsNotification, NoWorkspaceData, OnboardingDemo, OnboardingTour, OptifyeAgentClient, OptifyeLogoLoader_default as OptifyeLogoLoader, OutputProgressChart, PageHeader, PieChart4 as PieChart, PlayPauseIndicator, PrefetchConfigurationError, PrefetchError, PrefetchEvents, PrefetchStatus, PrefetchTimeoutError, ProfileView_default as ProfileView, RegistryProvider, RoleBadge, S3ClipsSupabaseService as S3ClipsService, S3Service, SKUManagementView, SOPComplianceChart, SSEChatClient, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, SessionTracker, SessionTrackingContext, SessionTrackingProvider, SettingsPopup, ShiftDisplay_default as ShiftDisplay, ShiftsView_default as ShiftsView, SideNavBar, SignupWithInvitation, SilentErrorBoundary, SimpleOnboardingPopup, SingleVideoStream_default as SingleVideoStream, Skeleton, SubscriptionManager, SubscriptionManagerProvider, SupabaseProvider, SupervisorDropdown_default as SupervisorDropdown, SupervisorManagementView_default as SupervisorManagementView, SupervisorService, TargetWorkspaceGrid, TargetsView_default as TargetsView, TeamManagementView_default as TeamManagementView, TeamUsagePdfGenerator, ThreadSidebar, TicketHistory_default as TicketHistory, TicketHistoryService, TicketsView_default as TicketsView, TimeDisplay_default as TimeDisplay, TimePickerDropdown, Timer_default as Timer, TimezoneProvider, TimezoneService, UserAvatar, UserManagementService, UserManagementTable, UserService, UserUsageDetailModal, UserUsageStats, 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, aggregateKPIsFromLineMetricsRows, apiUtils, areAllLinesOnSameShift, authCoreService, authOTPService, authRateLimitService, awardsService, buildDateKey, buildKPIsFromLineMetricsRow, buildShiftGroupsKey, checkRateLimit2 as checkRateLimit, clearAllRateLimits2 as clearAllRateLimits, clearRateLimit2 as clearRateLimit, clearS3VideoCache, clearS3VideoFromCache, clearSentryContext, clearWorkspaceDisplayNamesCache, cn, createDefaultKPIs, createInvitationService, createLinesService, createSessionTracker, createStorageService, createStreamProxyHandler, createSupabaseClient, createSupervisorService, createThrottledReload, createUserManagementService, createUserService, dashboardService, deleteThread, fetchIdleTimeReasons, filterDataByDateKeyRange, forceRefreshWorkspaceDisplayNames, formatAwardMonth, formatDateInZone, formatDateKeyForDisplay, formatDateTimeInZone, formatDuration, formatISTDate, formatIdleTime, formatRangeLabel, formatReasonLabel, formatRelativeTime, formatTimeInZone, fromUrlFriendlyName, getAllLineDisplayNames, getAllThreadMessages, getAllWorkspaceDisplayNamesAsync, getAllWorkspaceDisplayNamesSnapshot, getAnonClient, getAvailableShiftIds, getAwardBadgeType, getAwardDescription, getAwardTitle, getBrowserName, getCameraNumber, getCompanyMetricsTableName, getConfigurableShortWorkspaceDisplayName, getConfigurableWorkspaceDisplayName, getConfiguredLineIds, getCoreSessionRecordingProperties, getCoreSessionReplayUrl, getCurrentShift, getCurrentShiftForLine, getCurrentTimeInZone, getDashboardHeaderTimeInZone, getDateKeyFromDate, getDaysDifferenceInZone, getDefaultCameraStreamUrl, getDefaultLineId, getDefaultTabForWorkspace, getInitials, getLineDisplayName, getManufacturingInsights, getMetricsTablePrefix, getMonthKeyBounds, getMonthWeekRanges, getNextUpdateInterval, getOperationalDate, getReasonColor, getS3SignedUrl, getS3VideoSrc, getShiftData, getShiftNameById, getShiftWorkDurationSeconds, getShortShiftName, getShortWorkspaceDisplayName, getShortWorkspaceDisplayNameAsync, getStoredWorkspaceMappings, getSubscriptionManager, getThreadMessages, getUniformShiftGroup, getUserThreads, getUserThreadsPaginated, getWorkspaceDisplayName, getWorkspaceDisplayNameAsync, getWorkspaceDisplayNamesMap, getWorkspaceFromUrl, getWorkspaceNavigationParams, groupLinesByShift, hasAnyShiftData, identifyCoreUser, initializeCoreMixpanel, isFullMonthRange, isLegacyConfiguration, isPrefetchError, isSafari, isTransitionPeriod, isUrlPermanentlyFailed, isValidFactoryViewConfiguration, isValidLineInfoPayload, isValidPrefetchParams, isValidPrefetchStatus, isValidWorkspaceDetailedMetricsPayload, isValidWorkspaceMetricsPayload, isWorkspaceDisplayNamesLoaded, isWorkspaceDisplayNamesLoading, linesService, mergeWithDefaultConfig, migrateLegacyConfiguration, normalizeDateKeyRange, optifyeAgentClient, parseDateKeyToDate, parseS3Uri, preInitializeWorkspaceDisplayNames, preloadS3Video, preloadS3VideoUrl, preloadS3VideosUrl, preloadVideoUrl, preloadVideosUrl, qualityService, realtimeService, refreshWorkspaceDisplayNames, resetCoreMixpanel, resetFailedUrl, resetSubscriptionManager, s3VideoPreloader, setSentryUserContext, setSentryWorkspaceContext, shuffleArray, simulateApiDelay, skuService, startCoreSessionRecording, stopCoreSessionRecording, storeWorkspaceMapping, streamProxyConfig, subscribeWorkspaceDisplayNames, throttledReloadDashboard, toUrlFriendlyName, trackCoreEvent, trackCorePageView, transformToChartData, updateThreadTitle, upsertWorkspaceDisplayNameInCache, useAccessControl, useActiveBreaks, useActiveLineId, useAllWorkspaceMetrics, useAnalyticsConfig, useAppTimezone, useAudioService, useAuth, useAuthConfig, useAxelNotifications, useCanSaveTargets, useClipFilter, useClipTypes, useClipTypesWithCounts, useClipsInit, useCompanyUsersUsage, useComponentOverride, useCustomConfig, useDashboardConfig, useDashboardMetrics, useDatabaseConfig, useDateFormatter, useDateTimeConfig, useDynamicShiftConfig, useEndpointsConfig, useEntityConfig, useFactoryOverviewMetrics, useFeatureFlags, useFormatNumber, useHasLineAccess, useHideMobileHeader, useHistoricWorkspaceMetrics, useHlsStream, useHlsStreamWithCropping, useHookOverride, useHourEndTimer, useHourlyTargetAchievements, useHourlyTargetMisses, useIdleTimeClipClassifications, useIdleTimeReasons, useLeaderboardMetrics, useLineDetailedMetrics, useLineKPIs, useLineMetrics, useLineShiftConfig, useLineSupervisor, useLineWorkspaceMetrics, useLines, useMessages, useMetrics, useMobileMenu, useMultiLineShiftConfigs, useNavigation, useOperationalShiftKey, useOptionalSupabase, useOverrides, usePageOverride, usePrefetchClipCounts, useRealtimeLineMetrics, useRegistry, useSKUs, useSessionKeepAlive, useSessionTracking, useSessionTrackingContext, useShiftConfig, useShiftGroups, useShifts, useSubscriptionManager, useSubscriptionManagerSafe, useSupabase, useSupabaseClient, useSupervisorsByLineIds, useTargets, useTeamManagementPermissions, useTheme, useThemeConfig, useThreads, useTicketHistory, useTimezoneContext, useUserLineAccess, useUserUsage, useVideoConfig, useWorkspaceConfig, useWorkspaceDetailedMetrics, useWorkspaceDisplayName, useWorkspaceDisplayNames, useWorkspaceDisplayNamesMap, useWorkspaceHealthById, useWorkspaceHealthLastSeen, useWorkspaceHealthStatus, useWorkspaceMetrics, useWorkspaceNavigation, useWorkspaceOperators, useWorkspaceUptimeTimeline, useWorkspaceVideoStreams, userService, videoPrefetchManager, videoPreloader, weeklyTopPerformerService, whatsappService, withAccessControl, withAuth, withRegistry, withTimezone, workspaceHealthService, workspaceService };