@optifye/dashboard-core 6.10.29 → 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.css +62 -92
- package/dist/index.d.mts +49 -1
- package/dist/index.d.ts +49 -1
- package/dist/index.js +682 -240
- package/dist/index.mjs +682 -242
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2569,6 +2569,7 @@ var workspaceService = {
|
|
|
2569
2569
|
}
|
|
2570
2570
|
}
|
|
2571
2571
|
};
|
|
2572
|
+
var DATA_PROCESSING_DELAY_MINUTES = 5;
|
|
2572
2573
|
var WorkspaceHealthService = class _WorkspaceHealthService {
|
|
2573
2574
|
constructor() {
|
|
2574
2575
|
this.cache = /* @__PURE__ */ new Map();
|
|
@@ -2624,9 +2625,11 @@ var WorkspaceHealthService = class _WorkspaceHealthService {
|
|
|
2624
2625
|
const totalMinutes = getShiftDurationMinutes(shiftStartStr, shiftEndStr);
|
|
2625
2626
|
const shiftEndDate = dateFns.addMinutes(shiftStartDate, totalMinutes);
|
|
2626
2627
|
const now4 = /* @__PURE__ */ new Date();
|
|
2627
|
-
let
|
|
2628
|
-
if (
|
|
2629
|
-
if (
|
|
2628
|
+
let rawCompletedMinutes = dateFns.differenceInMinutes(now4 < shiftEndDate ? now4 : shiftEndDate, shiftStartDate);
|
|
2629
|
+
if (rawCompletedMinutes < 0) rawCompletedMinutes = 0;
|
|
2630
|
+
if (rawCompletedMinutes > totalMinutes) rawCompletedMinutes = totalMinutes;
|
|
2631
|
+
const isShiftComplete = shiftEndDate <= now4;
|
|
2632
|
+
const completedMinutes = isShiftComplete ? rawCompletedMinutes : Math.max(0, rawCompletedMinutes - DATA_PROCESSING_DELAY_MINUTES);
|
|
2630
2633
|
const pendingMinutes = Math.max(0, totalMinutes - completedMinutes);
|
|
2631
2634
|
return {
|
|
2632
2635
|
shiftId,
|
|
@@ -2661,7 +2664,8 @@ var WorkspaceHealthService = class _WorkspaceHealthService {
|
|
|
2661
2664
|
} else if (shiftStartDate > now4) {
|
|
2662
2665
|
completedMinutes = 0;
|
|
2663
2666
|
} else {
|
|
2664
|
-
|
|
2667
|
+
const rawCompleted = Math.floor((now4.getTime() - shiftStartDate.getTime()) / (1e3 * 60));
|
|
2668
|
+
completedMinutes = Math.max(0, rawCompleted - DATA_PROCESSING_DELAY_MINUTES);
|
|
2665
2669
|
}
|
|
2666
2670
|
const pendingMinutes = totalMinutes - completedMinutes;
|
|
2667
2671
|
return {
|
|
@@ -4009,7 +4013,7 @@ var initializeCoreMixpanel = (token, debugOrOptions, trackPageViewArg) => {
|
|
|
4009
4013
|
track_pageview: trackPageView ?? true,
|
|
4010
4014
|
persistence: "localStorage"
|
|
4011
4015
|
};
|
|
4012
|
-
const recordSessionsPercent = sessionOpts.recordSessionsPercent ??
|
|
4016
|
+
const recordSessionsPercent = sessionOpts.recordSessionsPercent ?? 20;
|
|
4013
4017
|
initOptions.record_sessions_percent = recordSessionsPercent;
|
|
4014
4018
|
const defaultIdleTimeoutMs = 10 * 60 * 1e3;
|
|
4015
4019
|
const recordIdleTimeoutMs = sessionOpts.recordIdleTimeoutMs ?? (recordSessionsPercent > 0 ? defaultIdleTimeoutMs : void 0);
|
|
@@ -11687,6 +11691,8 @@ var FAILURE_EXPIRY_MS = 5 * 60 * 1e3;
|
|
|
11687
11691
|
var LIVE_RELOAD_MIN_INTERVAL_MS = 15 * 1e3;
|
|
11688
11692
|
var DEFAULT_LIVE_OFFSET_SECONDS = 120;
|
|
11689
11693
|
var DEFAULT_MAX_MANIFEST_AGE_MS = 10 * 60 * 1e3;
|
|
11694
|
+
var SEGMENT_MAX_AGE_MS = 10 * 60 * 1e3;
|
|
11695
|
+
var SEGMENT_TIMESTAMP_REGEX = /(\d{8}T\d{6}Z)(?=\.ts(?:$|[?#]))/;
|
|
11690
11696
|
var STALE_MANIFEST_POLL_INITIAL_DELAY_MS = 15 * 1e3;
|
|
11691
11697
|
var STALE_MANIFEST_POLL_MAX_DELAY_MS = 60 * 1e3;
|
|
11692
11698
|
function resetFailedUrl(url) {
|
|
@@ -11722,6 +11728,8 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
|
|
|
11722
11728
|
});
|
|
11723
11729
|
};
|
|
11724
11730
|
const [restartKey, setRestartKey] = React25.useState(0);
|
|
11731
|
+
const [isStale, setIsStale] = React25.useState(false);
|
|
11732
|
+
const [staleReason, setStaleReason] = React25.useState(null);
|
|
11725
11733
|
const hlsRef = React25.useRef(null);
|
|
11726
11734
|
const stallCheckIntervalRef = React25.useRef(null);
|
|
11727
11735
|
const noProgressTimerRef = React25.useRef(null);
|
|
@@ -11733,6 +11741,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
|
|
|
11733
11741
|
const playRetryTimerRef = React25.useRef(null);
|
|
11734
11742
|
const playRetryCountRef = React25.useRef(0);
|
|
11735
11743
|
const manifestWatchdogRef = React25.useRef(null);
|
|
11744
|
+
const nativeFreshnessIntervalRef = React25.useRef(null);
|
|
11736
11745
|
const lastHiddenAtRef = React25.useRef(null);
|
|
11737
11746
|
const manifestRetryTimerRef = React25.useRef(null);
|
|
11738
11747
|
const manifestRetryDelayRef = React25.useRef(5e3);
|
|
@@ -11757,6 +11766,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
|
|
|
11757
11766
|
const staleManifestEndSnRef = React25.useRef(null);
|
|
11758
11767
|
const staleManifestPollTimerRef = React25.useRef(null);
|
|
11759
11768
|
const staleManifestPollDelayRef = React25.useRef(STALE_MANIFEST_POLL_INITIAL_DELAY_MS);
|
|
11769
|
+
const lastSegmentTimestampMsRef = React25.useRef(null);
|
|
11760
11770
|
const authTokenRef = React25.useRef(null);
|
|
11761
11771
|
const proxyEnabled = process.env.NEXT_PUBLIC_HLS_PROXY_ENABLED === "true";
|
|
11762
11772
|
const proxyBaseUrl = (process.env.NEXT_PUBLIC_HLS_PROXY_URL || "/api/stream").replace(/\/$/, "");
|
|
@@ -11766,6 +11776,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
|
|
|
11766
11776
|
const parsed = raw ? Number(raw) : NaN;
|
|
11767
11777
|
return Number.isFinite(parsed) ? parsed : DEFAULT_MAX_MANIFEST_AGE_MS;
|
|
11768
11778
|
})();
|
|
11779
|
+
const manifestStaleThresholdMs = maxManifestAgeMs > 0 ? Math.min(maxManifestAgeMs, SEGMENT_MAX_AGE_MS) : SEGMENT_MAX_AGE_MS;
|
|
11769
11780
|
const debugLog = (...args) => {
|
|
11770
11781
|
if (debugEnabled) {
|
|
11771
11782
|
console.log(...args);
|
|
@@ -11780,10 +11791,49 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
|
|
|
11780
11791
|
}
|
|
11781
11792
|
return null;
|
|
11782
11793
|
};
|
|
11794
|
+
const parseSegmentTimestampMs = (value) => {
|
|
11795
|
+
const match = value.match(SEGMENT_TIMESTAMP_REGEX);
|
|
11796
|
+
if (!match) return null;
|
|
11797
|
+
const stamp = match[1];
|
|
11798
|
+
const year = Number(stamp.slice(0, 4));
|
|
11799
|
+
const month = Number(stamp.slice(4, 6));
|
|
11800
|
+
const day = Number(stamp.slice(6, 8));
|
|
11801
|
+
const hour = Number(stamp.slice(9, 11));
|
|
11802
|
+
const minute = Number(stamp.slice(11, 13));
|
|
11803
|
+
const second = Number(stamp.slice(13, 15));
|
|
11804
|
+
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) {
|
|
11805
|
+
return null;
|
|
11806
|
+
}
|
|
11807
|
+
const timestampMs = Date.UTC(year, month - 1, day, hour, minute, second);
|
|
11808
|
+
return Number.isNaN(timestampMs) ? null : timestampMs;
|
|
11809
|
+
};
|
|
11810
|
+
const getFragmentTimestampMs = (fragment, enforceSegmentAge = false) => {
|
|
11811
|
+
if (fragment?.relurl) {
|
|
11812
|
+
const parsed = parseSegmentTimestampMs(fragment.relurl);
|
|
11813
|
+
if (parsed !== null) return parsed;
|
|
11814
|
+
}
|
|
11815
|
+
if (fragment?.url) {
|
|
11816
|
+
const parsed = parseSegmentTimestampMs(fragment.url);
|
|
11817
|
+
if (parsed !== null) return parsed;
|
|
11818
|
+
}
|
|
11819
|
+
if (enforceSegmentAge) return null;
|
|
11820
|
+
return getProgramDateTimeMs(fragment?.programDateTime);
|
|
11821
|
+
};
|
|
11822
|
+
const getLatestFragmentTimestampMs = (fragments, enforceSegmentAge = false) => {
|
|
11823
|
+
if (!Array.isArray(fragments) || fragments.length === 0) return null;
|
|
11824
|
+
for (let i = fragments.length - 1; i >= 0; i -= 1) {
|
|
11825
|
+
const timestampMs = getFragmentTimestampMs(fragments[i], enforceSegmentAge);
|
|
11826
|
+
if (timestampMs !== null) {
|
|
11827
|
+
return timestampMs;
|
|
11828
|
+
}
|
|
11829
|
+
}
|
|
11830
|
+
return null;
|
|
11831
|
+
};
|
|
11783
11832
|
const parseManifestStatus = (manifestText) => {
|
|
11784
11833
|
let mediaSequence = null;
|
|
11785
11834
|
let segmentCount = 0;
|
|
11786
11835
|
let lastProgramDateTimeMs = null;
|
|
11836
|
+
let lastSegmentTimestampMs = null;
|
|
11787
11837
|
const lines = manifestText.split(/\r?\n/);
|
|
11788
11838
|
for (const rawLine of lines) {
|
|
11789
11839
|
const line = rawLine.trim();
|
|
@@ -11801,13 +11851,60 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
|
|
|
11801
11851
|
}
|
|
11802
11852
|
} else if (line.startsWith("#EXTINF:")) {
|
|
11803
11853
|
segmentCount += 1;
|
|
11854
|
+
} else if (!line.startsWith("#")) {
|
|
11855
|
+
const segmentTimestampMs = parseSegmentTimestampMs(line);
|
|
11856
|
+
if (segmentTimestampMs !== null) {
|
|
11857
|
+
lastSegmentTimestampMs = segmentTimestampMs;
|
|
11858
|
+
}
|
|
11804
11859
|
}
|
|
11805
11860
|
}
|
|
11806
11861
|
let lastSequenceNumber = null;
|
|
11807
11862
|
if (mediaSequence !== null && segmentCount > 0) {
|
|
11808
11863
|
lastSequenceNumber = mediaSequence + segmentCount - 1;
|
|
11809
11864
|
}
|
|
11810
|
-
return { lastProgramDateTimeMs, lastSequenceNumber };
|
|
11865
|
+
return { lastProgramDateTimeMs, lastSequenceNumber, lastSegmentTimestampMs };
|
|
11866
|
+
};
|
|
11867
|
+
const evaluateSegmentFreshness = ({
|
|
11868
|
+
segmentTimestampMs,
|
|
11869
|
+
fallbackTimestampMs,
|
|
11870
|
+
enforceSegmentAge
|
|
11871
|
+
}) => {
|
|
11872
|
+
if (segmentTimestampMs !== null) {
|
|
11873
|
+
const ageMs = Date.now() - segmentTimestampMs;
|
|
11874
|
+
return {
|
|
11875
|
+
isFresh: ageMs <= SEGMENT_MAX_AGE_MS,
|
|
11876
|
+
ageMs,
|
|
11877
|
+
reason: `segment age ${Math.round(ageMs / 1e3)}s`
|
|
11878
|
+
};
|
|
11879
|
+
}
|
|
11880
|
+
if (enforceSegmentAge) {
|
|
11881
|
+
return { isFresh: false, ageMs: null, reason: "segment timestamp missing" };
|
|
11882
|
+
}
|
|
11883
|
+
if (fallbackTimestampMs !== null) {
|
|
11884
|
+
const ageMs = Date.now() - fallbackTimestampMs;
|
|
11885
|
+
return {
|
|
11886
|
+
isFresh: ageMs <= SEGMENT_MAX_AGE_MS,
|
|
11887
|
+
ageMs,
|
|
11888
|
+
reason: `program date time age ${Math.round(ageMs / 1e3)}s`
|
|
11889
|
+
};
|
|
11890
|
+
}
|
|
11891
|
+
return { isFresh: true, ageMs: null, reason: null };
|
|
11892
|
+
};
|
|
11893
|
+
const fetchManifestStatus = async (manifestUrl) => {
|
|
11894
|
+
const headers = {};
|
|
11895
|
+
if (authTokenRef.current) {
|
|
11896
|
+
headers.Authorization = `Bearer ${authTokenRef.current}`;
|
|
11897
|
+
}
|
|
11898
|
+
const response = await fetch(buildCacheBustedUrl(manifestUrl), {
|
|
11899
|
+
method: "GET",
|
|
11900
|
+
cache: "no-store",
|
|
11901
|
+
headers
|
|
11902
|
+
});
|
|
11903
|
+
if (!response.ok) {
|
|
11904
|
+
throw new Error(`Manifest fetch failed: ${response.status}`);
|
|
11905
|
+
}
|
|
11906
|
+
const manifestText = await response.text();
|
|
11907
|
+
return parseManifestStatus(manifestText);
|
|
11811
11908
|
};
|
|
11812
11909
|
const stopStaleManifestPolling = () => {
|
|
11813
11910
|
if (staleManifestPollTimerRef.current) {
|
|
@@ -11818,6 +11915,8 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
|
|
|
11818
11915
|
staleManifestUrlRef.current = null;
|
|
11819
11916
|
staleManifestEndSnRef.current = null;
|
|
11820
11917
|
staleManifestPollDelayRef.current = STALE_MANIFEST_POLL_INITIAL_DELAY_MS;
|
|
11918
|
+
setIsStale(false);
|
|
11919
|
+
setStaleReason(null);
|
|
11821
11920
|
};
|
|
11822
11921
|
const pollStaleManifestOnce = async () => {
|
|
11823
11922
|
if (!staleManifestTriggeredRef.current) return;
|
|
@@ -11831,25 +11930,24 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
|
|
|
11831
11930
|
return;
|
|
11832
11931
|
}
|
|
11833
11932
|
try {
|
|
11834
|
-
const
|
|
11835
|
-
|
|
11836
|
-
|
|
11837
|
-
|
|
11838
|
-
|
|
11839
|
-
|
|
11840
|
-
|
|
11841
|
-
|
|
11933
|
+
const {
|
|
11934
|
+
lastProgramDateTimeMs,
|
|
11935
|
+
lastSequenceNumber,
|
|
11936
|
+
lastSegmentTimestampMs
|
|
11937
|
+
} = await fetchManifestStatus(manifestUrl);
|
|
11938
|
+
if (lastSegmentTimestampMs !== null) {
|
|
11939
|
+
lastSegmentTimestampMsRef.current = lastSegmentTimestampMs;
|
|
11940
|
+
}
|
|
11941
|
+
const enforceSegmentAge = isR2StreamRef.current;
|
|
11942
|
+
const hasAnyTimestamp = lastSegmentTimestampMs !== null || lastProgramDateTimeMs !== null;
|
|
11943
|
+
const freshness = evaluateSegmentFreshness({
|
|
11944
|
+
segmentTimestampMs: lastSegmentTimestampMs,
|
|
11945
|
+
fallbackTimestampMs: lastProgramDateTimeMs,
|
|
11946
|
+
enforceSegmentAge
|
|
11842
11947
|
});
|
|
11843
|
-
if (!response.ok) {
|
|
11844
|
-
throw new Error(`Manifest poll failed: ${response.status}`);
|
|
11845
|
-
}
|
|
11846
|
-
const manifestText = await response.text();
|
|
11847
|
-
const { lastProgramDateTimeMs, lastSequenceNumber } = parseManifestStatus(manifestText);
|
|
11848
|
-
const now4 = Date.now();
|
|
11849
|
-
const isFreshByProgramDateTime = lastProgramDateTimeMs !== null && now4 - lastProgramDateTimeMs <= maxManifestAgeMs;
|
|
11850
11948
|
const priorEndSn = staleManifestEndSnRef.current;
|
|
11851
11949
|
const isSequenceAdvanced = typeof lastSequenceNumber === "number" && (priorEndSn === null || lastSequenceNumber > priorEndSn);
|
|
11852
|
-
if (
|
|
11950
|
+
if (freshness.isFresh || !enforceSegmentAge && !hasAnyTimestamp && isSequenceAdvanced) {
|
|
11853
11951
|
stopStaleManifestPolling();
|
|
11854
11952
|
if (hlsRef.current) {
|
|
11855
11953
|
hlsRef.current.startLoad(-1);
|
|
@@ -11884,6 +11982,8 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
|
|
|
11884
11982
|
staleManifestUrlRef.current = activeStreamUrlRef.current || latestSrcRef.current;
|
|
11885
11983
|
staleManifestEndSnRef.current = lastManifestEndSnRef.current;
|
|
11886
11984
|
staleManifestPollDelayRef.current = STALE_MANIFEST_POLL_INITIAL_DELAY_MS;
|
|
11985
|
+
setIsStale(true);
|
|
11986
|
+
setStaleReason(reason);
|
|
11887
11987
|
const hls = hlsRef.current;
|
|
11888
11988
|
if (hls) {
|
|
11889
11989
|
hls.stopLoad();
|
|
@@ -11941,6 +12041,10 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
|
|
|
11941
12041
|
clearInterval(manifestWatchdogRef.current);
|
|
11942
12042
|
manifestWatchdogRef.current = null;
|
|
11943
12043
|
}
|
|
12044
|
+
if (nativeFreshnessIntervalRef.current) {
|
|
12045
|
+
clearInterval(nativeFreshnessIntervalRef.current);
|
|
12046
|
+
nativeFreshnessIntervalRef.current = null;
|
|
12047
|
+
}
|
|
11944
12048
|
if (manifestRetryTimerRef.current) {
|
|
11945
12049
|
clearTimeout(manifestRetryTimerRef.current);
|
|
11946
12050
|
manifestRetryTimerRef.current = null;
|
|
@@ -11965,6 +12069,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
|
|
|
11965
12069
|
lastManifestEndSnRef.current = null;
|
|
11966
12070
|
lastManifestEndSnUpdatedAtRef.current = null;
|
|
11967
12071
|
staleManifestTriggeredRef.current = false;
|
|
12072
|
+
lastSegmentTimestampMsRef.current = null;
|
|
11968
12073
|
manifestRetryDelayRef.current = 5e3;
|
|
11969
12074
|
playRetryCountRef.current = 0;
|
|
11970
12075
|
if (hlsRef.current) {
|
|
@@ -12084,6 +12189,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
|
|
|
12084
12189
|
const attemptPlay = (reason) => {
|
|
12085
12190
|
const video = videoRef.current;
|
|
12086
12191
|
if (!video || !shouldPlayRef.current) return;
|
|
12192
|
+
if (staleManifestTriggeredRef.current) return;
|
|
12087
12193
|
if (!video.paused || video.seeking) return;
|
|
12088
12194
|
if (video.readyState < 2) return;
|
|
12089
12195
|
video.play().then(() => {
|
|
@@ -12109,7 +12215,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
|
|
|
12109
12215
|
lastHiddenAtRef.current = null;
|
|
12110
12216
|
if (!lastHiddenAt) return;
|
|
12111
12217
|
if (Date.now() - lastHiddenAt < 3e4) return;
|
|
12112
|
-
refreshLiveStream("tab visible after idle");
|
|
12218
|
+
void refreshLiveStream("tab visible after idle");
|
|
12113
12219
|
attemptPlay();
|
|
12114
12220
|
};
|
|
12115
12221
|
const startManifestWatchdog = () => {
|
|
@@ -12185,7 +12291,38 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
|
|
|
12185
12291
|
return `${url}${separator}ts=${Date.now()}`;
|
|
12186
12292
|
}
|
|
12187
12293
|
};
|
|
12188
|
-
const
|
|
12294
|
+
const ensureManifestFreshness = async (manifestUrl, enforceSegmentAge, reason) => {
|
|
12295
|
+
try {
|
|
12296
|
+
const status = await fetchManifestStatus(manifestUrl);
|
|
12297
|
+
if (status.lastSegmentTimestampMs !== null) {
|
|
12298
|
+
lastSegmentTimestampMsRef.current = status.lastSegmentTimestampMs;
|
|
12299
|
+
}
|
|
12300
|
+
const freshness = evaluateSegmentFreshness({
|
|
12301
|
+
segmentTimestampMs: status.lastSegmentTimestampMs,
|
|
12302
|
+
fallbackTimestampMs: status.lastProgramDateTimeMs,
|
|
12303
|
+
enforceSegmentAge
|
|
12304
|
+
});
|
|
12305
|
+
if (!freshness.isFresh) {
|
|
12306
|
+
markStaleStream(freshness.reason || reason);
|
|
12307
|
+
return false;
|
|
12308
|
+
}
|
|
12309
|
+
return true;
|
|
12310
|
+
} catch (error) {
|
|
12311
|
+
debugLog("[HLS] Manifest freshness check failed", error);
|
|
12312
|
+
{
|
|
12313
|
+
markStaleStream("manifest freshness check failed");
|
|
12314
|
+
return false;
|
|
12315
|
+
}
|
|
12316
|
+
}
|
|
12317
|
+
};
|
|
12318
|
+
const startNativeFreshnessMonitor = (manifestUrl) => {
|
|
12319
|
+
if (nativeFreshnessIntervalRef.current) return;
|
|
12320
|
+
nativeFreshnessIntervalRef.current = setInterval(() => {
|
|
12321
|
+
if (staleManifestTriggeredRef.current) return;
|
|
12322
|
+
void ensureManifestFreshness(manifestUrl, true, "native freshness check");
|
|
12323
|
+
}, 3e4);
|
|
12324
|
+
};
|
|
12325
|
+
const refreshLiveStream = async (reason) => {
|
|
12189
12326
|
if (!isR2StreamRef.current) return;
|
|
12190
12327
|
const now4 = Date.now();
|
|
12191
12328
|
if (now4 - lastLiveReloadRef.current < LIVE_RELOAD_MIN_INTERVAL_MS) {
|
|
@@ -12201,6 +12338,10 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
|
|
|
12201
12338
|
}
|
|
12202
12339
|
if (video && nativeStreamUrlRef.current) {
|
|
12203
12340
|
console.log(`[HLS] Native live reload (${reason})`);
|
|
12341
|
+
const isFresh = await ensureManifestFreshness(nativeStreamUrlRef.current, true, "native live reload");
|
|
12342
|
+
if (!isFresh) {
|
|
12343
|
+
return;
|
|
12344
|
+
}
|
|
12204
12345
|
const refreshedUrl = buildCacheBustedUrl(nativeStreamUrlRef.current);
|
|
12205
12346
|
video.src = refreshedUrl;
|
|
12206
12347
|
video.load();
|
|
@@ -12292,7 +12433,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
|
|
|
12292
12433
|
};
|
|
12293
12434
|
const handleEnded = () => {
|
|
12294
12435
|
if (isNativeHlsRef.current) {
|
|
12295
|
-
refreshLiveStream("ended");
|
|
12436
|
+
void refreshLiveStream("ended");
|
|
12296
12437
|
return;
|
|
12297
12438
|
}
|
|
12298
12439
|
if (isR2StreamRef.current) {
|
|
@@ -12306,7 +12447,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
|
|
|
12306
12447
|
if (isNativeHlsRef.current) {
|
|
12307
12448
|
if (!isR2StreamRef.current) return;
|
|
12308
12449
|
waitingTimerRef.current = setTimeout(() => {
|
|
12309
|
-
refreshLiveStream("native waiting timeout");
|
|
12450
|
+
void refreshLiveStream("native waiting timeout");
|
|
12310
12451
|
}, getWaitingTimeoutMs());
|
|
12311
12452
|
return;
|
|
12312
12453
|
}
|
|
@@ -12429,12 +12570,17 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
|
|
|
12429
12570
|
nativeStreamUrlRef.current = resolvedSrc;
|
|
12430
12571
|
activeStreamUrlRef.current = resolvedSrc;
|
|
12431
12572
|
console.log("[HLS] Using proxy playlist for Safari R2 stream");
|
|
12573
|
+
const isFresh = await ensureManifestFreshness(resolvedSrc, true, "native proxy start");
|
|
12574
|
+
if (!isFresh) {
|
|
12575
|
+
return;
|
|
12576
|
+
}
|
|
12432
12577
|
video.src = resolvedSrc;
|
|
12433
12578
|
video.addEventListener("waiting", handleWaiting);
|
|
12434
12579
|
video.addEventListener("loadedmetadata", handleLoadedMetadata);
|
|
12435
12580
|
video.addEventListener("canplay", handleCanPlay);
|
|
12436
12581
|
video.addEventListener("ended", handleEnded);
|
|
12437
12582
|
video.addEventListener("error", handleNativeError);
|
|
12583
|
+
startNativeFreshnessMonitor(resolvedSrc);
|
|
12438
12584
|
attemptPlay();
|
|
12439
12585
|
return;
|
|
12440
12586
|
}
|
|
@@ -12602,20 +12748,29 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
|
|
|
12602
12748
|
}
|
|
12603
12749
|
}
|
|
12604
12750
|
}
|
|
12605
|
-
if (
|
|
12751
|
+
if (!details.endList) {
|
|
12606
12752
|
const now4 = Date.now();
|
|
12607
12753
|
const fragments = Array.isArray(details.fragments) ? details.fragments : [];
|
|
12608
12754
|
const lastFragment = fragments.length ? fragments[fragments.length - 1] : void 0;
|
|
12609
|
-
const
|
|
12610
|
-
|
|
12611
|
-
|
|
12755
|
+
const segmentTimestampMs = getLatestFragmentTimestampMs(fragments, true);
|
|
12756
|
+
const enforceSegmentAge = isR2StreamRef.current;
|
|
12757
|
+
if (segmentTimestampMs !== null) {
|
|
12758
|
+
lastSegmentTimestampMsRef.current = segmentTimestampMs;
|
|
12759
|
+
}
|
|
12760
|
+
const freshness = evaluateSegmentFreshness({
|
|
12761
|
+
segmentTimestampMs,
|
|
12762
|
+
fallbackTimestampMs: getProgramDateTimeMs(lastFragment?.programDateTime),
|
|
12763
|
+
enforceSegmentAge
|
|
12764
|
+
});
|
|
12765
|
+
if (!freshness.isFresh) {
|
|
12766
|
+
markStaleStream(freshness.reason || "segment stale");
|
|
12612
12767
|
return;
|
|
12613
12768
|
}
|
|
12614
12769
|
const endSn = typeof details.endSN === "number" ? details.endSN : lastFragment?.sn;
|
|
12615
12770
|
if (typeof endSn === "number") {
|
|
12616
12771
|
if (lastManifestEndSnRef.current === endSn) {
|
|
12617
12772
|
const lastUpdatedAt = lastManifestEndSnUpdatedAtRef.current;
|
|
12618
|
-
if (lastUpdatedAt && now4 - lastUpdatedAt >
|
|
12773
|
+
if (lastUpdatedAt && now4 - lastUpdatedAt > manifestStaleThresholdMs) {
|
|
12619
12774
|
markStaleStream(`sequence stalled at ${endSn}`);
|
|
12620
12775
|
return;
|
|
12621
12776
|
}
|
|
@@ -12640,6 +12795,27 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
|
|
|
12640
12795
|
lastManifestLoadRef.current = Date.now();
|
|
12641
12796
|
resetManifestRetry();
|
|
12642
12797
|
});
|
|
12798
|
+
hls.on(Hls__default.default.Events.FRAG_LOADING, (_event, data) => {
|
|
12799
|
+
if (staleManifestTriggeredRef.current) return;
|
|
12800
|
+
const frag = data?.frag;
|
|
12801
|
+
if (!frag || frag.sn === "initSegment") return;
|
|
12802
|
+
const enforceSegmentAge = isR2StreamRef.current;
|
|
12803
|
+
const segmentTimestampMs = getFragmentTimestampMs(frag, true);
|
|
12804
|
+
if (segmentTimestampMs !== null) {
|
|
12805
|
+
lastSegmentTimestampMsRef.current = segmentTimestampMs;
|
|
12806
|
+
}
|
|
12807
|
+
const freshness = evaluateSegmentFreshness({
|
|
12808
|
+
segmentTimestampMs,
|
|
12809
|
+
fallbackTimestampMs: getProgramDateTimeMs(frag.programDateTime),
|
|
12810
|
+
enforceSegmentAge
|
|
12811
|
+
});
|
|
12812
|
+
if (!freshness.isFresh) {
|
|
12813
|
+
if (frag.loader?.abort) {
|
|
12814
|
+
frag.loader.abort();
|
|
12815
|
+
}
|
|
12816
|
+
markStaleStream(freshness.reason || "segment stale");
|
|
12817
|
+
}
|
|
12818
|
+
});
|
|
12643
12819
|
hls.on(Hls__default.default.Events.FRAG_LOADED, (_event, data) => {
|
|
12644
12820
|
if (!isR2Stream) return;
|
|
12645
12821
|
lastFragLoadRef.current = Date.now();
|
|
@@ -12664,9 +12840,15 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
|
|
|
12664
12840
|
if (canUseNative) {
|
|
12665
12841
|
isNativeHlsRef.current = true;
|
|
12666
12842
|
console.log("[HLS] Using native HLS");
|
|
12667
|
-
video.src = resolvedSrc;
|
|
12668
12843
|
nativeStreamUrlRef.current = resolvedSrc;
|
|
12669
12844
|
activeStreamUrlRef.current = resolvedSrc;
|
|
12845
|
+
if (isR2Stream) {
|
|
12846
|
+
const isFresh = await ensureManifestFreshness(resolvedSrc, true, "native start");
|
|
12847
|
+
if (!isFresh) {
|
|
12848
|
+
return;
|
|
12849
|
+
}
|
|
12850
|
+
}
|
|
12851
|
+
video.src = resolvedSrc;
|
|
12670
12852
|
video.addEventListener("waiting", handleWaiting);
|
|
12671
12853
|
video.addEventListener("loadedmetadata", handleLoadedMetadata);
|
|
12672
12854
|
video.addEventListener("canplay", handleCanPlay);
|
|
@@ -12674,6 +12856,9 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
|
|
|
12674
12856
|
video.addEventListener("error", handleNativeError);
|
|
12675
12857
|
startPlaybackGovernor();
|
|
12676
12858
|
startManifestWatchdog();
|
|
12859
|
+
if (isR2Stream) {
|
|
12860
|
+
startNativeFreshnessMonitor(resolvedSrc);
|
|
12861
|
+
}
|
|
12677
12862
|
attemptPlay();
|
|
12678
12863
|
} else {
|
|
12679
12864
|
console.error("[HLS] HLS not supported");
|
|
@@ -12687,7 +12872,10 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
|
|
|
12687
12872
|
}, [src, shouldPlay, restartKey, isPermanentlyFailed]);
|
|
12688
12873
|
return {
|
|
12689
12874
|
restartKey,
|
|
12690
|
-
isNativeHls: isNativeHlsRef.current
|
|
12875
|
+
isNativeHls: isNativeHlsRef.current,
|
|
12876
|
+
isStale,
|
|
12877
|
+
staleReason,
|
|
12878
|
+
lastSegmentTimestampMs: lastSegmentTimestampMsRef.current
|
|
12691
12879
|
};
|
|
12692
12880
|
}
|
|
12693
12881
|
function useHlsStreamWithCropping(videoRef, canvasRef, options) {
|
|
@@ -14823,7 +15011,77 @@ function getNextUpdateInterval(timestamp) {
|
|
|
14823
15011
|
}
|
|
14824
15012
|
}
|
|
14825
15013
|
|
|
14826
|
-
// src/lib/hooks/
|
|
15014
|
+
// src/lib/hooks/useWorkspaceHealthLastSeen.ts
|
|
15015
|
+
var DEFAULT_REFRESH_INTERVAL_MS = 3e4;
|
|
15016
|
+
var useWorkspaceHealthLastSeen = (workspaceIds, options = {}) => {
|
|
15017
|
+
const supabase = useSupabase();
|
|
15018
|
+
const databaseConfig = useDatabaseConfig();
|
|
15019
|
+
const refreshInterval = options.refreshInterval ?? DEFAULT_REFRESH_INTERVAL_MS;
|
|
15020
|
+
const workspaceIdsKey = React25.useMemo(() => {
|
|
15021
|
+
const ids = Array.from(new Set(workspaceIds.filter(Boolean)));
|
|
15022
|
+
return ids.sort().join(",");
|
|
15023
|
+
}, [workspaceIds]);
|
|
15024
|
+
const [lastSeenByWorkspaceId, setLastSeenByWorkspaceId] = React25.useState({});
|
|
15025
|
+
const [isLoading, setIsLoading] = React25.useState(Boolean(workspaceIdsKey));
|
|
15026
|
+
const [error, setError] = React25.useState(null);
|
|
15027
|
+
const isFetchingRef = React25.useRef(false);
|
|
15028
|
+
const refreshIntervalRef = React25.useRef(null);
|
|
15029
|
+
const fetchLastSeen = React25.useCallback(async () => {
|
|
15030
|
+
if (!supabase || !workspaceIdsKey || isFetchingRef.current) return;
|
|
15031
|
+
const healthTable = databaseConfig?.tables?.workspace_health || "workspace_health_status";
|
|
15032
|
+
try {
|
|
15033
|
+
isFetchingRef.current = true;
|
|
15034
|
+
setIsLoading(true);
|
|
15035
|
+
setError(null);
|
|
15036
|
+
const workspaceIdsList = workspaceIdsKey.split(",").filter(Boolean);
|
|
15037
|
+
if (!workspaceIdsList.length) {
|
|
15038
|
+
setLastSeenByWorkspaceId({});
|
|
15039
|
+
return;
|
|
15040
|
+
}
|
|
15041
|
+
const { data, error: fetchError } = await supabase.from(healthTable).select("workspace_id,last_heartbeat,is_healthy").in("workspace_id", workspaceIdsList);
|
|
15042
|
+
if (fetchError) throw fetchError;
|
|
15043
|
+
const nextMap = {};
|
|
15044
|
+
(data || []).forEach((row) => {
|
|
15045
|
+
if (!row?.workspace_id) return;
|
|
15046
|
+
const lastHeartbeat = row.last_heartbeat || null;
|
|
15047
|
+
nextMap[row.workspace_id] = {
|
|
15048
|
+
lastHeartbeat,
|
|
15049
|
+
timeSinceLastUpdate: formatRelativeTime(lastHeartbeat),
|
|
15050
|
+
isHealthy: Boolean(row.is_healthy)
|
|
15051
|
+
};
|
|
15052
|
+
});
|
|
15053
|
+
setLastSeenByWorkspaceId(nextMap);
|
|
15054
|
+
} catch (err) {
|
|
15055
|
+
console.error("[useWorkspaceHealthLastSeen] Error fetching workspace health:", err);
|
|
15056
|
+
setError(err?.message || "Failed to load workspace health");
|
|
15057
|
+
setLastSeenByWorkspaceId({});
|
|
15058
|
+
} finally {
|
|
15059
|
+
setIsLoading(false);
|
|
15060
|
+
isFetchingRef.current = false;
|
|
15061
|
+
}
|
|
15062
|
+
}, [supabase, workspaceIdsKey, databaseConfig?.tables?.workspace_health]);
|
|
15063
|
+
React25.useEffect(() => {
|
|
15064
|
+
fetchLastSeen();
|
|
15065
|
+
}, [fetchLastSeen]);
|
|
15066
|
+
React25.useEffect(() => {
|
|
15067
|
+
if (!refreshInterval || !workspaceIdsKey) return;
|
|
15068
|
+
refreshIntervalRef.current = setInterval(() => {
|
|
15069
|
+
fetchLastSeen();
|
|
15070
|
+
}, refreshInterval);
|
|
15071
|
+
return () => {
|
|
15072
|
+
if (refreshIntervalRef.current) {
|
|
15073
|
+
clearInterval(refreshIntervalRef.current);
|
|
15074
|
+
refreshIntervalRef.current = null;
|
|
15075
|
+
}
|
|
15076
|
+
};
|
|
15077
|
+
}, [fetchLastSeen, refreshInterval, workspaceIdsKey]);
|
|
15078
|
+
return {
|
|
15079
|
+
lastSeenByWorkspaceId,
|
|
15080
|
+
isLoading,
|
|
15081
|
+
error,
|
|
15082
|
+
refetch: fetchLastSeen
|
|
15083
|
+
};
|
|
15084
|
+
};
|
|
14827
15085
|
var useWorkspaceHealthStatus = (workspaceId) => {
|
|
14828
15086
|
const supabase = useSupabase();
|
|
14829
15087
|
const databaseConfig = useDatabaseConfig();
|
|
@@ -15504,6 +15762,7 @@ var useSupervisorsByLineIds = (lineIds, options) => {
|
|
|
15504
15762
|
const targetLineIdSet = React25.useMemo(() => new Set(lineIds || []), [lineIdsKey]);
|
|
15505
15763
|
const [supervisorsByLineId, setSupervisorsByLineId] = React25.useState(/* @__PURE__ */ new Map());
|
|
15506
15764
|
const [supervisorNamesByLineId, setSupervisorNamesByLineId] = React25.useState(/* @__PURE__ */ new Map());
|
|
15765
|
+
const [allSupervisorsMap, setAllSupervisorsMap] = React25.useState(/* @__PURE__ */ new Map());
|
|
15507
15766
|
const [isLoading, setIsLoading] = React25.useState(true);
|
|
15508
15767
|
const [error, setError] = React25.useState(null);
|
|
15509
15768
|
const fetchSupervisors = React25.useCallback(async () => {
|
|
@@ -15519,9 +15778,9 @@ var useSupervisorsByLineIds = (lineIds, options) => {
|
|
|
15519
15778
|
throw fetchError;
|
|
15520
15779
|
}
|
|
15521
15780
|
const nextSupervisorsByLineId = /* @__PURE__ */ new Map();
|
|
15781
|
+
const nextAllSupervisorsMap = /* @__PURE__ */ new Map();
|
|
15522
15782
|
(data || []).forEach((row) => {
|
|
15523
15783
|
const assignedLineIds = row?.properties?.line_id || row?.properties?.line_ids || [];
|
|
15524
|
-
if (!Array.isArray(assignedLineIds) || assignedLineIds.length === 0) return;
|
|
15525
15784
|
const email = row.email || "";
|
|
15526
15785
|
let displayName;
|
|
15527
15786
|
if (row.first_name) {
|
|
@@ -15535,6 +15794,8 @@ var useSupervisorsByLineIds = (lineIds, options) => {
|
|
|
15535
15794
|
displayName,
|
|
15536
15795
|
profilePhotoUrl: row.profile_photo_url
|
|
15537
15796
|
};
|
|
15797
|
+
nextAllSupervisorsMap.set(supervisor.userId, supervisor);
|
|
15798
|
+
if (!Array.isArray(assignedLineIds) || assignedLineIds.length === 0) return;
|
|
15538
15799
|
assignedLineIds.forEach((lineId) => {
|
|
15539
15800
|
if (typeof lineId !== "string") return;
|
|
15540
15801
|
if (targetLineIdSet.size > 0 && !targetLineIdSet.has(lineId)) return;
|
|
@@ -15549,10 +15810,12 @@ var useSupervisorsByLineIds = (lineIds, options) => {
|
|
|
15549
15810
|
});
|
|
15550
15811
|
setSupervisorsByLineId(nextSupervisorsByLineId);
|
|
15551
15812
|
setSupervisorNamesByLineId(nextSupervisorNamesByLineId);
|
|
15813
|
+
setAllSupervisorsMap(nextAllSupervisorsMap);
|
|
15552
15814
|
} catch (err) {
|
|
15553
15815
|
setError(err instanceof Error ? err : new Error("Failed to fetch supervisors"));
|
|
15554
15816
|
setSupervisorsByLineId(/* @__PURE__ */ new Map());
|
|
15555
15817
|
setSupervisorNamesByLineId(/* @__PURE__ */ new Map());
|
|
15818
|
+
setAllSupervisorsMap(/* @__PURE__ */ new Map());
|
|
15556
15819
|
} finally {
|
|
15557
15820
|
setIsLoading(false);
|
|
15558
15821
|
}
|
|
@@ -15577,6 +15840,7 @@ var useSupervisorsByLineIds = (lineIds, options) => {
|
|
|
15577
15840
|
return {
|
|
15578
15841
|
supervisorNamesByLineId,
|
|
15579
15842
|
supervisorsByLineId,
|
|
15843
|
+
allSupervisorsMap,
|
|
15580
15844
|
isLoading,
|
|
15581
15845
|
error,
|
|
15582
15846
|
refetch: fetchSupervisors
|
|
@@ -29782,13 +30046,14 @@ var VideoCard = React25__namespace.default.memo(({
|
|
|
29782
30046
|
className = "",
|
|
29783
30047
|
compact = false,
|
|
29784
30048
|
displayName,
|
|
30049
|
+
lastSeenLabel,
|
|
29785
30050
|
onMouseEnter,
|
|
29786
30051
|
onMouseLeave
|
|
29787
30052
|
}) => {
|
|
29788
30053
|
const videoRef = React25.useRef(null);
|
|
29789
30054
|
const canvasRef = React25.useRef(null);
|
|
29790
30055
|
const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;
|
|
29791
|
-
useHlsStreamWithCropping(videoRef, canvasRef, {
|
|
30056
|
+
const { isStale: isStreamStale } = useHlsStreamWithCropping(videoRef, canvasRef, {
|
|
29792
30057
|
src: hlsUrl,
|
|
29793
30058
|
shouldPlay,
|
|
29794
30059
|
cropping,
|
|
@@ -29796,6 +30061,8 @@ var VideoCard = React25__namespace.default.memo(({
|
|
|
29796
30061
|
useRAF,
|
|
29797
30062
|
onFatalError: onFatalError ?? (() => throttledReloadDashboard())
|
|
29798
30063
|
});
|
|
30064
|
+
const showOffline = Boolean(isStreamStale);
|
|
30065
|
+
const lastSeenText = lastSeenLabel || "Unknown";
|
|
29799
30066
|
const workspaceDisplayName = displayName || getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
|
|
29800
30067
|
const efficiencyColor = getEfficiencyColor(workspace.efficiency, effectiveLegend);
|
|
29801
30068
|
const efficiencyOverlayClass = efficiencyColor === "green" ? "bg-[#00D654]/25" : efficiencyColor === "yellow" ? "bg-[#FFD700]/30" : "bg-[#FF2D0A]/30";
|
|
@@ -29865,6 +30132,14 @@ var VideoCard = React25__namespace.default.memo(({
|
|
|
29865
30132
|
)
|
|
29866
30133
|
] }),
|
|
29867
30134
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: `absolute inset-0 z-20 pointer-events-none ${efficiencyOverlayClass}` }),
|
|
30135
|
+
showOffline && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 z-40 flex items-center justify-center bg-black/70 px-2 text-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-1", children: [
|
|
30136
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: `${compact ? "w-4 h-4" : "w-5 h-5"} text-amber-300` }),
|
|
30137
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: `font-semibold text-white ${compact ? "text-[11px]" : "text-xs"}`, children: "Not streaming" }),
|
|
30138
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: `text-gray-200 ${compact ? "text-[10px]" : "text-[11px]"}`, children: [
|
|
30139
|
+
"Last seen: ",
|
|
30140
|
+
lastSeenText
|
|
30141
|
+
] })
|
|
30142
|
+
] }) }),
|
|
29868
30143
|
/* @__PURE__ */ jsxRuntime.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: [
|
|
29869
30144
|
Math.round(workspace.efficiency),
|
|
29870
30145
|
"%"
|
|
@@ -29891,8 +30166,13 @@ var VideoCard = React25__namespace.default.memo(({
|
|
|
29891
30166
|
children: trendInfo.arrow
|
|
29892
30167
|
}
|
|
29893
30168
|
),
|
|
29894
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
29895
|
-
|
|
30169
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
30170
|
+
"div",
|
|
30171
|
+
{
|
|
30172
|
+
className: `${compact ? "w-1 h-1" : "w-1.5 h-1.5"} rounded-full ${showOffline ? "bg-red-500" : "bg-green-500"}`
|
|
30173
|
+
}
|
|
30174
|
+
),
|
|
30175
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: `text-white text-[11px] sm:${compact ? "text-[10px]" : "text-xs"}`, children: showOffline ? "Offline" : "Live" })
|
|
29896
30176
|
] })
|
|
29897
30177
|
] })
|
|
29898
30178
|
]
|
|
@@ -29908,6 +30188,9 @@ var VideoCard = React25__namespace.default.memo(({
|
|
|
29908
30188
|
if (prevProps.displayName !== nextProps.displayName) {
|
|
29909
30189
|
return false;
|
|
29910
30190
|
}
|
|
30191
|
+
if (prevProps.lastSeenLabel !== nextProps.lastSeenLabel) {
|
|
30192
|
+
return false;
|
|
30193
|
+
}
|
|
29911
30194
|
if (prevProps.legend !== nextProps.legend) {
|
|
29912
30195
|
return false;
|
|
29913
30196
|
}
|
|
@@ -29965,6 +30248,16 @@ var VideoGridView = React25__namespace.default.memo(({
|
|
|
29965
30248
|
const supabase = useSupabase();
|
|
29966
30249
|
const { cropping, canvasConfig, hlsUrls } = videoConfig;
|
|
29967
30250
|
const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;
|
|
30251
|
+
const workspaceHealthIds = React25.useMemo(() => {
|
|
30252
|
+
const ids = /* @__PURE__ */ new Set();
|
|
30253
|
+
for (const workspace of workspaces) {
|
|
30254
|
+
if (workspace.workspace_uuid) {
|
|
30255
|
+
ids.add(workspace.workspace_uuid);
|
|
30256
|
+
}
|
|
30257
|
+
}
|
|
30258
|
+
return Array.from(ids);
|
|
30259
|
+
}, [workspaces]);
|
|
30260
|
+
const { lastSeenByWorkspaceId } = useWorkspaceHealthLastSeen(workspaceHealthIds);
|
|
29968
30261
|
React25.useEffect(() => {
|
|
29969
30262
|
const sample = workspaces.slice(0, 3).map((workspace) => ({
|
|
29970
30263
|
id: workspace.workspace_uuid || workspace.workspace_name,
|
|
@@ -30216,6 +30509,7 @@ var VideoGridView = React25__namespace.default.memo(({
|
|
|
30216
30509
|
const isVeryLowEfficiency = workspace.show_exclamation ?? (workspace.efficiency < 50 && workspace.efficiency >= 10);
|
|
30217
30510
|
const workspaceCropping = getWorkspaceCropping(workspaceId, workspace.workspace_name);
|
|
30218
30511
|
const workspaceStream = videoStreamsByWorkspaceId?.[workspaceId];
|
|
30512
|
+
const lastSeenLabel = workspace.workspace_uuid ? lastSeenByWorkspaceId[workspace.workspace_uuid]?.timeSinceLastUpdate : void 0;
|
|
30219
30513
|
const r2Url = workspaceStream?.hls_url;
|
|
30220
30514
|
const fallbackUrl = getWorkspaceHlsUrl(workspace.workspace_name, workspace.line_id);
|
|
30221
30515
|
const hasR2Stream = Boolean(r2Url);
|
|
@@ -30233,7 +30527,8 @@ var VideoGridView = React25__namespace.default.memo(({
|
|
|
30233
30527
|
fallbackUrl,
|
|
30234
30528
|
hlsUrl,
|
|
30235
30529
|
isR2Stream,
|
|
30236
|
-
shouldPlay
|
|
30530
|
+
shouldPlay,
|
|
30531
|
+
lastSeenLabel
|
|
30237
30532
|
};
|
|
30238
30533
|
});
|
|
30239
30534
|
const croppedActiveCount = workspaceCards.reduce((count, card) => {
|
|
@@ -30271,6 +30566,7 @@ var VideoGridView = React25__namespace.default.memo(({
|
|
|
30271
30566
|
displayNames[`${card.workspace.line_id}_${card.workspace.workspace_name}`] || // Always pass line_id to fallback to ensure correct mapping per line
|
|
30272
30567
|
getWorkspaceDisplayName(card.workspace.workspace_name, card.workspace.line_id)
|
|
30273
30568
|
),
|
|
30569
|
+
lastSeenLabel: card.lastSeenLabel,
|
|
30274
30570
|
useRAF: effectiveUseRAF,
|
|
30275
30571
|
compact: !selectedLine,
|
|
30276
30572
|
onMouseEnter: onWorkspaceHover ? () => onWorkspaceHover(card.workspaceId) : void 0,
|
|
@@ -33988,6 +34284,53 @@ var BackButtonMinimal = ({
|
|
|
33988
34284
|
}
|
|
33989
34285
|
);
|
|
33990
34286
|
};
|
|
34287
|
+
var FittingTitle = ({
|
|
34288
|
+
title,
|
|
34289
|
+
className,
|
|
34290
|
+
as: Component3 = "h3"
|
|
34291
|
+
}) => {
|
|
34292
|
+
const containerRef = React25.useRef(null);
|
|
34293
|
+
const textRef = React25.useRef(null);
|
|
34294
|
+
const [fontSize, setFontSize] = React25.useState(null);
|
|
34295
|
+
React25.useLayoutEffect(() => {
|
|
34296
|
+
const adjustFontSize = () => {
|
|
34297
|
+
if (!containerRef.current || !textRef.current) return;
|
|
34298
|
+
const containerWidth = containerRef.current.offsetWidth;
|
|
34299
|
+
if (containerWidth <= 0) return;
|
|
34300
|
+
textRef.current.style.fontSize = "";
|
|
34301
|
+
textRef.current.style.display = "inline-block";
|
|
34302
|
+
const naturalWidth = textRef.current.offsetWidth;
|
|
34303
|
+
if (naturalWidth > containerWidth) {
|
|
34304
|
+
const computedStyle = window.getComputedStyle(textRef.current);
|
|
34305
|
+
const currentFontSize = parseFloat(computedStyle.fontSize);
|
|
34306
|
+
const ratio = (containerWidth - 2) / naturalWidth;
|
|
34307
|
+
const newFontSize = Math.max(8, currentFontSize * ratio);
|
|
34308
|
+
setFontSize(newFontSize);
|
|
34309
|
+
} else {
|
|
34310
|
+
setFontSize(null);
|
|
34311
|
+
}
|
|
34312
|
+
textRef.current.style.display = "";
|
|
34313
|
+
};
|
|
34314
|
+
adjustFontSize();
|
|
34315
|
+
const observer = new ResizeObserver(() => {
|
|
34316
|
+
window.requestAnimationFrame(adjustFontSize);
|
|
34317
|
+
});
|
|
34318
|
+
if (containerRef.current) {
|
|
34319
|
+
observer.observe(containerRef.current);
|
|
34320
|
+
}
|
|
34321
|
+
return () => observer.disconnect();
|
|
34322
|
+
}, [title]);
|
|
34323
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: "w-full overflow-hidden min-w-0 flex items-center", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
34324
|
+
Component3,
|
|
34325
|
+
{
|
|
34326
|
+
ref: textRef,
|
|
34327
|
+
className: cn("whitespace-nowrap font-bold text-gray-900 tracking-tight", className),
|
|
34328
|
+
style: fontSize ? { fontSize: `${fontSize}px`, lineHeight: 1.2 } : {},
|
|
34329
|
+
title,
|
|
34330
|
+
children: title
|
|
34331
|
+
}
|
|
34332
|
+
) });
|
|
34333
|
+
};
|
|
33991
34334
|
var InlineEditableText = ({
|
|
33992
34335
|
value,
|
|
33993
34336
|
onSave,
|
|
@@ -53349,7 +53692,7 @@ var KPIDetailView = ({
|
|
|
53349
53692
|
const configuredTimezone = timezone || dashboardConfig.dateTimeConfig?.defaultTimezone || "UTC";
|
|
53350
53693
|
React25.useMemo(() => getCurrentTimeInZone(configuredTimezone), [configuredTimezone]);
|
|
53351
53694
|
const supervisorEnabled = dashboardConfig?.supervisorConfig?.enabled || false;
|
|
53352
|
-
const { supervisorName } = useLineSupervisor(lineId);
|
|
53695
|
+
const { supervisorName, supervisors } = useLineSupervisor(lineId);
|
|
53353
53696
|
const { displayNames: workspaceDisplayNames } = useWorkspaceDisplayNames(lineId, companyId);
|
|
53354
53697
|
React25.useEffect(() => {
|
|
53355
53698
|
if (urlDate || urlShift !== void 0) {
|
|
@@ -53944,10 +54287,20 @@ var KPIDetailView = ({
|
|
|
53944
54287
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "relative inline-flex rounded-full h-2 w-2 bg-green-500" })
|
|
53945
54288
|
] })
|
|
53946
54289
|
] }),
|
|
53947
|
-
supervisorEnabled && /* @__PURE__ */ jsxRuntime.
|
|
54290
|
+
supervisorEnabled && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1 flex justify-center", children: supervisors && supervisors.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1.5 justify-center", children: supervisors.map((supervisor) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 bg-gray-50 rounded-full pl-0.5 pr-2 py-0.5 border border-gray-100", children: [
|
|
54291
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-5 h-5 rounded-full overflow-hidden bg-white border border-gray-100 shadow-sm flex-shrink-0", children: supervisor.profilePhotoUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
54292
|
+
"img",
|
|
54293
|
+
{
|
|
54294
|
+
src: supervisor.profilePhotoUrl,
|
|
54295
|
+
alt: supervisor.displayName,
|
|
54296
|
+
className: "w-full h-full object-cover"
|
|
54297
|
+
}
|
|
54298
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full flex items-center justify-center bg-gray-50 text-[8px] font-bold text-gray-500 uppercase", children: getInitials(supervisor.displayName) }) }),
|
|
54299
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-medium text-gray-700 truncate max-w-[100px]", children: supervisor.displayName })
|
|
54300
|
+
] }, supervisor.userId)) }) : /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[10px] text-gray-500 text-center", children: [
|
|
53948
54301
|
"Supervisor: ",
|
|
53949
54302
|
supervisorName || "Unassigned"
|
|
53950
|
-
] })
|
|
54303
|
+
] }) })
|
|
53951
54304
|
] }),
|
|
53952
54305
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-9" })
|
|
53953
54306
|
] }) }),
|
|
@@ -53966,10 +54319,17 @@ var KPIDetailView = ({
|
|
|
53966
54319
|
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl md:text-2xl lg:text-3xl font-semibold text-gray-900 text-center truncate", children: resolvedLineInfo?.line_name || "Line" }),
|
|
53967
54320
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-2 w-2 rounded-full bg-green-500 animate-pulse ring-2 ring-green-500/30 ring-offset-1 flex-shrink-0" })
|
|
53968
54321
|
] }),
|
|
53969
|
-
supervisorEnabled && /* @__PURE__ */ jsxRuntime.
|
|
53970
|
-
"
|
|
53971
|
-
|
|
53972
|
-
|
|
54322
|
+
supervisorEnabled && supervisors && supervisors.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center mt-2", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap items-center justify-center gap-6", children: supervisors.map((supervisor) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
54323
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-8 h-8 rounded-full overflow-hidden bg-gray-100 border border-gray-200 shadow-sm flex-shrink-0", children: supervisor.profilePhotoUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
54324
|
+
"img",
|
|
54325
|
+
{
|
|
54326
|
+
src: supervisor.profilePhotoUrl,
|
|
54327
|
+
alt: supervisor.displayName,
|
|
54328
|
+
className: "w-full h-full object-cover"
|
|
54329
|
+
}
|
|
54330
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full flex items-center justify-center bg-gray-50 text-[10px] font-bold text-gray-500 uppercase", children: getInitials(supervisor.displayName) }) }),
|
|
54331
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-gray-700", children: supervisor.displayName })
|
|
54332
|
+
] }, supervisor.userId)) }) })
|
|
53973
54333
|
] }) })
|
|
53974
54334
|
] }) }),
|
|
53975
54335
|
(activeTab !== "monthly_history" || urlDate || urlShift) && chartMetrics && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
@@ -53984,20 +54344,26 @@ var KPIDetailView = ({
|
|
|
53984
54344
|
] }),
|
|
53985
54345
|
!urlDate && !urlShift ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "inline-flex items-center px-2.5 py-1 bg-green-100 rounded-full", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-green-700", children: /* @__PURE__ */ jsxRuntime.jsx(ISTTimer_default, {}) }) }) : urlDate && chartMetrics.date ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "inline-flex items-center px-2.5 py-1 bg-blue-100 rounded-full", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-blue-700", children: getDaysDifference2(chartMetrics.date) }) }) : null
|
|
53986
54346
|
] }),
|
|
53987
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden sm:block mt-3 bg-blue-50 px-
|
|
54347
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden sm:block mt-3 bg-blue-50/50 px-4 py-2 rounded-xl border border-blue-100/50 backdrop-blur-sm", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-center gap-6", children: [
|
|
53988
54348
|
!urlDate && !urlShift && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
53989
|
-
/* @__PURE__ */ jsxRuntime.
|
|
53990
|
-
|
|
54349
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-blue-700", children: [
|
|
54350
|
+
/* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-4 h-4 opacity-70", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" }) }),
|
|
54351
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-base font-semibold tabular-nums", children: /* @__PURE__ */ jsxRuntime.jsx(ISTTimer_default, {}) })
|
|
54352
|
+
] }),
|
|
54353
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-200" })
|
|
53991
54354
|
] }),
|
|
53992
|
-
/* @__PURE__ */ jsxRuntime.
|
|
53993
|
-
|
|
54355
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-blue-600", children: [
|
|
54356
|
+
/* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-4 h-4 opacity-70", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" }) }),
|
|
54357
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm md:text-base font-medium", children: chartMetrics && formatLocalDate(new Date(chartMetrics.date)) })
|
|
54358
|
+
] }),
|
|
54359
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-200" }),
|
|
53994
54360
|
urlDate && chartMetrics.date && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
53995
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "px-2 py-1 text-xs font-medium bg-blue-
|
|
53996
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-
|
|
54361
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "px-2 py-1 text-xs font-medium bg-blue-100 text-blue-700 border border-blue-200 rounded-md", children: getDaysDifference2(chartMetrics.date) }),
|
|
54362
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-200" })
|
|
53997
54363
|
] }),
|
|
53998
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
53999
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "
|
|
54000
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm md:text-base font-
|
|
54364
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-blue-600", children: [
|
|
54365
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "opacity-70", children: getShiftIcon(chartMetrics.shift_id ?? 0) }),
|
|
54366
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm md:text-base font-semibold uppercase tracking-wider", children: [
|
|
54001
54367
|
getShiftName(chartMetrics.shift_id ?? 0).replace(/ Shift$/i, ""),
|
|
54002
54368
|
" Shift"
|
|
54003
54369
|
] })
|
|
@@ -54018,18 +54384,24 @@ var KPIDetailView = ({
|
|
|
54018
54384
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-gray-700", children: getShiftName(selectedShiftId) })
|
|
54019
54385
|
] })
|
|
54020
54386
|
] }),
|
|
54021
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden sm:block mt-3 bg-blue-50 px-
|
|
54022
|
-
/* @__PURE__ */ jsxRuntime.
|
|
54023
|
-
|
|
54024
|
-
|
|
54025
|
-
|
|
54026
|
-
|
|
54027
|
-
|
|
54028
|
-
|
|
54029
|
-
|
|
54030
|
-
|
|
54031
|
-
|
|
54032
|
-
|
|
54387
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden sm:block mt-3 bg-blue-50/50 px-4 py-2 rounded-xl border border-blue-100/50 backdrop-blur-sm", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-center gap-6", children: [
|
|
54388
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-blue-600", children: [
|
|
54389
|
+
/* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-4 h-4 opacity-70", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" }) }),
|
|
54390
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm md:text-base font-medium", children: (() => {
|
|
54391
|
+
const monthBounds2 = getMonthKeyBounds(currentYear, currentMonth);
|
|
54392
|
+
const normalizedRange = normalizeDateKeyRange(rangeStart, rangeEnd, monthBounds2.startKey, monthBounds2.endKey);
|
|
54393
|
+
const startDate = new Date(normalizedRange.startKey);
|
|
54394
|
+
const endDate = new Date(normalizedRange.endKey);
|
|
54395
|
+
return `${startDate.toLocaleDateString("en-US", { month: "short", day: "numeric" })} - ${endDate.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" })}`;
|
|
54396
|
+
})() })
|
|
54397
|
+
] }),
|
|
54398
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-200" }),
|
|
54399
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-blue-600", children: [
|
|
54400
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "opacity-70", children: getShiftIcon(selectedShiftId) }),
|
|
54401
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm md:text-base font-semibold uppercase tracking-wider", children: [
|
|
54402
|
+
getShiftName(selectedShiftId).replace(/ Shift$/i, ""),
|
|
54403
|
+
" Shift"
|
|
54404
|
+
] })
|
|
54033
54405
|
] })
|
|
54034
54406
|
] }) })
|
|
54035
54407
|
] }),
|
|
@@ -54225,21 +54597,65 @@ var LineCard = ({
|
|
|
54225
54597
|
children: [
|
|
54226
54598
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-4 sm:mb-5 md:mb-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-start gap-2 sm:gap-3", children: [
|
|
54227
54599
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
54228
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
54229
|
-
|
|
54230
|
-
|
|
54231
|
-
|
|
54232
|
-
|
|
54233
|
-
|
|
54234
|
-
|
|
54235
|
-
|
|
54236
|
-
|
|
54237
|
-
|
|
54238
|
-
|
|
54239
|
-
|
|
54240
|
-
|
|
54241
|
-
|
|
54242
|
-
|
|
54600
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
54601
|
+
FittingTitle,
|
|
54602
|
+
{
|
|
54603
|
+
title: line.line_name,
|
|
54604
|
+
className: "text-[10px] sm:text-xs md:text-sm"
|
|
54605
|
+
}
|
|
54606
|
+
),
|
|
54607
|
+
supervisorEnabled && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4", children: [
|
|
54608
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[10px] font-semibold text-gray-400 uppercase tracking-wider mb-1.5", children: "Assigned To" }),
|
|
54609
|
+
supervisors && supervisors.length > 0 ? supervisors.length === 1 ? (
|
|
54610
|
+
// Single supervisor - just avatar with tooltip
|
|
54611
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-12 h-12 rounded-full bg-white border border-gray-100 shadow-sm flex-shrink-0 group/avatar hover:scale-110 transition-transform", children: [
|
|
54612
|
+
supervisors[0].profilePhotoUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
54613
|
+
"img",
|
|
54614
|
+
{
|
|
54615
|
+
src: supervisors[0].profilePhotoUrl,
|
|
54616
|
+
alt: supervisors[0].displayName,
|
|
54617
|
+
className: "w-full h-full object-cover rounded-full"
|
|
54618
|
+
}
|
|
54619
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full flex items-center justify-center bg-gray-50 text-base font-bold text-gray-500 uppercase rounded-full", children: getInitials(supervisors[0].displayName) }),
|
|
54620
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute bottom-full left-1/2 -translate-x-1/2 mb-2 px-2 py-1 bg-gray-900 text-white text-[10px] font-medium rounded shadow-lg opacity-0 group-hover/avatar:opacity-100 transition-opacity whitespace-nowrap pointer-events-none z-20", children: [
|
|
54621
|
+
supervisors[0].displayName,
|
|
54622
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-full left-1/2 -translate-x-1/2 -mt-[1px] border-4 border-transparent border-t-gray-900" })
|
|
54623
|
+
] })
|
|
54624
|
+
] })
|
|
54625
|
+
) : (
|
|
54626
|
+
// Multiple supervisors - overlapping avatars
|
|
54627
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex -space-x-4 py-1", children: [
|
|
54628
|
+
supervisors.slice(0, 3).map((supervisor) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
54629
|
+
"div",
|
|
54630
|
+
{
|
|
54631
|
+
className: "relative inline-block w-12 h-12 rounded-full ring-2 ring-white bg-white shadow-sm z-0 hover:z-10 transition-all hover:scale-110 group/avatar",
|
|
54632
|
+
children: [
|
|
54633
|
+
supervisor.profilePhotoUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
54634
|
+
"img",
|
|
54635
|
+
{
|
|
54636
|
+
src: supervisor.profilePhotoUrl,
|
|
54637
|
+
alt: supervisor.displayName,
|
|
54638
|
+
className: "w-full h-full object-cover rounded-full"
|
|
54639
|
+
}
|
|
54640
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full flex items-center justify-center bg-gray-100 text-sm font-bold text-gray-600 uppercase rounded-full", children: getInitials(supervisor.displayName) }),
|
|
54641
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute bottom-full left-1/2 -translate-x-1/2 mb-2 px-2 py-1 bg-gray-900 text-white text-[10px] font-medium rounded shadow-lg opacity-0 group-hover/avatar:opacity-100 transition-opacity whitespace-nowrap pointer-events-none z-20", children: [
|
|
54642
|
+
supervisor.displayName,
|
|
54643
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-full left-1/2 -translate-x-1/2 -mt-[1px] border-4 border-transparent border-t-gray-900" })
|
|
54644
|
+
] })
|
|
54645
|
+
]
|
|
54646
|
+
},
|
|
54647
|
+
supervisor.userId
|
|
54648
|
+
)),
|
|
54649
|
+
supervisors.length > 3 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex w-12 h-12 rounded-full ring-2 ring-white bg-gray-100 items-center justify-center text-sm font-medium text-gray-600 z-0", children: [
|
|
54650
|
+
"+",
|
|
54651
|
+
supervisors.length - 3
|
|
54652
|
+
] })
|
|
54653
|
+
] })
|
|
54654
|
+
) : /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-gray-600", children: [
|
|
54655
|
+
"Supervisor: ",
|
|
54656
|
+
supervisorName || "Unassigned"
|
|
54657
|
+
] })
|
|
54658
|
+
] })
|
|
54243
54659
|
] }),
|
|
54244
54660
|
kpis && isOnTrack !== null && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-1.5 px-2.5 sm:px-3 py-1 sm:py-1.5 rounded-full text-xs font-medium flex-shrink-0 ${isOnTrack ? "bg-emerald-100 text-emerald-700 border border-emerald-200" : "bg-red-100 text-red-700 border border-red-200"}`, style: { minWidth: "fit-content" }, children: [
|
|
54245
54661
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-2 h-2 rounded-full ${isOnTrack ? "bg-emerald-500" : "bg-red-500"} animate-pulse` }),
|
|
@@ -54261,7 +54677,7 @@ var LineCard = ({
|
|
|
54261
54677
|
] })
|
|
54262
54678
|
] }),
|
|
54263
54679
|
error && !kpis && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center py-8", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500", children: "Unable to load metrics" }) }),
|
|
54264
|
-
kpis && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4 sm:space-y-5", children: [
|
|
54680
|
+
kpis && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4 sm:space-y-5 pb-8 sm:pb-10", children: [
|
|
54265
54681
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
54266
54682
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs font-medium text-gray-500 uppercase tracking-wider mb-1.5 sm:mb-2", children: "Efficiency" }),
|
|
54267
54683
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline justify-between", children: [
|
|
@@ -54298,16 +54714,6 @@ var LineCard = ({
|
|
|
54298
54714
|
}
|
|
54299
54715
|
}
|
|
54300
54716
|
) })
|
|
54301
|
-
] }),
|
|
54302
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
54303
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs font-medium text-gray-500 uppercase tracking-wider mb-1.5 sm:mb-2", children: "Underperforming Workspaces" }),
|
|
54304
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline gap-2", children: [
|
|
54305
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: `text-xl sm:text-2xl font-semibold ${kpis.underperformingWorkers.current === 0 ? "text-emerald-600" : kpis.underperformingWorkers.current <= 2 ? "text-yellow-600" : "text-red-600"}`, children: kpis.underperformingWorkers.current }),
|
|
54306
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs sm:text-sm text-gray-500 font-medium", children: [
|
|
54307
|
-
"of ",
|
|
54308
|
-
kpis.underperformingWorkers.total
|
|
54309
|
-
] })
|
|
54310
|
-
] })
|
|
54311
54717
|
] })
|
|
54312
54718
|
] }),
|
|
54313
54719
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute bottom-3 right-3 sm:bottom-4 sm:right-4", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-2 sm:p-2.5 rounded-full bg-gray-50 group-hover:bg-blue-50 transition-colors shadow-sm", children: /* @__PURE__ */ jsxRuntime.jsx(outline.ArrowRightIcon, { className: "w-4 h-4 sm:w-5 sm:h-5 text-gray-400 group-hover:text-blue-600 transition-colors" }) }) })
|
|
@@ -54369,7 +54775,7 @@ var KPIsOverviewView = ({
|
|
|
54369
54775
|
return map;
|
|
54370
54776
|
}, [lineMetrics]);
|
|
54371
54777
|
const visibleLineIds = React25__namespace.default.useMemo(() => lines.map((l) => l.id), [lines]);
|
|
54372
|
-
const { supervisorNamesByLineId, supervisorsByLineId } = useSupervisorsByLineIds(visibleLineIds, {
|
|
54778
|
+
const { supervisorNamesByLineId, supervisorsByLineId, allSupervisorsMap } = useSupervisorsByLineIds(visibleLineIds, {
|
|
54373
54779
|
enabled: supervisorEnabled && visibleLineIds.length > 0
|
|
54374
54780
|
});
|
|
54375
54781
|
React25.useEffect(() => {
|
|
@@ -54441,17 +54847,34 @@ var KPIsOverviewView = ({
|
|
|
54441
54847
|
return `${startLabel} - ${endLabel}`;
|
|
54442
54848
|
};
|
|
54443
54849
|
const buildTopPerformerDisplay = (record) => {
|
|
54444
|
-
|
|
54850
|
+
let displayName = record.recipient_name || "Supervisor";
|
|
54851
|
+
if (record.first_name) {
|
|
54852
|
+
displayName = record.last_name ? `${record.first_name} ${record.last_name}` : record.first_name;
|
|
54853
|
+
} else if (record.recipient_user_id && allSupervisorsMap?.has(record.recipient_user_id)) {
|
|
54854
|
+
const supervisor = allSupervisorsMap.get(record.recipient_user_id);
|
|
54855
|
+
if (supervisor) {
|
|
54856
|
+
displayName = supervisor.displayName;
|
|
54857
|
+
}
|
|
54858
|
+
} else if (displayName.includes(".") || displayName.includes("@")) {
|
|
54859
|
+
const parts = displayName.split(/[.@]/);
|
|
54860
|
+
if (parts.length >= 2) {
|
|
54861
|
+
const firstName = parts[0].charAt(0).toUpperCase() + parts[0].slice(1);
|
|
54862
|
+
const lastName = parts[1].charAt(0).toUpperCase() + parts[1].slice(1);
|
|
54863
|
+
displayName = `${firstName} ${lastName}`;
|
|
54864
|
+
} else if (parts.length === 1) {
|
|
54865
|
+
displayName = parts[0].charAt(0).toUpperCase() + parts[0].slice(1);
|
|
54866
|
+
}
|
|
54867
|
+
}
|
|
54445
54868
|
const efficiencyValue = typeof record.avg_efficiency === "number" ? record.avg_efficiency : Number.parseFloat(String(record.avg_efficiency));
|
|
54446
54869
|
const efficiency = Number.isFinite(efficiencyValue) ? efficiencyValue : null;
|
|
54447
54870
|
return {
|
|
54448
|
-
name,
|
|
54871
|
+
name: displayName,
|
|
54449
54872
|
role: "Sup.",
|
|
54450
54873
|
unit: record.unit || "Line",
|
|
54451
54874
|
periodLabel: formatTopPerformerWeek(record.period_start, record.period_end),
|
|
54452
54875
|
efficiency,
|
|
54453
54876
|
imageUrl: record.profile_photo_url || null,
|
|
54454
|
-
initials: getInitials(
|
|
54877
|
+
initials: getInitials(displayName)
|
|
54455
54878
|
};
|
|
54456
54879
|
};
|
|
54457
54880
|
const handleLineClick = (line, kpis) => {
|
|
@@ -54493,8 +54916,8 @@ var KPIsOverviewView = ({
|
|
|
54493
54916
|
const currentShiftDetails = getCurrentShift(configuredTimezone, shiftConfig);
|
|
54494
54917
|
const shiftName = (currentShiftDetails.shiftName || "Day").replace(/ Shift$/i, "");
|
|
54495
54918
|
const showTopPerformerImage = Boolean(topPerformer.imageUrl) && !topPerformerImageError;
|
|
54496
|
-
|
|
54497
|
-
|
|
54919
|
+
typeof topPerformer.efficiency === "number" && Number.isFinite(topPerformer.efficiency);
|
|
54920
|
+
topPerformerLoading ? "--" : typeof topPerformer.efficiency === "number" && Number.isFinite(topPerformer.efficiency) ? `${topPerformer.efficiency.toFixed(1)}%` : "--";
|
|
54498
54921
|
const getShiftIcon = (shiftId) => {
|
|
54499
54922
|
const shiftNameLower = shiftName.toLowerCase();
|
|
54500
54923
|
if (shiftNameLower.includes("day") || shiftNameLower.includes("morning") || shiftId === 0) {
|
|
@@ -54523,7 +54946,7 @@ var KPIsOverviewView = ({
|
|
|
54523
54946
|
}
|
|
54524
54947
|
),
|
|
54525
54948
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
54526
|
-
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-lg font-semibold text-gray-900", children: "
|
|
54949
|
+
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-lg font-semibold text-gray-900", children: "Overview" }),
|
|
54527
54950
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-2 w-2 rounded-full bg-emerald-500 animate-pulse ring-2 ring-emerald-500/20" })
|
|
54528
54951
|
] }) }),
|
|
54529
54952
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-12" })
|
|
@@ -54539,7 +54962,7 @@ var KPIsOverviewView = ({
|
|
|
54539
54962
|
}
|
|
54540
54963
|
) }),
|
|
54541
54964
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 flex justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
54542
|
-
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl md:text-2xl lg:text-3xl font-semibold text-gray-900 text-center", children: "
|
|
54965
|
+
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl md:text-2xl lg:text-3xl font-semibold text-gray-900 text-center", children: "Overview" }),
|
|
54543
54966
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-2 w-2 rounded-full bg-green-500 animate-pulse ring-2 ring-green-500/30 ring-offset-1 flex-shrink-0" })
|
|
54544
54967
|
] }) })
|
|
54545
54968
|
] }) })
|
|
@@ -54559,7 +54982,7 @@ var KPIsOverviewView = ({
|
|
|
54559
54982
|
}
|
|
54560
54983
|
),
|
|
54561
54984
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
54562
|
-
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-lg font-semibold text-gray-900", children: "
|
|
54985
|
+
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-lg font-semibold text-gray-900", children: "Overview" }),
|
|
54563
54986
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-2 w-2 rounded-full bg-emerald-500 animate-pulse ring-2 ring-emerald-500/20" })
|
|
54564
54987
|
] }) }),
|
|
54565
54988
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-12" })
|
|
@@ -54575,7 +54998,7 @@ var KPIsOverviewView = ({
|
|
|
54575
54998
|
}
|
|
54576
54999
|
) }),
|
|
54577
55000
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 flex justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
54578
|
-
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl md:text-2xl lg:text-3xl font-semibold text-gray-900 text-center", children: "
|
|
55001
|
+
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl md:text-2xl lg:text-3xl font-semibold text-gray-900 text-center", children: "Overview" }),
|
|
54579
55002
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-2 w-2 rounded-full bg-green-500 animate-pulse ring-2 ring-green-500/30 ring-offset-1 flex-shrink-0" })
|
|
54580
55003
|
] }) })
|
|
54581
55004
|
] }) })
|
|
@@ -54598,7 +55021,7 @@ var KPIsOverviewView = ({
|
|
|
54598
55021
|
}
|
|
54599
55022
|
),
|
|
54600
55023
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
54601
|
-
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-lg font-semibold text-gray-900", children: "
|
|
55024
|
+
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-lg font-semibold text-gray-900", children: "Overview" }),
|
|
54602
55025
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-2 w-2 rounded-full bg-emerald-500 animate-pulse ring-2 ring-emerald-500/20" })
|
|
54603
55026
|
] }) }),
|
|
54604
55027
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-12" })
|
|
@@ -54611,43 +55034,46 @@ var KPIsOverviewView = ({
|
|
|
54611
55034
|
] }),
|
|
54612
55035
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "inline-flex items-center px-2.5 py-1 bg-green-100 rounded-full", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-green-700", children: /* @__PURE__ */ jsxRuntime.jsx(ISTTimer_default, {}) }) })
|
|
54613
55036
|
] }),
|
|
54614
|
-
/* @__PURE__ */ jsxRuntime.
|
|
54615
|
-
/* @__PURE__ */ jsxRuntime.
|
|
54616
|
-
|
|
54617
|
-
|
|
54618
|
-
|
|
55037
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-4 bg-white shadow-md hover:shadow-lg transition-all duration-300 ease-out hover:scale-[1.01] relative rounded-2xl border border-amber-100 pl-2 pr-4 py-1.5 flex items-center justify-between group", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-4 min-w-0", children: [
|
|
55038
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-10 h-10 rounded-full border-2 border-amber-100 overflow-hidden bg-amber-50 shadow-sm transition-transform group-hover:scale-105", children: showTopPerformerImage ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
55039
|
+
"img",
|
|
55040
|
+
{
|
|
55041
|
+
src: topPerformer.imageUrl || "",
|
|
55042
|
+
alt: "Top Performer",
|
|
55043
|
+
className: "w-full h-full object-cover",
|
|
55044
|
+
onError: () => setTopPerformerImageError(true)
|
|
55045
|
+
}
|
|
55046
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full flex items-center justify-center bg-amber-50 text-sm font-bold text-amber-600", children: topPerformer.initials }) }) }),
|
|
55047
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
|
|
55048
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 leading-none mb-1", children: [
|
|
55049
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-bold text-amber-600 uppercase tracking-widest", children: "Performer of the week" }),
|
|
55050
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-1 h-1 bg-amber-200 rounded-full" }),
|
|
55051
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-semibold text-gray-400", children: topPerformerLoading ? "Loading" : topPerformer.periodLabel })
|
|
55052
|
+
] }),
|
|
55053
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 leading-none", children: [
|
|
55054
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-[140px]", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
55055
|
+
FittingTitle,
|
|
54619
55056
|
{
|
|
54620
|
-
|
|
54621
|
-
|
|
54622
|
-
className: "
|
|
54623
|
-
onError: () => setTopPerformerImageError(true)
|
|
55057
|
+
title: topPerformer.name,
|
|
55058
|
+
as: "span",
|
|
55059
|
+
className: "text-sm text-gray-900 font-bold"
|
|
54624
55060
|
}
|
|
54625
|
-
)
|
|
54626
|
-
/* @__PURE__ */ jsxRuntime.jsx("
|
|
54627
|
-
|
|
54628
|
-
|
|
54629
|
-
|
|
54630
|
-
|
|
54631
|
-
|
|
54632
|
-
|
|
54633
|
-
|
|
54634
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 leading-none", children: [
|
|
54635
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-semibold text-gray-900", children: topPerformer.unit }),
|
|
54636
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-px h-2.5 bg-gray-200" }),
|
|
54637
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[10px] text-gray-500", children: [
|
|
54638
|
-
topPerformer.role,
|
|
54639
|
-
" ",
|
|
54640
|
-
topPerformer.name
|
|
54641
|
-
] })
|
|
54642
|
-
] })
|
|
55061
|
+
) }),
|
|
55062
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-px h-3 bg-gray-200 flex-shrink-0" }),
|
|
55063
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-[120px]", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
55064
|
+
FittingTitle,
|
|
55065
|
+
{
|
|
55066
|
+
title: topPerformer.unit,
|
|
55067
|
+
className: "text-xs font-medium text-gray-500"
|
|
55068
|
+
}
|
|
55069
|
+
) })
|
|
54643
55070
|
] })
|
|
54644
|
-
] })
|
|
54645
|
-
|
|
54646
|
-
] })
|
|
55071
|
+
] })
|
|
55072
|
+
] }) })
|
|
54647
55073
|
] }),
|
|
54648
55074
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "hidden sm:block", children: [
|
|
54649
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center relative mb-
|
|
54650
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute left-0 z-10", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
55075
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center relative mb-4 h-12", children: [
|
|
55076
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute left-0 top-1/2 -translate-y-1/2 z-10", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
54651
55077
|
BackButtonMinimal,
|
|
54652
55078
|
{
|
|
54653
55079
|
onClick: handleBackClick,
|
|
@@ -54656,45 +55082,61 @@ var KPIsOverviewView = ({
|
|
|
54656
55082
|
"aria-label": "Navigate back to previous page"
|
|
54657
55083
|
}
|
|
54658
55084
|
) }),
|
|
54659
|
-
/* @__PURE__ */ jsxRuntime.
|
|
54660
|
-
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-
|
|
54661
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-2 w-2 rounded-full bg-
|
|
54662
|
-
] })
|
|
54663
|
-
/* @__PURE__ */ jsxRuntime.
|
|
54664
|
-
/* @__PURE__ */ jsxRuntime.
|
|
54665
|
-
|
|
54666
|
-
|
|
54667
|
-
|
|
54668
|
-
|
|
54669
|
-
|
|
54670
|
-
|
|
54671
|
-
|
|
54672
|
-
|
|
54673
|
-
|
|
54674
|
-
/* @__PURE__ */ jsxRuntime.
|
|
54675
|
-
|
|
54676
|
-
|
|
54677
|
-
|
|
54678
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[8px] sm:text-[9px] lg:text-[10px] font-bold text-amber-600 uppercase tracking-wide sm:tracking-widest transition-all duration-300 truncate", children: "Top Performer" }),
|
|
54679
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-0.5 h-0.5 bg-gray-300 rounded-full flex-shrink-0" }),
|
|
54680
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[8px] sm:text-[9px] lg:text-[10px] font-medium text-gray-500 transition-all duration-300 truncate", children: topPerformerLoading ? "Loading" : topPerformer.periodLabel })
|
|
55085
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
55086
|
+
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl md:text-3xl lg:text-4xl font-semibold text-gray-900 tracking-tight", children: "Overview" }),
|
|
55087
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-2.5 w-2.5 rounded-full bg-emerald-500 animate-pulse ring-4 ring-emerald-500/10 flex-shrink-0" })
|
|
55088
|
+
] }),
|
|
55089
|
+
!topPerformerLoading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-0 top-1/2 -translate-y-1/2 z-10", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-2xl border border-amber-200 shadow-md pl-1.5 pr-4 py-1.5 flex items-center gap-4 transition-all hover:shadow-lg hover:border-amber-300 group", children: [
|
|
55090
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-10 h-10 rounded-full ring-2 ring-amber-100 overflow-hidden bg-amber-50 shadow-inner flex-shrink-0 transition-transform group-hover:scale-105", children: showTopPerformerImage ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
55091
|
+
"img",
|
|
55092
|
+
{
|
|
55093
|
+
src: topPerformer.imageUrl || "",
|
|
55094
|
+
alt: topPerformer.name,
|
|
55095
|
+
className: "w-full h-full object-cover",
|
|
55096
|
+
onError: () => setTopPerformerImageError(true)
|
|
55097
|
+
}
|
|
55098
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full flex items-center justify-center text-sm font-bold text-amber-600 uppercase", children: topPerformer.initials }) }) }),
|
|
55099
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col min-w-0", children: [
|
|
55100
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-[10px] leading-tight mb-1", children: [
|
|
55101
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-bold text-amber-600 uppercase tracking-widest", children: "Performer of the week" }),
|
|
55102
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-amber-200 opacity-50", children: "\u2022" }),
|
|
55103
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-gray-400", children: topPerformer.periodLabel })
|
|
54681
55104
|
] }),
|
|
54682
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-
|
|
54683
|
-
/* @__PURE__ */ jsxRuntime.jsx("
|
|
54684
|
-
|
|
54685
|
-
|
|
55105
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 leading-tight", children: [
|
|
55106
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-[140px]", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
55107
|
+
FittingTitle,
|
|
55108
|
+
{
|
|
55109
|
+
title: topPerformer.name,
|
|
55110
|
+
as: "span",
|
|
55111
|
+
className: "text-sm text-gray-900 font-bold"
|
|
55112
|
+
}
|
|
55113
|
+
) }),
|
|
55114
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-px h-3.5 bg-gray-200 flex-shrink-0" }),
|
|
55115
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-[150px]", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
55116
|
+
FittingTitle,
|
|
55117
|
+
{
|
|
55118
|
+
title: topPerformer.unit,
|
|
55119
|
+
className: "text-xs font-medium text-gray-500"
|
|
55120
|
+
}
|
|
55121
|
+
) })
|
|
54686
55122
|
] })
|
|
54687
55123
|
] })
|
|
54688
|
-
] })
|
|
55124
|
+
] }) })
|
|
54689
55125
|
] }),
|
|
54690
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "
|
|
54691
|
-
/* @__PURE__ */ jsxRuntime.
|
|
54692
|
-
|
|
54693
|
-
|
|
54694
|
-
|
|
54695
|
-
/* @__PURE__ */ jsxRuntime.
|
|
54696
|
-
|
|
54697
|
-
/* @__PURE__ */ jsxRuntime.
|
|
55126
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-blue-50/50 px-4 py-2 rounded-xl border border-blue-100/50 backdrop-blur-sm", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center gap-6", children: [
|
|
55127
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-blue-700", children: [
|
|
55128
|
+
/* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-4 h-4 opacity-70", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" }) }),
|
|
55129
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-base font-semibold tabular-nums", children: /* @__PURE__ */ jsxRuntime.jsx(ISTTimer_default, {}) })
|
|
55130
|
+
] }),
|
|
55131
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-200" }),
|
|
55132
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-blue-600", children: [
|
|
55133
|
+
/* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-4 h-4 opacity-70", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" }) }),
|
|
55134
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium", children: formatLocalDate2(/* @__PURE__ */ new Date()) })
|
|
55135
|
+
] }),
|
|
55136
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-200" }),
|
|
55137
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-blue-600", children: [
|
|
55138
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "opacity-70", children: getShiftIcon(currentShiftDetails.shiftId) }),
|
|
55139
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm font-semibold uppercase tracking-wider", children: [
|
|
54698
55140
|
shiftName,
|
|
54699
55141
|
" Shift"
|
|
54700
55142
|
] })
|
|
@@ -54742,16 +55184,22 @@ var HeaderRibbon = React25.memo(({
|
|
|
54742
55184
|
] }),
|
|
54743
55185
|
showTimer && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "inline-flex items-center px-2.5 py-1 bg-green-100 rounded-full", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-green-700", children: /* @__PURE__ */ jsxRuntime.jsx(IsolatedTimer, {}) }) })
|
|
54744
55186
|
] }),
|
|
54745
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden sm:block mt-3 bg-blue-50 px-
|
|
55187
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden sm:block mt-3 bg-blue-50/50 px-4 py-2 rounded-xl border border-blue-100/50 backdrop-blur-sm", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center gap-6", children: [
|
|
54746
55188
|
showTimer && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
54747
|
-
/* @__PURE__ */ jsxRuntime.
|
|
54748
|
-
|
|
55189
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-blue-700", children: [
|
|
55190
|
+
/* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-4 h-4 opacity-70", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" }) }),
|
|
55191
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-base font-semibold tabular-nums", children: /* @__PURE__ */ jsxRuntime.jsx(IsolatedTimer, {}) })
|
|
55192
|
+
] }),
|
|
55193
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-200" })
|
|
54749
55194
|
] }),
|
|
54750
|
-
/* @__PURE__ */ jsxRuntime.
|
|
54751
|
-
|
|
54752
|
-
|
|
54753
|
-
|
|
54754
|
-
|
|
55195
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-blue-600", children: [
|
|
55196
|
+
/* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-4 h-4 opacity-70", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" }) }),
|
|
55197
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium", children: currentDate })
|
|
55198
|
+
] }),
|
|
55199
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-200" }),
|
|
55200
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-blue-600", children: [
|
|
55201
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "opacity-70", children: shiftIcon }),
|
|
55202
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm font-semibold uppercase tracking-wider", children: [
|
|
54755
55203
|
shiftName.replace(/ Shift$/i, ""),
|
|
54756
55204
|
" Shift"
|
|
54757
55205
|
] })
|
|
@@ -58650,14 +59098,6 @@ var getInitialTab = (sourceType, defaultTab, fromMonthly, urlDate) => {
|
|
|
58650
59098
|
}
|
|
58651
59099
|
return "overview";
|
|
58652
59100
|
};
|
|
58653
|
-
var getInitials3 = (name) => {
|
|
58654
|
-
if (!name) return "??";
|
|
58655
|
-
const parts = name.trim().split(" ");
|
|
58656
|
-
if (parts.length >= 2) {
|
|
58657
|
-
return `${parts[0][0]}${parts[parts.length - 1][0]}`.toUpperCase();
|
|
58658
|
-
}
|
|
58659
|
-
return name.substring(0, 2).toUpperCase();
|
|
58660
|
-
};
|
|
58661
59101
|
var chartCardVariants = {
|
|
58662
59102
|
initial: { opacity: 0, y: 10 },
|
|
58663
59103
|
animate: {
|
|
@@ -58739,7 +59179,7 @@ var WorkspaceDetailView = ({
|
|
|
58739
59179
|
const prewarmedClipsRef = React25.useRef(/* @__PURE__ */ new Set());
|
|
58740
59180
|
const prewarmInFlightRef = React25.useRef(/* @__PURE__ */ new Set());
|
|
58741
59181
|
const isClipsEnabled = dashboardConfig?.clipsConfig?.enabled ?? true;
|
|
58742
|
-
|
|
59182
|
+
dashboardConfig?.supervisorConfig?.enabled || false;
|
|
58743
59183
|
const effectiveLineId = lineId || selectedLineId;
|
|
58744
59184
|
const { shiftConfig, isLoading: isShiftConfigLoading, isFromDatabase: isShiftConfigFromDatabase } = useDynamicShiftConfig(effectiveLineId);
|
|
58745
59185
|
const currentShiftDetails = React25.useMemo(() => {
|
|
@@ -59371,29 +59811,16 @@ var WorkspaceDetailView = ({
|
|
|
59371
59811
|
"aria-label": "Navigate back to previous page"
|
|
59372
59812
|
}
|
|
59373
59813
|
) }),
|
|
59374
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute left-1/2 transform -translate-x-1/2 max-w-[calc(100%-200px)]", children: /* @__PURE__ */ jsxRuntime.
|
|
59375
|
-
/* @__PURE__ */ jsxRuntime.
|
|
59376
|
-
|
|
59377
|
-
/* @__PURE__ */ jsxRuntime.
|
|
59378
|
-
|
|
59379
|
-
|
|
59380
|
-
|
|
59381
|
-
|
|
59382
|
-
|
|
59383
|
-
|
|
59384
|
-
] }),
|
|
59385
|
-
supervisorEnabled && supervisors && supervisors.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-2 justify-center", children: supervisors.map((supervisor) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 bg-gray-50 rounded-full pl-0.5 pr-2.5 py-0.5 border border-gray-100 hover:border-gray-200 transition-all duration-300 ease-out group/supervisor hover:scale-150 hover:z-50 hover:shadow-lg relative", children: [
|
|
59386
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-6 h-6 rounded-full overflow-hidden bg-white border border-gray-100 shadow-sm flex-shrink-0", children: supervisor.profilePhotoUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
59387
|
-
"img",
|
|
59388
|
-
{
|
|
59389
|
-
src: supervisor.profilePhotoUrl,
|
|
59390
|
-
alt: supervisor.displayName,
|
|
59391
|
-
className: "w-full h-full object-cover"
|
|
59392
|
-
}
|
|
59393
|
-
) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full flex items-center justify-center bg-gray-50 text-[9px] font-bold text-gray-500 uppercase", children: getInitials3(supervisor.displayName) }) }),
|
|
59394
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-gray-700 truncate max-w-[120px]", children: supervisor.displayName })
|
|
59395
|
-
] }, supervisor.userId)) })
|
|
59396
|
-
] }) }),
|
|
59814
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute left-1/2 transform -translate-x-1/2 max-w-[calc(100%-200px)]", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col items-center gap-2", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
59815
|
+
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-lg md:text-xl lg:text-2xl xl:text-3xl font-semibold text-gray-900 truncate", children: formattedWorkspaceName }),
|
|
59816
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex h-2.5 w-2.5", children: [
|
|
59817
|
+
isLive && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75" }),
|
|
59818
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: clsx(
|
|
59819
|
+
"relative inline-flex rounded-full h-2.5 w-2.5",
|
|
59820
|
+
isLive ? "bg-green-500" : "bg-red-500"
|
|
59821
|
+
) })
|
|
59822
|
+
] })
|
|
59823
|
+
] }) }) }),
|
|
59397
59824
|
activeTab !== "monthly_history" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-0 top-0 flex flex-col items-end gap-1", children: workspaceHealth && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-gray-500", children: [
|
|
59398
59825
|
"Last update: ",
|
|
59399
59826
|
workspaceHealth.timeSinceLastUpdate
|
|
@@ -59425,28 +59852,32 @@ var WorkspaceDetailView = ({
|
|
|
59425
59852
|
})() })
|
|
59426
59853
|
] })
|
|
59427
59854
|
] }),
|
|
59428
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden sm:block mt-3 bg-blue-50 px-
|
|
59429
|
-
/* @__PURE__ */ jsxRuntime.
|
|
59430
|
-
|
|
59431
|
-
|
|
59432
|
-
|
|
59433
|
-
|
|
59434
|
-
|
|
59435
|
-
|
|
59855
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden sm:block mt-3 bg-blue-50/50 px-4 py-2 rounded-xl border border-blue-100/50 backdrop-blur-sm", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-center gap-6", children: [
|
|
59856
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-blue-600", children: [
|
|
59857
|
+
/* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-4 h-4 opacity-70", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" }) }),
|
|
59858
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm md:text-base font-medium", children: (() => {
|
|
59859
|
+
const monthBounds2 = getMonthKeyBounds(selectedYear, selectedMonth);
|
|
59860
|
+
const normalizedRange = normalizeDateKeyRange(rangeStart, rangeEnd, monthBounds2.startKey, monthBounds2.endKey);
|
|
59861
|
+
const startDate = new Date(normalizedRange.startKey);
|
|
59862
|
+
const endDate = new Date(normalizedRange.endKey);
|
|
59863
|
+
const isFullMonth = normalizedRange.startKey === monthBounds2.startKey && normalizedRange.endKey === monthBounds2.endKey;
|
|
59864
|
+
if (isFullMonth) {
|
|
59865
|
+
return `${startDate.toLocaleDateString("en-US", { month: "short", day: "numeric" })} - ${endDate.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" })}`;
|
|
59866
|
+
}
|
|
59436
59867
|
return `${startDate.toLocaleDateString("en-US", { month: "short", day: "numeric" })} - ${endDate.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" })}`;
|
|
59437
|
-
}
|
|
59438
|
-
|
|
59439
|
-
|
|
59440
|
-
/* @__PURE__ */ jsxRuntime.
|
|
59441
|
-
|
|
59442
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-blue-600", children: (() => {
|
|
59868
|
+
})() })
|
|
59869
|
+
] }),
|
|
59870
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-200" }),
|
|
59871
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-blue-600", children: [
|
|
59872
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "opacity-70", children: (() => {
|
|
59443
59873
|
const shift2 = shiftConfig?.shifts?.find((s) => s.shiftId === selectedShift);
|
|
59444
59874
|
const shiftName = shift2?.shiftName || (selectedShift === 0 ? "Day Shift" : "Night Shift");
|
|
59445
59875
|
return getShiftIcon(shiftName);
|
|
59446
59876
|
})() }),
|
|
59447
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm md:text-base font-
|
|
59877
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm md:text-base font-semibold uppercase tracking-wider", children: (() => {
|
|
59448
59878
|
const shift2 = shiftConfig?.shifts?.find((s) => s.shiftId === selectedShift);
|
|
59449
|
-
|
|
59879
|
+
const name = shift2?.shiftName || (selectedShift === 0 ? "Day Shift" : "Night Shift");
|
|
59880
|
+
return name.replace(/ Shift$/i, "") + " Shift";
|
|
59450
59881
|
})() })
|
|
59451
59882
|
] })
|
|
59452
59883
|
] }) })
|
|
@@ -59459,28 +59890,37 @@ var WorkspaceDetailView = ({
|
|
|
59459
59890
|
] }),
|
|
59460
59891
|
!date && !shift && !usingFallbackData ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "inline-flex items-center px-2.5 py-1 bg-green-100 rounded-full", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-green-700", children: /* @__PURE__ */ jsxRuntime.jsx(LiveTimer, {}) }) }) : date ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "inline-flex items-center px-2.5 py-1 bg-blue-100 rounded-full", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-blue-700", children: getDaysDifference(workspace.date, timezone, dashboardConfig?.shiftConfig?.dayShift?.startTime || "06:00") }) }) : usingFallbackData ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "inline-flex items-center px-2.5 py-1 bg-amber-100 rounded-full", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-amber-700", children: getDaysDifference(workspace.date, timezone, dashboardConfig?.shiftConfig?.dayShift?.startTime || "06:00") }) }) : null
|
|
59461
59892
|
] }),
|
|
59462
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden sm:block mt-3 bg-blue-50 px-
|
|
59893
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden sm:block mt-3 bg-blue-50/50 px-4 py-2 rounded-xl border border-blue-100/50 backdrop-blur-sm", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-center gap-6", children: [
|
|
59463
59894
|
!date && !shift && !usingFallbackData && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
59464
|
-
/* @__PURE__ */ jsxRuntime.
|
|
59465
|
-
|
|
59895
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-blue-700", children: [
|
|
59896
|
+
/* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-4 h-4 opacity-70", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" }) }),
|
|
59897
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-base font-semibold tabular-nums", children: /* @__PURE__ */ jsxRuntime.jsx(LiveTimer, {}) })
|
|
59898
|
+
] }),
|
|
59899
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-200" })
|
|
59900
|
+
] }),
|
|
59901
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-blue-600", children: [
|
|
59902
|
+
/* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-4 h-4 opacity-70", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" }) }),
|
|
59903
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm md:text-base font-medium", children: formatISTDate2(new Date(workspace.date)) })
|
|
59466
59904
|
] }),
|
|
59467
|
-
/* @__PURE__ */ jsxRuntime.jsx("
|
|
59468
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-300" }),
|
|
59905
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-200" }),
|
|
59469
59906
|
date && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
59470
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "px-2 py-1 text-xs font-medium bg-blue-
|
|
59471
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-
|
|
59907
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "px-2 py-1 text-xs font-medium bg-blue-100 text-blue-700 border border-blue-200 rounded-md", children: getDaysDifference(workspace.date, timezone, dashboardConfig?.shiftConfig?.dayShift?.startTime || "06:00") }),
|
|
59908
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-200" })
|
|
59472
59909
|
] }),
|
|
59473
59910
|
!date && !shift && usingFallbackData && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
59474
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "px-2 py-1 text-xs font-medium bg-amber-
|
|
59911
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "px-2 py-1 text-xs font-medium bg-amber-50 text-amber-700 border border-amber-200 rounded-md", children: [
|
|
59475
59912
|
"Latest available data (",
|
|
59476
59913
|
getDaysDifference(workspace.date, timezone, dashboardConfig?.shiftConfig?.dayShift?.startTime || "06:00"),
|
|
59477
59914
|
")"
|
|
59478
59915
|
] }),
|
|
59479
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-
|
|
59916
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-200" })
|
|
59480
59917
|
] }),
|
|
59481
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
59482
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "
|
|
59483
|
-
/* @__PURE__ */ jsxRuntime.
|
|
59918
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-blue-600", children: [
|
|
59919
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "opacity-70", children: getShiftIcon(workspace.shift_type) }),
|
|
59920
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm md:text-base font-semibold uppercase tracking-wider", children: [
|
|
59921
|
+
workspace.shift_type.replace(/ Shift$/i, ""),
|
|
59922
|
+
" Shift"
|
|
59923
|
+
] })
|
|
59484
59924
|
] })
|
|
59485
59925
|
] }) })
|
|
59486
59926
|
] }) }),
|
|
@@ -66179,6 +66619,7 @@ exports.FileManagerFilters = FileManagerFilters;
|
|
|
66179
66619
|
exports.FilterDialogTrigger = FilterDialogTrigger;
|
|
66180
66620
|
exports.FirstTimeLoginDebug = FirstTimeLoginDebug;
|
|
66181
66621
|
exports.FirstTimeLoginHandler = FirstTimeLoginHandler;
|
|
66622
|
+
exports.FittingTitle = FittingTitle;
|
|
66182
66623
|
exports.GaugeChart = GaugeChart;
|
|
66183
66624
|
exports.GridComponentsPlaceholder = GridComponentsPlaceholder;
|
|
66184
66625
|
exports.HamburgerButton = HamburgerButton;
|
|
@@ -66567,6 +67008,7 @@ exports.useWorkspaceDisplayName = useWorkspaceDisplayName;
|
|
|
66567
67008
|
exports.useWorkspaceDisplayNames = useWorkspaceDisplayNames;
|
|
66568
67009
|
exports.useWorkspaceDisplayNamesMap = useWorkspaceDisplayNamesMap;
|
|
66569
67010
|
exports.useWorkspaceHealthById = useWorkspaceHealthById;
|
|
67011
|
+
exports.useWorkspaceHealthLastSeen = useWorkspaceHealthLastSeen;
|
|
66570
67012
|
exports.useWorkspaceHealthStatus = useWorkspaceHealthStatus;
|
|
66571
67013
|
exports.useWorkspaceMetrics = useWorkspaceMetrics;
|
|
66572
67014
|
exports.useWorkspaceNavigation = useWorkspaceNavigation;
|