@optifye/dashboard-core 4.2.7 → 4.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -7,8 +7,8 @@ var dateFnsTz = require('date-fns-tz');
7
7
  var dateFns = require('date-fns');
8
8
  var mixpanel = require('mixpanel-browser');
9
9
  var supabaseJs = require('@supabase/supabase-js');
10
- var useSWR = require('swr');
11
10
  var Hls2 = require('hls.js');
11
+ var useSWR = require('swr');
12
12
  var motionUtils = require('motion-utils');
13
13
  var motionDom = require('motion-dom');
14
14
  var recharts = require('recharts');
@@ -46,8 +46,8 @@ function _interopNamespace(e) {
46
46
 
47
47
  var React14__namespace = /*#__PURE__*/_interopNamespace(React14);
48
48
  var mixpanel__default = /*#__PURE__*/_interopDefault(mixpanel);
49
- var useSWR__default = /*#__PURE__*/_interopDefault(useSWR);
50
49
  var Hls2__default = /*#__PURE__*/_interopDefault(Hls2);
50
+ var useSWR__default = /*#__PURE__*/_interopDefault(useSWR);
51
51
  var html2canvas__default = /*#__PURE__*/_interopDefault(html2canvas);
52
52
  var jsPDF__default = /*#__PURE__*/_interopDefault(jsPDF);
53
53
  var SelectPrimitive__namespace = /*#__PURE__*/_interopNamespace(SelectPrimitive);
@@ -154,6 +154,12 @@ var DEFAULT_ANALYTICS_CONFIG = {
154
154
  var DEFAULT_AUTH_CONFIG = {
155
155
  // Defaults related to auth providers, redirects etc.
156
156
  };
157
+ var DEFAULT_VIDEO_CONFIG = {
158
+ canvasConfig: {
159
+ fps: 30,
160
+ useRAF: true
161
+ }
162
+ };
157
163
  var LINE_1_UUID = "910a224b-0abc-459a-babb-4c899824cfe7";
158
164
  var DEFAULT_CONFIG = {
159
165
  apiBaseUrl: void 0,
@@ -167,7 +173,8 @@ var DEFAULT_CONFIG = {
167
173
  // Add entity config here
168
174
  shiftConfig: DEFAULT_SHIFT_CONFIG,
169
175
  workspaceConfig: DEFAULT_WORKSPACE_CONFIG,
170
- endpoints: DEFAULT_ENDPOINTS_CONFIG
176
+ endpoints: DEFAULT_ENDPOINTS_CONFIG,
177
+ videoConfig: DEFAULT_VIDEO_CONFIG
171
178
  };
172
179
 
173
180
  // src/lib/utils/config.ts
@@ -299,6 +306,10 @@ function useCustomConfig() {
299
306
  const { customConfig } = useDashboardConfig();
300
307
  return customConfig ?? {};
301
308
  }
309
+ function useVideoConfig() {
310
+ const { videoConfig } = useDashboardConfig();
311
+ return videoConfig ?? DEFAULT_VIDEO_CONFIG;
312
+ }
302
313
 
303
314
  // src/lib/internal/supabaseClientInstance.ts
304
315
  var supabaseInstance = null;
@@ -1009,7 +1020,7 @@ var dashboardService = {
1009
1020
  const formattedStartDate = formatDate(startDate);
1010
1021
  const formattedEndDate = formatDate(endDate);
1011
1022
  try {
1012
- const { data, error } = await supabase.from(metricsTable).select("date, shift_id, efficiency, total_output, avg_cycle_time, idle_time, ideal_output, avg_pph, pph_threshold, workspace_rank").eq("workspace_id", workspaceUuid).gte("date", formattedStartDate).lte("date", formattedEndDate).order("date", { ascending: true }).order("shift_id", { ascending: true });
1023
+ const { data, error } = await supabase.from(metricsTable).select("date, shift_id, efficiency, total_output, avg_cycle_time, ideal_output, avg_pph, pph_threshold, workspace_rank").eq("workspace_id", workspaceUuid).gte("date", formattedStartDate).lte("date", formattedEndDate).order("date", { ascending: true }).order("shift_id", { ascending: true });
1013
1024
  if (error) throw error;
1014
1025
  if (!data) return [];
1015
1026
  const transformedData = data.map((item) => ({
@@ -1022,8 +1033,7 @@ var dashboardService = {
1022
1033
  ideal_output: item.ideal_output || 0,
1023
1034
  avg_pph: item.avg_pph || 0,
1024
1035
  pph_threshold: item.pph_threshold || 0,
1025
- workspace_rank: item.workspace_rank || 0,
1026
- idle_time: item.idle_time || 0
1036
+ workspace_rank: item.workspace_rank || 0
1027
1037
  }));
1028
1038
  return transformedData;
1029
1039
  } catch (err) {
@@ -4247,6 +4257,351 @@ var useWorkspaceOperators = (workspaceId, options) => {
4247
4257
  refetch: fetchData
4248
4258
  };
4249
4259
  };
4260
+
4261
+ // src/lib/utils/dashboardReload.ts
4262
+ var createThrottledReload = (interval = 5e3) => {
4263
+ let last = 0;
4264
+ let queued = false;
4265
+ const doReload = () => {
4266
+ if (typeof window !== "undefined") {
4267
+ window.location.reload();
4268
+ }
4269
+ };
4270
+ return () => {
4271
+ const now2 = Date.now();
4272
+ if (now2 - last >= interval) {
4273
+ last = now2;
4274
+ doReload();
4275
+ } else if (!queued) {
4276
+ queued = true;
4277
+ setTimeout(() => {
4278
+ queued = false;
4279
+ last = Date.now();
4280
+ doReload();
4281
+ }, interval - (now2 - last));
4282
+ }
4283
+ };
4284
+ };
4285
+ var throttledReloadDashboard = createThrottledReload(5e3);
4286
+
4287
+ // src/lib/hooks/useHlsStream.ts
4288
+ var HLS_CONFIG = {
4289
+ maxBufferLength: 8,
4290
+ maxMaxBufferLength: 15,
4291
+ lowLatencyMode: false,
4292
+ enableWorker: true,
4293
+ // Retry + timeout tuned for quick recovery
4294
+ manifestLoadingMaxRetry: 4,
4295
+ levelLoadingMaxRetry: 3,
4296
+ fragLoadingMaxRetry: 4,
4297
+ manifestLoadingRetryDelay: 500,
4298
+ levelLoadingRetryDelay: 500,
4299
+ fragLoadingRetryDelay: 500,
4300
+ manifestLoadingTimeOut: 1e4,
4301
+ levelLoadingTimeOut: 8e3,
4302
+ fragLoadingTimeOut: 1e4,
4303
+ liveSyncDurationCount: 2
4304
+ // Follow live edge aggressively
4305
+ };
4306
+ function useHlsStream(videoRef, { src, shouldPlay, onFatalError }) {
4307
+ const [restartKey, setRestartKey] = React14.useState(0);
4308
+ const hlsRef = React14.useRef(null);
4309
+ const stallCheckIntervalRef = React14.useRef(null);
4310
+ const noProgressTimerRef = React14.useRef(null);
4311
+ const lastTimeUpdateRef = React14.useRef(0);
4312
+ const softRestartCountRef = React14.useRef(0);
4313
+ const isNativeHlsRef = React14.useRef(false);
4314
+ const waitingTimerRef = React14.useRef(null);
4315
+ const cleanup = () => {
4316
+ if (stallCheckIntervalRef.current) {
4317
+ clearInterval(stallCheckIntervalRef.current);
4318
+ stallCheckIntervalRef.current = null;
4319
+ }
4320
+ if (noProgressTimerRef.current) {
4321
+ clearTimeout(noProgressTimerRef.current);
4322
+ noProgressTimerRef.current = null;
4323
+ }
4324
+ if (waitingTimerRef.current) {
4325
+ clearTimeout(waitingTimerRef.current);
4326
+ waitingTimerRef.current = null;
4327
+ }
4328
+ if (hlsRef.current) {
4329
+ hlsRef.current.destroy();
4330
+ hlsRef.current = null;
4331
+ }
4332
+ const video = videoRef.current;
4333
+ if (video) {
4334
+ video.pause();
4335
+ video.removeAttribute("src");
4336
+ video.load();
4337
+ video.removeEventListener("waiting", handleWaiting);
4338
+ video.removeEventListener("timeupdate", handleTimeUpdate);
4339
+ video.removeEventListener("error", handleNativeError);
4340
+ }
4341
+ lastTimeUpdateRef.current = 0;
4342
+ softRestartCountRef.current = 0;
4343
+ };
4344
+ const seekToLiveEdge = () => {
4345
+ const hls = hlsRef.current;
4346
+ const video = videoRef.current;
4347
+ if (!hls || !video) return;
4348
+ if (hls.liveSyncPosition !== null && hls.liveSyncPosition !== void 0) {
4349
+ video.currentTime = hls.liveSyncPosition;
4350
+ } else if (hls.levels?.[hls.currentLevel]?.details) {
4351
+ const levelDetails = hls.levels[hls.currentLevel].details;
4352
+ if (levelDetails && levelDetails.edge !== void 0) {
4353
+ video.currentTime = Math.max(0, levelDetails.edge - 5);
4354
+ }
4355
+ }
4356
+ };
4357
+ const softRestart = (reason) => {
4358
+ console.warn(`[HLS] Soft restart: ${reason}`);
4359
+ const hls = hlsRef.current;
4360
+ if (!hls) return;
4361
+ try {
4362
+ hls.stopLoad();
4363
+ hls.startLoad(-1);
4364
+ seekToLiveEdge();
4365
+ softRestartCountRef.current++;
4366
+ if (softRestartCountRef.current >= 5) {
4367
+ hardRestart(`${reason} (escalated after ${softRestartCountRef.current} soft restarts)`);
4368
+ }
4369
+ } catch (error) {
4370
+ console.error("[HLS] Soft restart failed:", error);
4371
+ hardRestart(`${reason} (soft restart error)`);
4372
+ }
4373
+ };
4374
+ const hardRestart = (reason) => {
4375
+ console.warn(`[HLS] Hard restart: ${reason}`);
4376
+ cleanup();
4377
+ setRestartKey((k) => k + 1);
4378
+ softRestartCountRef.current = 0;
4379
+ if (reason.includes("hard restart") || reason.includes("native video error")) {
4380
+ if (onFatalError) {
4381
+ onFatalError();
4382
+ } else {
4383
+ throttledReloadDashboard();
4384
+ }
4385
+ }
4386
+ };
4387
+ const handleWaiting = () => {
4388
+ if (isNativeHlsRef.current) return;
4389
+ console.log("[HLS] Video waiting (buffer underrun)");
4390
+ if (waitingTimerRef.current) {
4391
+ clearTimeout(waitingTimerRef.current);
4392
+ }
4393
+ waitingTimerRef.current = setTimeout(() => {
4394
+ const video = videoRef.current;
4395
+ if (video && video.readyState < 3) {
4396
+ softRestart("waiting timeout");
4397
+ }
4398
+ }, 1e4);
4399
+ };
4400
+ const handleTimeUpdate = () => {
4401
+ const video = videoRef.current;
4402
+ if (!video) return;
4403
+ lastTimeUpdateRef.current = video.currentTime;
4404
+ if (waitingTimerRef.current) {
4405
+ clearTimeout(waitingTimerRef.current);
4406
+ waitingTimerRef.current = null;
4407
+ }
4408
+ };
4409
+ const handleNativeError = () => {
4410
+ console.error("[HLS] Native video error");
4411
+ hardRestart("native video error");
4412
+ };
4413
+ const startStallDetection = () => {
4414
+ if (isNativeHlsRef.current) return;
4415
+ stallCheckIntervalRef.current = setInterval(() => {
4416
+ const video = videoRef.current;
4417
+ if (!video || video.paused || video.ended) return;
4418
+ const currentTime = video.currentTime;
4419
+ const lastTime = lastTimeUpdateRef.current;
4420
+ if (Math.abs(currentTime - lastTime) < 0.1 && video.readyState >= 2) {
4421
+ console.warn("[HLS] Playback stall detected");
4422
+ if (!noProgressTimerRef.current) {
4423
+ noProgressTimerRef.current = setTimeout(() => {
4424
+ softRestart("playback stall");
4425
+ noProgressTimerRef.current = null;
4426
+ }, 8e3);
4427
+ }
4428
+ } else {
4429
+ if (noProgressTimerRef.current) {
4430
+ clearTimeout(noProgressTimerRef.current);
4431
+ noProgressTimerRef.current = null;
4432
+ }
4433
+ }
4434
+ }, 7e3);
4435
+ };
4436
+ React14.useEffect(() => {
4437
+ if (!src || !shouldPlay) {
4438
+ cleanup();
4439
+ return;
4440
+ }
4441
+ const video = videoRef.current;
4442
+ if (!video) return;
4443
+ isNativeHlsRef.current = video.canPlayType("application/vnd.apple.mpegurl") === "probably";
4444
+ if (Hls2__default.default.isSupported() && !isNativeHlsRef.current) {
4445
+ const hls = new Hls2__default.default(HLS_CONFIG);
4446
+ hlsRef.current = hls;
4447
+ hls.attachMedia(video);
4448
+ hls.loadSource(src);
4449
+ hls.on(Hls2__default.default.Events.ERROR, (_, data) => {
4450
+ if (!data.fatal) return;
4451
+ console.error("[HLS] Fatal error:", data.type, data.details);
4452
+ if (data.response?.code === 404) {
4453
+ hardRestart("404 hard restart");
4454
+ return;
4455
+ }
4456
+ switch (data.type) {
4457
+ case Hls2__default.default.ErrorTypes.NETWORK_ERROR:
4458
+ case Hls2__default.default.ErrorTypes.MEDIA_ERROR:
4459
+ softRestart(`${data.type}: ${data.details}`);
4460
+ break;
4461
+ default:
4462
+ hardRestart(`Fatal ${data.type}: ${data.details}`);
4463
+ break;
4464
+ }
4465
+ });
4466
+ hls.on(Hls2__default.default.Events.MANIFEST_PARSED, () => {
4467
+ video.play().catch((err) => {
4468
+ console.error("[HLS] Play failed:", err);
4469
+ });
4470
+ });
4471
+ video.addEventListener("waiting", handleWaiting);
4472
+ video.addEventListener("timeupdate", handleTimeUpdate);
4473
+ startStallDetection();
4474
+ } else if (isNativeHlsRef.current) {
4475
+ console.log("[HLS] Using native HLS");
4476
+ video.src = src;
4477
+ video.addEventListener("error", handleNativeError);
4478
+ video.play().catch((err) => {
4479
+ console.error("[HLS] Native play failed:", err);
4480
+ });
4481
+ } else {
4482
+ console.error("[HLS] HLS not supported");
4483
+ }
4484
+ return cleanup;
4485
+ }, [src, shouldPlay, restartKey, onFatalError]);
4486
+ return {
4487
+ restartKey,
4488
+ isNativeHls: isNativeHlsRef.current
4489
+ };
4490
+ }
4491
+ function useHlsStreamWithCropping(videoRef, canvasRef, options) {
4492
+ const { src, shouldPlay, cropping, canvasFps = 30, useRAF = true, onFatalError } = options;
4493
+ const animationFrameRef = React14.useRef(null);
4494
+ const intervalRef = React14.useRef(null);
4495
+ const isDrawingRef = React14.useRef(false);
4496
+ const hlsState = useHlsStream(videoRef, { src, shouldPlay, onFatalError });
4497
+ const calculateCropRect = React14.useCallback((video, cropping2) => {
4498
+ const videoWidth = video.videoWidth;
4499
+ const videoHeight = video.videoHeight;
4500
+ const sx = cropping2.x / 100 * videoWidth;
4501
+ const sy = cropping2.y / 100 * videoHeight;
4502
+ const sw = cropping2.width / 100 * videoWidth;
4503
+ const sh = cropping2.height / 100 * videoHeight;
4504
+ return { sx, sy, sw, sh };
4505
+ }, []);
4506
+ const drawFrame = React14.useCallback(() => {
4507
+ const video = videoRef.current;
4508
+ const canvas = canvasRef.current;
4509
+ if (!video || !canvas || !cropping) return;
4510
+ const ctx = canvas.getContext("2d");
4511
+ if (!ctx) return;
4512
+ if (video.readyState < 2) return;
4513
+ try {
4514
+ const videoWidth = video.videoWidth;
4515
+ const videoHeight = video.videoHeight;
4516
+ if (!videoWidth || !videoHeight) return;
4517
+ const { sx, sy, sw, sh } = calculateCropRect(video, cropping);
4518
+ const canvasContainer = canvas.parentElement;
4519
+ if (canvasContainer) {
4520
+ const containerWidth = canvasContainer.clientWidth;
4521
+ const containerHeight = canvasContainer.clientHeight;
4522
+ if (canvas.width !== containerWidth || canvas.height !== containerHeight) {
4523
+ canvas.width = containerWidth;
4524
+ canvas.height = containerHeight;
4525
+ }
4526
+ }
4527
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
4528
+ ctx.drawImage(
4529
+ video,
4530
+ sx,
4531
+ sy,
4532
+ sw,
4533
+ sh,
4534
+ // Source rectangle (cropped portion)
4535
+ 0,
4536
+ 0,
4537
+ canvas.width,
4538
+ canvas.height
4539
+ // Destination rectangle (full canvas)
4540
+ );
4541
+ } catch (err) {
4542
+ console.warn("Canvas drawing error:", err);
4543
+ }
4544
+ }, [videoRef, canvasRef, cropping, calculateCropRect]);
4545
+ const startCanvasRendering = React14.useCallback(() => {
4546
+ if (isDrawingRef.current) return;
4547
+ isDrawingRef.current = true;
4548
+ if (useRAF) {
4549
+ const animate = () => {
4550
+ drawFrame();
4551
+ animationFrameRef.current = requestAnimationFrame(animate);
4552
+ };
4553
+ animate();
4554
+ } else {
4555
+ const frameInterval = 1e3 / canvasFps;
4556
+ intervalRef.current = setInterval(drawFrame, frameInterval);
4557
+ }
4558
+ }, [drawFrame, canvasFps, useRAF]);
4559
+ const stopCanvasRendering = React14.useCallback(() => {
4560
+ isDrawingRef.current = false;
4561
+ if (animationFrameRef.current) {
4562
+ cancelAnimationFrame(animationFrameRef.current);
4563
+ animationFrameRef.current = null;
4564
+ }
4565
+ if (intervalRef.current) {
4566
+ clearInterval(intervalRef.current);
4567
+ intervalRef.current = null;
4568
+ }
4569
+ }, []);
4570
+ React14.useEffect(() => {
4571
+ const video = videoRef.current;
4572
+ if (!video || !cropping || !shouldPlay) {
4573
+ stopCanvasRendering();
4574
+ return;
4575
+ }
4576
+ const handlePlay = () => {
4577
+ if (cropping) {
4578
+ startCanvasRendering();
4579
+ }
4580
+ };
4581
+ const handlePause = () => {
4582
+ stopCanvasRendering();
4583
+ };
4584
+ const handleEnded = () => {
4585
+ stopCanvasRendering();
4586
+ };
4587
+ video.addEventListener("play", handlePlay);
4588
+ video.addEventListener("pause", handlePause);
4589
+ video.addEventListener("ended", handleEnded);
4590
+ if (!video.paused && video.readyState >= 2) {
4591
+ startCanvasRendering();
4592
+ }
4593
+ return () => {
4594
+ stopCanvasRendering();
4595
+ video.removeEventListener("play", handlePlay);
4596
+ video.removeEventListener("pause", handlePause);
4597
+ video.removeEventListener("ended", handleEnded);
4598
+ };
4599
+ }, [videoRef, cropping, shouldPlay, startCanvasRendering, stopCanvasRendering]);
4600
+ return {
4601
+ ...hlsState,
4602
+ isCanvasRendering: isDrawingRef.current
4603
+ };
4604
+ }
4250
4605
  function useThreads() {
4251
4606
  const supabase = _getSupabaseInstance();
4252
4607
  const fetcher = async (key) => {
@@ -4926,327 +5281,96 @@ function useNavigation(customNavigate) {
4926
5281
  goToProfile,
4927
5282
  navigate
4928
5283
  };
4929
- }
4930
- function useWorkspaceNavigation() {
4931
- const { defaultTimezone } = useDateTimeConfig();
4932
- const getWorkspaceNavigationParams3 = React14.useCallback(
4933
- (workspaceId, options) => {
4934
- let dateToUse = options?.date;
4935
- if (!dateToUse && options?.useCurrentDate) {
4936
- dateToUse = getOperationalDate(defaultTimezone || "UTC");
4937
- }
4938
- return {
4939
- workspaceId,
4940
- date: dateToUse,
4941
- shift: options?.shift,
4942
- sourceType: options?.sourceType
4943
- };
4944
- },
4945
- [defaultTimezone]
4946
- );
4947
- return {
4948
- getWorkspaceNavigationParams: getWorkspaceNavigationParams3
4949
- };
4950
- }
4951
- function useDateFormatter() {
4952
- const { defaultTimezone, defaultLocale, dateFormatOptions, timeFormatOptions, dateTimeFormatOptions } = useDateTimeConfig();
4953
- const formatDate = React14.useCallback(
4954
- (date, formatString) => {
4955
- const dateObj = typeof date === "string" ? dateFns.parseISO(date) : date;
4956
- if (!dateFns.isValid(dateObj)) return "Invalid Date";
4957
- const tz = defaultTimezone || "UTC";
4958
- if (formatString) {
4959
- return dateFnsTz.formatInTimeZone(dateObj, tz, formatString);
4960
- }
4961
- const effectiveOptions = dateFormatOptions || { year: "numeric", month: "short", day: "numeric" };
4962
- return new Intl.DateTimeFormat(defaultLocale || "en-US", effectiveOptions).format(dateObj);
4963
- },
4964
- [defaultTimezone, defaultLocale, dateFormatOptions]
4965
- );
4966
- const formatTime2 = React14.useCallback(
4967
- (date, formatString) => {
4968
- const dateObj = typeof date === "string" ? dateFns.parseISO(date) : date;
4969
- if (!dateFns.isValid(dateObj)) return "Invalid Time";
4970
- const tz = defaultTimezone || "UTC";
4971
- if (formatString) {
4972
- return dateFnsTz.formatInTimeZone(dateObj, tz, formatString);
4973
- }
4974
- const effectiveOptions = timeFormatOptions || { hour: "numeric", minute: "numeric" };
4975
- return new Intl.DateTimeFormat(defaultLocale || "en-US", effectiveOptions).format(dateObj);
4976
- },
4977
- [defaultTimezone, defaultLocale, timeFormatOptions]
4978
- );
4979
- const formatDateTime = React14.useCallback(
4980
- (date, formatString) => {
4981
- const dateObj = typeof date === "string" ? dateFns.parseISO(date) : date;
4982
- if (!dateFns.isValid(dateObj)) return "Invalid Date/Time";
4983
- const tz = defaultTimezone || "UTC";
4984
- if (formatString) {
4985
- return dateFnsTz.formatInTimeZone(dateObj, tz, formatString);
4986
- }
4987
- const effectiveOptions = dateTimeFormatOptions || { year: "numeric", month: "short", day: "numeric", hour: "numeric", minute: "numeric" };
4988
- return new Intl.DateTimeFormat(defaultLocale || "en-US", effectiveOptions).format(dateObj);
4989
- },
4990
- [defaultTimezone, defaultLocale, dateTimeFormatOptions]
4991
- );
4992
- const getNow = React14.useCallback(() => {
4993
- return /* @__PURE__ */ new Date();
4994
- }, []);
4995
- return {
4996
- formatDate,
4997
- formatTime: formatTime2,
4998
- formatDateTime,
4999
- getNow,
5000
- timezone: defaultTimezone || "UTC",
5001
- locale: defaultLocale || "en-US"
5002
- };
5003
- }
5004
- var useFormatNumber = () => {
5005
- const { defaultLocale } = useDateTimeConfig();
5006
- const formatNumber = React14.useCallback(
5007
- (value, options) => {
5008
- try {
5009
- return new Intl.NumberFormat(defaultLocale || "en-US", options).format(value);
5010
- } catch (error) {
5011
- console.error("Error formatting number:", error);
5012
- return String(value);
5013
- }
5014
- },
5015
- [defaultLocale]
5016
- );
5017
- return { formatNumber };
5018
- };
5019
-
5020
- // src/lib/utils/dashboardReload.ts
5021
- var createThrottledReload = (interval = 5e3) => {
5022
- let last = 0;
5023
- let queued = false;
5024
- const doReload = () => {
5025
- if (typeof window !== "undefined") {
5026
- window.location.reload();
5027
- }
5028
- };
5029
- return () => {
5030
- const now2 = Date.now();
5031
- if (now2 - last >= interval) {
5032
- last = now2;
5033
- doReload();
5034
- } else if (!queued) {
5035
- queued = true;
5036
- setTimeout(() => {
5037
- queued = false;
5038
- last = Date.now();
5039
- doReload();
5040
- }, interval - (now2 - last));
5041
- }
5042
- };
5043
- };
5044
- var throttledReloadDashboard = createThrottledReload(5e3);
5045
-
5046
- // src/lib/hooks/useHlsStream.ts
5047
- var HLS_CONFIG = {
5048
- maxBufferLength: 8,
5049
- maxMaxBufferLength: 15,
5050
- lowLatencyMode: false,
5051
- enableWorker: true,
5052
- // Retry + timeout tuned for quick recovery
5053
- manifestLoadingMaxRetry: 4,
5054
- levelLoadingMaxRetry: 3,
5055
- fragLoadingMaxRetry: 4,
5056
- manifestLoadingRetryDelay: 500,
5057
- levelLoadingRetryDelay: 500,
5058
- fragLoadingRetryDelay: 500,
5059
- manifestLoadingTimeOut: 1e4,
5060
- levelLoadingTimeOut: 8e3,
5061
- fragLoadingTimeOut: 1e4,
5062
- liveSyncDurationCount: 2
5063
- // Follow live edge aggressively
5064
- };
5065
- function useHlsStream(videoRef, { src, shouldPlay, onFatalError }) {
5066
- const [restartKey, setRestartKey] = React14.useState(0);
5067
- const hlsRef = React14.useRef(null);
5068
- const stallCheckIntervalRef = React14.useRef(null);
5069
- const noProgressTimerRef = React14.useRef(null);
5070
- const lastTimeUpdateRef = React14.useRef(0);
5071
- const softRestartCountRef = React14.useRef(0);
5072
- const isNativeHlsRef = React14.useRef(false);
5073
- const waitingTimerRef = React14.useRef(null);
5074
- const cleanup = () => {
5075
- if (stallCheckIntervalRef.current) {
5076
- clearInterval(stallCheckIntervalRef.current);
5077
- stallCheckIntervalRef.current = null;
5078
- }
5079
- if (noProgressTimerRef.current) {
5080
- clearTimeout(noProgressTimerRef.current);
5081
- noProgressTimerRef.current = null;
5082
- }
5083
- if (waitingTimerRef.current) {
5084
- clearTimeout(waitingTimerRef.current);
5085
- waitingTimerRef.current = null;
5086
- }
5087
- if (hlsRef.current) {
5088
- hlsRef.current.destroy();
5089
- hlsRef.current = null;
5090
- }
5091
- const video = videoRef.current;
5092
- if (video) {
5093
- video.pause();
5094
- video.removeAttribute("src");
5095
- video.load();
5096
- video.removeEventListener("waiting", handleWaiting);
5097
- video.removeEventListener("timeupdate", handleTimeUpdate);
5098
- video.removeEventListener("error", handleNativeError);
5099
- }
5100
- lastTimeUpdateRef.current = 0;
5101
- softRestartCountRef.current = 0;
5102
- };
5103
- const seekToLiveEdge = () => {
5104
- const hls = hlsRef.current;
5105
- const video = videoRef.current;
5106
- if (!hls || !video) return;
5107
- if (hls.liveSyncPosition !== null && hls.liveSyncPosition !== void 0) {
5108
- video.currentTime = hls.liveSyncPosition;
5109
- } else if (hls.levels?.[hls.currentLevel]?.details) {
5110
- const levelDetails = hls.levels[hls.currentLevel].details;
5111
- if (levelDetails && levelDetails.edge !== void 0) {
5112
- video.currentTime = Math.max(0, levelDetails.edge - 5);
5113
- }
5114
- }
5115
- };
5116
- const softRestart = (reason) => {
5117
- console.warn(`[HLS] Soft restart: ${reason}`);
5118
- const hls = hlsRef.current;
5119
- if (!hls) return;
5120
- try {
5121
- hls.stopLoad();
5122
- hls.startLoad(-1);
5123
- seekToLiveEdge();
5124
- softRestartCountRef.current++;
5125
- if (softRestartCountRef.current >= 5) {
5126
- hardRestart(`${reason} (escalated after ${softRestartCountRef.current} soft restarts)`);
5127
- }
5128
- } catch (error) {
5129
- console.error("[HLS] Soft restart failed:", error);
5130
- hardRestart(`${reason} (soft restart error)`);
5131
- }
5132
- };
5133
- const hardRestart = (reason) => {
5134
- console.warn(`[HLS] Hard restart: ${reason}`);
5135
- cleanup();
5136
- setRestartKey((k) => k + 1);
5137
- softRestartCountRef.current = 0;
5138
- if (reason.includes("hard restart") || reason.includes("native video error")) {
5139
- if (onFatalError) {
5140
- onFatalError();
5141
- } else {
5142
- throttledReloadDashboard();
5143
- }
5144
- }
5145
- };
5146
- const handleWaiting = () => {
5147
- if (isNativeHlsRef.current) return;
5148
- console.log("[HLS] Video waiting (buffer underrun)");
5149
- if (waitingTimerRef.current) {
5150
- clearTimeout(waitingTimerRef.current);
5151
- }
5152
- waitingTimerRef.current = setTimeout(() => {
5153
- const video = videoRef.current;
5154
- if (video && video.readyState < 3) {
5155
- softRestart("waiting timeout");
5156
- }
5157
- }, 1e4);
5158
- };
5159
- const handleTimeUpdate = () => {
5160
- const video = videoRef.current;
5161
- if (!video) return;
5162
- lastTimeUpdateRef.current = video.currentTime;
5163
- if (waitingTimerRef.current) {
5164
- clearTimeout(waitingTimerRef.current);
5165
- waitingTimerRef.current = null;
5166
- }
5167
- };
5168
- const handleNativeError = () => {
5169
- console.error("[HLS] Native video error");
5170
- hardRestart("native video error");
5171
- };
5172
- const startStallDetection = () => {
5173
- if (isNativeHlsRef.current) return;
5174
- stallCheckIntervalRef.current = setInterval(() => {
5175
- const video = videoRef.current;
5176
- if (!video || video.paused || video.ended) return;
5177
- const currentTime = video.currentTime;
5178
- const lastTime = lastTimeUpdateRef.current;
5179
- if (Math.abs(currentTime - lastTime) < 0.1 && video.readyState >= 2) {
5180
- console.warn("[HLS] Playback stall detected");
5181
- if (!noProgressTimerRef.current) {
5182
- noProgressTimerRef.current = setTimeout(() => {
5183
- softRestart("playback stall");
5184
- noProgressTimerRef.current = null;
5185
- }, 8e3);
5186
- }
5187
- } else {
5188
- if (noProgressTimerRef.current) {
5189
- clearTimeout(noProgressTimerRef.current);
5190
- noProgressTimerRef.current = null;
5191
- }
5284
+ }
5285
+ function useWorkspaceNavigation() {
5286
+ const { defaultTimezone } = useDateTimeConfig();
5287
+ const getWorkspaceNavigationParams3 = React14.useCallback(
5288
+ (workspaceId, options) => {
5289
+ let dateToUse = options?.date;
5290
+ if (!dateToUse && options?.useCurrentDate) {
5291
+ dateToUse = getOperationalDate(defaultTimezone || "UTC");
5192
5292
  }
5193
- }, 7e3);
5293
+ return {
5294
+ workspaceId,
5295
+ date: dateToUse,
5296
+ shift: options?.shift,
5297
+ sourceType: options?.sourceType
5298
+ };
5299
+ },
5300
+ [defaultTimezone]
5301
+ );
5302
+ return {
5303
+ getWorkspaceNavigationParams: getWorkspaceNavigationParams3
5194
5304
  };
5195
- React14.useEffect(() => {
5196
- if (!src || !shouldPlay) {
5197
- cleanup();
5198
- return;
5199
- }
5200
- const video = videoRef.current;
5201
- if (!video) return;
5202
- isNativeHlsRef.current = video.canPlayType("application/vnd.apple.mpegurl") === "probably";
5203
- if (Hls2__default.default.isSupported() && !isNativeHlsRef.current) {
5204
- const hls = new Hls2__default.default(HLS_CONFIG);
5205
- hlsRef.current = hls;
5206
- hls.attachMedia(video);
5207
- hls.loadSource(src);
5208
- hls.on(Hls2__default.default.Events.ERROR, (_, data) => {
5209
- if (!data.fatal) return;
5210
- console.error("[HLS] Fatal error:", data.type, data.details);
5211
- if (data.response?.code === 404) {
5212
- hardRestart("404 hard restart");
5213
- return;
5214
- }
5215
- switch (data.type) {
5216
- case Hls2__default.default.ErrorTypes.NETWORK_ERROR:
5217
- case Hls2__default.default.ErrorTypes.MEDIA_ERROR:
5218
- softRestart(`${data.type}: ${data.details}`);
5219
- break;
5220
- default:
5221
- hardRestart(`Fatal ${data.type}: ${data.details}`);
5222
- break;
5223
- }
5224
- });
5225
- hls.on(Hls2__default.default.Events.MANIFEST_PARSED, () => {
5226
- video.play().catch((err) => {
5227
- console.error("[HLS] Play failed:", err);
5228
- });
5229
- });
5230
- video.addEventListener("waiting", handleWaiting);
5231
- video.addEventListener("timeupdate", handleTimeUpdate);
5232
- startStallDetection();
5233
- } else if (isNativeHlsRef.current) {
5234
- console.log("[HLS] Using native HLS");
5235
- video.src = src;
5236
- video.addEventListener("error", handleNativeError);
5237
- video.play().catch((err) => {
5238
- console.error("[HLS] Native play failed:", err);
5239
- });
5240
- } else {
5241
- console.error("[HLS] HLS not supported");
5242
- }
5243
- return cleanup;
5244
- }, [src, shouldPlay, restartKey, onFatalError]);
5305
+ }
5306
+ function useDateFormatter() {
5307
+ const { defaultTimezone, defaultLocale, dateFormatOptions, timeFormatOptions, dateTimeFormatOptions } = useDateTimeConfig();
5308
+ const formatDate = React14.useCallback(
5309
+ (date, formatString) => {
5310
+ const dateObj = typeof date === "string" ? dateFns.parseISO(date) : date;
5311
+ if (!dateFns.isValid(dateObj)) return "Invalid Date";
5312
+ const tz = defaultTimezone || "UTC";
5313
+ if (formatString) {
5314
+ return dateFnsTz.formatInTimeZone(dateObj, tz, formatString);
5315
+ }
5316
+ const effectiveOptions = dateFormatOptions || { year: "numeric", month: "short", day: "numeric" };
5317
+ return new Intl.DateTimeFormat(defaultLocale || "en-US", effectiveOptions).format(dateObj);
5318
+ },
5319
+ [defaultTimezone, defaultLocale, dateFormatOptions]
5320
+ );
5321
+ const formatTime2 = React14.useCallback(
5322
+ (date, formatString) => {
5323
+ const dateObj = typeof date === "string" ? dateFns.parseISO(date) : date;
5324
+ if (!dateFns.isValid(dateObj)) return "Invalid Time";
5325
+ const tz = defaultTimezone || "UTC";
5326
+ if (formatString) {
5327
+ return dateFnsTz.formatInTimeZone(dateObj, tz, formatString);
5328
+ }
5329
+ const effectiveOptions = timeFormatOptions || { hour: "numeric", minute: "numeric" };
5330
+ return new Intl.DateTimeFormat(defaultLocale || "en-US", effectiveOptions).format(dateObj);
5331
+ },
5332
+ [defaultTimezone, defaultLocale, timeFormatOptions]
5333
+ );
5334
+ const formatDateTime = React14.useCallback(
5335
+ (date, formatString) => {
5336
+ const dateObj = typeof date === "string" ? dateFns.parseISO(date) : date;
5337
+ if (!dateFns.isValid(dateObj)) return "Invalid Date/Time";
5338
+ const tz = defaultTimezone || "UTC";
5339
+ if (formatString) {
5340
+ return dateFnsTz.formatInTimeZone(dateObj, tz, formatString);
5341
+ }
5342
+ const effectiveOptions = dateTimeFormatOptions || { year: "numeric", month: "short", day: "numeric", hour: "numeric", minute: "numeric" };
5343
+ return new Intl.DateTimeFormat(defaultLocale || "en-US", effectiveOptions).format(dateObj);
5344
+ },
5345
+ [defaultTimezone, defaultLocale, dateTimeFormatOptions]
5346
+ );
5347
+ const getNow = React14.useCallback(() => {
5348
+ return /* @__PURE__ */ new Date();
5349
+ }, []);
5245
5350
  return {
5246
- restartKey,
5247
- isNativeHls: isNativeHlsRef.current
5351
+ formatDate,
5352
+ formatTime: formatTime2,
5353
+ formatDateTime,
5354
+ getNow,
5355
+ timezone: defaultTimezone || "UTC",
5356
+ locale: defaultLocale || "en-US"
5248
5357
  };
5249
5358
  }
5359
+ var useFormatNumber = () => {
5360
+ const { defaultLocale } = useDateTimeConfig();
5361
+ const formatNumber = React14.useCallback(
5362
+ (value, options) => {
5363
+ try {
5364
+ return new Intl.NumberFormat(defaultLocale || "en-US", options).format(value);
5365
+ } catch (error) {
5366
+ console.error("Error formatting number:", error);
5367
+ return String(value);
5368
+ }
5369
+ },
5370
+ [defaultLocale]
5371
+ );
5372
+ return { formatNumber };
5373
+ };
5250
5374
 
5251
5375
  // src/lib/utils/api.ts
5252
5376
  var apiUtils = {
@@ -14606,16 +14730,16 @@ function createProjectionNode2({ attachResizeListener, defaultParent, measureScr
14606
14730
  if (!this.isVisible) {
14607
14731
  return hiddenVisibility;
14608
14732
  }
14609
- const styles = {
14733
+ const styles2 = {
14610
14734
  visibility: ""
14611
14735
  };
14612
14736
  const transformTemplate = this.getTransformTemplate();
14613
14737
  if (this.needsReset) {
14614
14738
  this.needsReset = false;
14615
- styles.opacity = "";
14616
- styles.pointerEvents = resolveMotionValue(styleProp === null || styleProp === void 0 ? void 0 : styleProp.pointerEvents) || "";
14617
- styles.transform = transformTemplate ? transformTemplate(this.latestValues, "") : "none";
14618
- return styles;
14739
+ styles2.opacity = "";
14740
+ styles2.pointerEvents = resolveMotionValue(styleProp === null || styleProp === void 0 ? void 0 : styleProp.pointerEvents) || "";
14741
+ styles2.transform = transformTemplate ? transformTemplate(this.latestValues, "") : "none";
14742
+ return styles2;
14619
14743
  }
14620
14744
  const lead = this.getLead();
14621
14745
  if (!this.projectionDelta || !this.layout || !lead.target) {
@@ -14632,35 +14756,35 @@ function createProjectionNode2({ attachResizeListener, defaultParent, measureScr
14632
14756
  }
14633
14757
  const valuesToRender = lead.animationValues || lead.latestValues;
14634
14758
  this.applyTransformsToTarget();
14635
- styles.transform = buildProjectionTransform(this.projectionDeltaWithTransform, this.treeScale, valuesToRender);
14759
+ styles2.transform = buildProjectionTransform(this.projectionDeltaWithTransform, this.treeScale, valuesToRender);
14636
14760
  if (transformTemplate) {
14637
- styles.transform = transformTemplate(valuesToRender, styles.transform);
14761
+ styles2.transform = transformTemplate(valuesToRender, styles2.transform);
14638
14762
  }
14639
14763
  const { x, y } = this.projectionDelta;
14640
- styles.transformOrigin = `${x.origin * 100}% ${y.origin * 100}% 0`;
14764
+ styles2.transformOrigin = `${x.origin * 100}% ${y.origin * 100}% 0`;
14641
14765
  if (lead.animationValues) {
14642
- styles.opacity = lead === this ? (_b = (_a = valuesToRender.opacity) !== null && _a !== void 0 ? _a : this.latestValues.opacity) !== null && _b !== void 0 ? _b : 1 : this.preserveOpacity ? this.latestValues.opacity : valuesToRender.opacityExit;
14766
+ styles2.opacity = lead === this ? (_b = (_a = valuesToRender.opacity) !== null && _a !== void 0 ? _a : this.latestValues.opacity) !== null && _b !== void 0 ? _b : 1 : this.preserveOpacity ? this.latestValues.opacity : valuesToRender.opacityExit;
14643
14767
  } else {
14644
- styles.opacity = lead === this ? valuesToRender.opacity !== void 0 ? valuesToRender.opacity : "" : valuesToRender.opacityExit !== void 0 ? valuesToRender.opacityExit : 0;
14768
+ styles2.opacity = lead === this ? valuesToRender.opacity !== void 0 ? valuesToRender.opacity : "" : valuesToRender.opacityExit !== void 0 ? valuesToRender.opacityExit : 0;
14645
14769
  }
14646
14770
  for (const key in scaleCorrectors) {
14647
14771
  if (valuesToRender[key] === void 0)
14648
14772
  continue;
14649
14773
  const { correct, applyTo } = scaleCorrectors[key];
14650
- const corrected = styles.transform === "none" ? valuesToRender[key] : correct(valuesToRender[key], lead);
14774
+ const corrected = styles2.transform === "none" ? valuesToRender[key] : correct(valuesToRender[key], lead);
14651
14775
  if (applyTo) {
14652
14776
  const num = applyTo.length;
14653
14777
  for (let i = 0; i < num; i++) {
14654
- styles[applyTo[i]] = corrected;
14778
+ styles2[applyTo[i]] = corrected;
14655
14779
  }
14656
14780
  } else {
14657
- styles[key] = corrected;
14781
+ styles2[key] = corrected;
14658
14782
  }
14659
14783
  }
14660
14784
  if (this.options.layoutId) {
14661
- styles.pointerEvents = lead === this ? resolveMotionValue(styleProp === null || styleProp === void 0 ? void 0 : styleProp.pointerEvents) || "" : "none";
14785
+ styles2.pointerEvents = lead === this ? resolveMotionValue(styleProp === null || styleProp === void 0 ? void 0 : styleProp.pointerEvents) || "" : "none";
14662
14786
  }
14663
- return styles;
14787
+ return styles2;
14664
14788
  }
14665
14789
  clearSnapshot() {
14666
14790
  this.resumeFrom = this.snapshot = void 0;
@@ -16767,23 +16891,35 @@ var HourlyOutputChart = ({
16767
16891
  const [animatedData, setAnimatedData] = React14__namespace.default.useState(Array(SHIFT_DURATION).fill(0));
16768
16892
  const prevDataRef = React14__namespace.default.useRef(Array(SHIFT_DURATION).fill(0));
16769
16893
  const animationFrameRef = React14__namespace.default.useRef(null);
16770
- const [shouldAnimateIdle, setShouldAnimateIdle] = React14__namespace.default.useState(false);
16894
+ const [idleBarState, setIdleBarState] = React14__namespace.default.useState({
16895
+ visible: showIdleTime,
16896
+ key: 0,
16897
+ shouldAnimate: false
16898
+ });
16771
16899
  const prevShowIdleTimeRef = React14__namespace.default.useRef(showIdleTime);
16772
- const animationTimeoutRef = React14__namespace.default.useRef(null);
16900
+ const stateUpdateTimeoutRef = React14__namespace.default.useRef(null);
16773
16901
  React14__namespace.default.useEffect(() => {
16902
+ if (stateUpdateTimeoutRef.current) {
16903
+ clearTimeout(stateUpdateTimeoutRef.current);
16904
+ }
16774
16905
  if (showIdleTime && !prevShowIdleTimeRef.current) {
16775
- setShouldAnimateIdle(true);
16776
- if (animationTimeoutRef.current) {
16777
- clearTimeout(animationTimeoutRef.current);
16778
- }
16779
- animationTimeoutRef.current = setTimeout(() => {
16780
- setShouldAnimateIdle(false);
16781
- }, 1e3);
16906
+ requestAnimationFrame(() => {
16907
+ setIdleBarState({
16908
+ visible: true,
16909
+ key: Date.now(),
16910
+ shouldAnimate: true
16911
+ });
16912
+ stateUpdateTimeoutRef.current = setTimeout(() => {
16913
+ setIdleBarState((prev) => ({ ...prev, shouldAnimate: false }));
16914
+ }, 1100);
16915
+ });
16916
+ } else if (!showIdleTime && prevShowIdleTimeRef.current) {
16917
+ setIdleBarState((prev) => ({ ...prev, visible: false }));
16782
16918
  }
16783
16919
  prevShowIdleTimeRef.current = showIdleTime;
16784
16920
  return () => {
16785
- if (animationTimeoutRef.current) {
16786
- clearTimeout(animationTimeoutRef.current);
16921
+ if (stateUpdateTimeoutRef.current) {
16922
+ clearTimeout(stateUpdateTimeoutRef.current);
16787
16923
  }
16788
16924
  };
16789
16925
  }, [showIdleTime]);
@@ -16876,6 +17012,55 @@ var HourlyOutputChart = ({
16876
17012
  };
16877
17013
  });
16878
17014
  }, [animatedData, data, pphThreshold, idleTimeHourly, shiftStartTime.hour, formatHour, formatTimeRange]);
17015
+ const IdleBar = React14__namespace.default.useMemo(() => {
17016
+ if (!idleBarState.visible) return null;
17017
+ return /* @__PURE__ */ jsxRuntime.jsx(
17018
+ recharts.Bar,
17019
+ {
17020
+ dataKey: "idleMinutes",
17021
+ yAxisId: "idle",
17022
+ maxBarSize: 35,
17023
+ radius: [10, 10, 0, 0],
17024
+ fill: "url(#idlePattern)",
17025
+ opacity: 0.7,
17026
+ isAnimationActive: idleBarState.shouldAnimate,
17027
+ animationBegin: 0,
17028
+ animationDuration: 1e3,
17029
+ animationEasing: "ease-out",
17030
+ children: /* @__PURE__ */ jsxRuntime.jsx(
17031
+ recharts.LabelList,
17032
+ {
17033
+ dataKey: "idleMinutes",
17034
+ position: "top",
17035
+ content: (props) => {
17036
+ const { x, y, width, value } = props;
17037
+ if (!value || value === 0) return null;
17038
+ return /* @__PURE__ */ jsxRuntime.jsxs(
17039
+ "text",
17040
+ {
17041
+ x: x + width / 2,
17042
+ y: y - 2,
17043
+ textAnchor: "middle",
17044
+ fontSize: "9",
17045
+ fontWeight: "600",
17046
+ fill: "#6b7280",
17047
+ style: {
17048
+ opacity: 1,
17049
+ pointerEvents: "none"
17050
+ },
17051
+ children: [
17052
+ value,
17053
+ "m"
17054
+ ]
17055
+ }
17056
+ );
17057
+ }
17058
+ }
17059
+ )
17060
+ },
17061
+ `idle-bar-${idleBarState.key}`
17062
+ );
17063
+ }, [idleBarState.visible, idleBarState.key, idleBarState.shouldAnimate]);
16879
17064
  const maxYValue = Math.ceil(pphThreshold * 1.5);
16880
17065
  const generateYAxisTicks = () => {
16881
17066
  const targetValue = Math.round(pphThreshold);
@@ -17133,51 +17318,7 @@ var HourlyOutputChart = ({
17133
17318
  ]
17134
17319
  }
17135
17320
  ),
17136
- showIdleTime && /* @__PURE__ */ jsxRuntime.jsx(
17137
- recharts.Bar,
17138
- {
17139
- dataKey: "idleMinutes",
17140
- yAxisId: "idle",
17141
- maxBarSize: 35,
17142
- radius: [10, 10, 0, 0],
17143
- fill: "url(#idlePattern)",
17144
- opacity: 0.7,
17145
- isAnimationActive: shouldAnimateIdle,
17146
- animationBegin: shouldAnimateIdle ? 200 : 0,
17147
- animationDuration: shouldAnimateIdle ? 800 : 0,
17148
- animationEasing: "ease-out",
17149
- children: /* @__PURE__ */ jsxRuntime.jsx(
17150
- recharts.LabelList,
17151
- {
17152
- dataKey: "idleMinutes",
17153
- position: "top",
17154
- content: (props) => {
17155
- const { x, y, width, value } = props;
17156
- if (!value || value === 0) return null;
17157
- return /* @__PURE__ */ jsxRuntime.jsxs(
17158
- "text",
17159
- {
17160
- x: x + width / 2,
17161
- y: y - 2,
17162
- textAnchor: "middle",
17163
- fontSize: "9",
17164
- fontWeight: "600",
17165
- fill: "#6b7280",
17166
- style: {
17167
- opacity: 1,
17168
- pointerEvents: "none"
17169
- },
17170
- children: [
17171
- value,
17172
- "m"
17173
- ]
17174
- }
17175
- );
17176
- }
17177
- }
17178
- )
17179
- }
17180
- ),
17321
+ IdleBar,
17181
17322
  /* @__PURE__ */ jsxRuntime.jsx("defs", { children: /* @__PURE__ */ jsxRuntime.jsxs("pattern", { id: "idlePattern", patternUnits: "userSpaceOnUse", width: "4", height: "4", children: [
17182
17323
  /* @__PURE__ */ jsxRuntime.jsx("rect", { width: "4", height: "4", fill: "#9ca3af", opacity: "0.2" }),
17183
17324
  /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M 0,4 l 4,-4 M -1,1 l 2,-2 M 3,5 l 2,-2", stroke: "#6b7280", strokeWidth: "0.5", opacity: "0.3" })
@@ -17188,15 +17329,15 @@ var HourlyOutputChart = ({
17188
17329
  renderLegend()
17189
17330
  ] });
17190
17331
  };
17191
- var TREND_STYLES = {
17192
- 0: { arrow: "\u2193", color: "text-red-500 font-bold text-shadow" },
17193
- // Down
17194
- 1: { arrow: "\u2013", color: "text-gray-500 font-bold text-shadow" },
17195
- // Unchanged
17196
- 2: { arrow: "\u2191", color: "text-green-500 font-bold text-shadow" }
17197
- // Up
17198
- };
17199
- var getTrendArrowAndColor = (trend) => TREND_STYLES[trend] || { arrow: "", color: "" };
17332
+ function getTrendArrowAndColor(trend) {
17333
+ if (trend > 0) {
17334
+ return { arrow: "\u2191", color: "text-green-400" };
17335
+ } else if (trend < 0) {
17336
+ return { arrow: "\u2193", color: "text-red-400" };
17337
+ } else {
17338
+ return { arrow: "\u2192", color: "text-gray-400" };
17339
+ }
17340
+ }
17200
17341
  var VideoCard = React14__namespace.default.memo(({
17201
17342
  workspace,
17202
17343
  hlsUrl,
@@ -17204,14 +17345,29 @@ var VideoCard = React14__namespace.default.memo(({
17204
17345
  onClick,
17205
17346
  onFatalError,
17206
17347
  isVeryLowEfficiency = false,
17348
+ cropping,
17349
+ canvasFps = 30,
17350
+ useRAF = true,
17207
17351
  className = ""
17208
17352
  }) => {
17209
17353
  const videoRef = React14.useRef(null);
17210
- useHlsStream(videoRef, {
17211
- src: hlsUrl,
17212
- shouldPlay,
17213
- onFatalError: onFatalError ?? (() => throttledReloadDashboard())
17214
- });
17354
+ const canvasRef = React14.useRef(null);
17355
+ if (cropping) {
17356
+ useHlsStreamWithCropping(videoRef, canvasRef, {
17357
+ src: hlsUrl,
17358
+ shouldPlay,
17359
+ cropping,
17360
+ canvasFps,
17361
+ useRAF,
17362
+ onFatalError: onFatalError ?? (() => throttledReloadDashboard())
17363
+ });
17364
+ } else {
17365
+ useHlsStream(videoRef, {
17366
+ src: hlsUrl,
17367
+ shouldPlay,
17368
+ onFatalError: onFatalError ?? (() => throttledReloadDashboard())
17369
+ });
17370
+ }
17215
17371
  const displayName = getWorkspaceDisplayName(workspace.workspace_name);
17216
17372
  workspace.workspace_uuid || workspace.workspace_name;
17217
17373
  const getEfficiencyOverlayColor = (efficiency) => {
@@ -17266,17 +17422,26 @@ var VideoCard = React14__namespace.default.memo(({
17266
17422
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Camera, { className: "w-6 h-6 text-gray-500" }),
17267
17423
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-gray-500 mt-1", children: "Loading..." })
17268
17424
  ] }) }),
17269
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 z-10", children: /* @__PURE__ */ jsxRuntime.jsx(
17270
- "video",
17271
- {
17272
- ref: videoRef,
17273
- className: "h-full w-full object-cover",
17274
- playsInline: true,
17275
- muted: true,
17276
- disablePictureInPicture: true,
17277
- controlsList: "nodownload noplaybackrate"
17278
- }
17279
- ) }),
17425
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute inset-0 z-10", children: [
17426
+ /* @__PURE__ */ jsxRuntime.jsx(
17427
+ "video",
17428
+ {
17429
+ ref: videoRef,
17430
+ className: `h-full w-full object-cover ${cropping ? "hidden" : ""}`,
17431
+ playsInline: true,
17432
+ muted: true,
17433
+ disablePictureInPicture: true,
17434
+ controlsList: "nodownload noplaybackrate"
17435
+ }
17436
+ ),
17437
+ cropping && /* @__PURE__ */ jsxRuntime.jsx(
17438
+ "canvas",
17439
+ {
17440
+ ref: canvasRef,
17441
+ className: "h-full w-full object-cover"
17442
+ }
17443
+ )
17444
+ ] }),
17280
17445
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: `absolute inset-0 z-20 pointer-events-none ${efficiencyOverlayClass}` }),
17281
17446
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute top-2 right-2 z-30 bg-black/70 backdrop-blur-sm rounded px-2 py-0.5 text-white text-xs font-semibold border border-white/10", children: [
17282
17447
  Math.round(workspace.efficiency),
@@ -17312,7 +17477,7 @@ var VideoCard = React14__namespace.default.memo(({
17312
17477
  }
17313
17478
  );
17314
17479
  }, (prevProps, nextProps) => {
17315
- return prevProps.workspace.workspace_uuid === nextProps.workspace.workspace_uuid && prevProps.workspace.workspace_name === nextProps.workspace.workspace_name && Math.abs(prevProps.workspace.efficiency - nextProps.workspace.efficiency) < 1 && prevProps.hlsUrl === nextProps.hlsUrl && prevProps.shouldPlay === nextProps.shouldPlay;
17480
+ return prevProps.workspace.workspace_uuid === nextProps.workspace.workspace_uuid && prevProps.workspace.workspace_name === nextProps.workspace.workspace_name && Math.abs(prevProps.workspace.efficiency - nextProps.workspace.efficiency) < 1 && prevProps.hlsUrl === nextProps.hlsUrl && prevProps.shouldPlay === nextProps.shouldPlay && prevProps.cropping?.x === nextProps.cropping?.x && prevProps.cropping?.y === nextProps.cropping?.y && prevProps.cropping?.width === nextProps.cropping?.width && prevProps.cropping?.height === nextProps.cropping?.height;
17316
17481
  });
17317
17482
  VideoCard.displayName = "VideoCard";
17318
17483
  var DEFAULT_WORKSPACE_HLS_URLS = {
@@ -17340,6 +17505,8 @@ var VideoGridView = React14__namespace.default.memo(({
17340
17505
  const observerRef = React14.useRef(null);
17341
17506
  const [gridCols, setGridCols] = React14.useState(4);
17342
17507
  const [visibleWorkspaces, setVisibleWorkspaces] = React14.useState(/* @__PURE__ */ new Set());
17508
+ const videoConfig = useVideoConfig();
17509
+ const { cropping, canvasConfig } = videoConfig;
17343
17510
  const mergedVideoSources = {
17344
17511
  defaultHlsUrl: videoSources.defaultHlsUrl || DEFAULT_HLS_URL,
17345
17512
  workspaceHlsUrls: { ...DEFAULT_WORKSPACE_HLS_URLS, ...videoSources.workspaceHlsUrls }
@@ -17348,6 +17515,13 @@ var VideoGridView = React14__namespace.default.memo(({
17348
17515
  const wsName = workspaceName.toUpperCase();
17349
17516
  return mergedVideoSources.workspaceHlsUrls[wsName] || mergedVideoSources.defaultHlsUrl;
17350
17517
  }, [mergedVideoSources]);
17518
+ const getWorkspaceCropping = React14.useCallback((workspaceName) => {
17519
+ if (!cropping) return void 0;
17520
+ if (cropping.workspaceOverrides?.[workspaceName]) {
17521
+ return cropping.workspaceOverrides[workspaceName];
17522
+ }
17523
+ return cropping.default;
17524
+ }, [cropping]);
17351
17525
  const veryLowEfficiencyWorkspaces = React14.useMemo(() => {
17352
17526
  return new Set(
17353
17527
  workspaces.filter((w) => w.efficiency < 50 && w.efficiency >= 10).map((w) => w.workspace_name)
@@ -17486,6 +17660,7 @@ var VideoGridView = React14__namespace.default.memo(({
17486
17660
  const workspaceId = workspace.workspace_uuid || workspace.workspace_name;
17487
17661
  const isVisible = visibleWorkspaces.has(workspaceId);
17488
17662
  const isVeryLowEfficiency = veryLowEfficiencyWorkspaces.has(workspace.workspace_name);
17663
+ const workspaceCropping = getWorkspaceCropping(workspace.workspace_name);
17489
17664
  return /* @__PURE__ */ jsxRuntime.jsx(
17490
17665
  "div",
17491
17666
  {
@@ -17500,7 +17675,10 @@ var VideoGridView = React14__namespace.default.memo(({
17500
17675
  shouldPlay: isVisible,
17501
17676
  onClick: () => handleWorkspaceClick(workspace),
17502
17677
  onFatalError: throttledReloadDashboard,
17503
- isVeryLowEfficiency
17678
+ isVeryLowEfficiency,
17679
+ cropping: workspaceCropping,
17680
+ canvasFps: canvasConfig?.fps,
17681
+ useRAF: canvasConfig?.useRAF
17504
17682
  }
17505
17683
  )
17506
17684
  },
@@ -19613,6 +19791,32 @@ var WorkspaceCard = ({
19613
19791
  }
19614
19792
  );
19615
19793
  };
19794
+ var styles = `
19795
+ @keyframes fadeIn {
19796
+ from {
19797
+ opacity: 0;
19798
+ transform: translateY(-10px);
19799
+ }
19800
+ to {
19801
+ opacity: 1;
19802
+ transform: translateY(0);
19803
+ }
19804
+ }
19805
+
19806
+ .calendar-container {
19807
+ will-change: transform, opacity;
19808
+ animation: fadeIn 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
19809
+ }
19810
+
19811
+ .calendar-wrapper:not(.animation-complete) * {
19812
+ transition: none !important;
19813
+ }
19814
+
19815
+ .calendar-wrapper:not(.animation-complete) .group:hover {
19816
+ opacity: 1 !important;
19817
+ transform: scale(1) !important;
19818
+ }
19819
+ `;
19616
19820
  var WEEKDAYS2 = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
19617
19821
  var getTimeInZoneAsDate = (timezone) => {
19618
19822
  const time2 = getCurrentTimeInZone(timezone);
@@ -19631,20 +19835,20 @@ var WorkspaceHistoryCalendar = ({
19631
19835
  }) => {
19632
19836
  const { dateTimeConfig } = useDashboardConfig();
19633
19837
  const configuredTimezone = dateTimeConfig?.defaultTimezone || "Asia/Kolkata";
19838
+ const [animationComplete, setAnimationComplete] = React14.useState(false);
19839
+ React14.useEffect(() => {
19840
+ setAnimationComplete(false);
19841
+ const timer = setTimeout(() => {
19842
+ setAnimationComplete(true);
19843
+ }, 600);
19844
+ return () => clearTimeout(timer);
19845
+ }, [month, year]);
19634
19846
  const calendarData = React14.useMemo(() => {
19635
19847
  const startOfMonth = dateFnsTz.toZonedTime(new Date(year, month, 1), configuredTimezone);
19636
19848
  const endOfMonth = dateFnsTz.toZonedTime(new Date(year, month + 1, 0), configuredTimezone);
19637
19849
  const totalDays = endOfMonth.getDate();
19638
19850
  let startOffset = startOfMonth.getDay() - 1;
19639
19851
  if (startOffset === -1) startOffset = 6;
19640
- console.log("Calendar generation for:", {
19641
- month: month + 1,
19642
- year,
19643
- startOffset,
19644
- totalDays,
19645
- configuredTimezone
19646
- });
19647
- console.log("Data points received:", data);
19648
19852
  const calendar = Array(startOffset).fill(null);
19649
19853
  for (let day = 1; day <= totalDays; day++) {
19650
19854
  const currentDate = new Date(year, month, day);
@@ -19658,15 +19862,6 @@ var WorkspaceHistoryCalendar = ({
19658
19862
  const dataDate = new Date(d.date);
19659
19863
  const dataDateStr = dataDate.toISOString().split("T")[0];
19660
19864
  const matches = dataDateStr === dateToMatch;
19661
- if (day <= 3 && matches) {
19662
- console.log(`Found data match for day ${day}:`, {
19663
- dataDate: dataDate.toISOString(),
19664
- currentDate: currentTimezoneDate.toISOString(),
19665
- dataDateStr,
19666
- dateToMatch,
19667
- data: d
19668
- });
19669
- }
19670
19865
  return matches;
19671
19866
  });
19672
19867
  if (matchingData.length > 0) {
@@ -19677,32 +19872,10 @@ var WorkspaceHistoryCalendar = ({
19677
19872
  // Use the timezone-adjusted date for display
19678
19873
  });
19679
19874
  } else {
19680
- calendar.push({
19681
- date: currentTimezoneDate,
19682
- dayShift: {
19683
- efficiency: 0,
19684
- output: 0,
19685
- cycleTime: 0,
19686
- pph: 0,
19687
- pphThreshold: 0,
19688
- idealOutput: 0,
19689
- rank: 0,
19690
- idleTime: 0
19691
- },
19692
- nightShift: {
19693
- efficiency: 0,
19694
- output: 0,
19695
- cycleTime: 0,
19696
- pph: 0,
19697
- pphThreshold: 0,
19698
- idealOutput: 0,
19699
- rank: 0,
19700
- idleTime: 0
19701
- }
19702
- });
19875
+ calendar.push(null);
19703
19876
  }
19704
19877
  }
19705
- return calendar;
19878
+ return { calendar, startOffset };
19706
19879
  }, [data, month, year, configuredTimezone]);
19707
19880
  const monthlyMetrics = React14.useMemo(() => {
19708
19881
  const validDays = data.filter((day) => {
@@ -19719,15 +19892,17 @@ var WorkspaceHistoryCalendar = ({
19719
19892
  return [];
19720
19893
  }
19721
19894
  return [day.dayShift, day.nightShift];
19722
- }).filter((shift) => shift.efficiency > 0);
19895
+ });
19723
19896
  if (validShifts.length === 0) return null;
19724
19897
  const badShiftsCount = validShifts.filter((shift) => shift.efficiency < 75).length;
19898
+ const totalIdleTime = validShifts.reduce((sum, shift) => sum + shift.idleTime, 0);
19899
+ const avgIdleTime = Math.round(totalIdleTime / validShifts.length);
19725
19900
  return {
19726
19901
  avgEfficiency: Math.round(validShifts.reduce((sum, shift) => sum + shift.efficiency, 0) / validShifts.length),
19727
19902
  avgCycleTime: Math.round(validShifts.reduce((sum, shift) => sum + shift.cycleTime, 0) / validShifts.length),
19903
+ avgIdleTime,
19728
19904
  badDaysCount: badShiftsCount,
19729
- totalDays: validShifts.length,
19730
- avgIdleTime: Math.round(validShifts.reduce((sum, shift) => sum + (shift.idleTime || 0), 0) / validShifts.length)
19905
+ totalDays: validShifts.length
19731
19906
  };
19732
19907
  }, [data, month, year, configuredTimezone]);
19733
19908
  const handleDayClick = React14.useCallback((day, shift) => {
@@ -19775,7 +19950,6 @@ var WorkspaceHistoryCalendar = ({
19775
19950
  compareDate.setHours(0, 0, 0, 0);
19776
19951
  if (compareDate.getDay() === 0) return "bg-gray-300";
19777
19952
  if (compareDate > istNow) return "bg-gray-200";
19778
- if (efficiency < 10) return "bg-gray-300";
19779
19953
  if (efficiency >= 80) return "bg-[#00AB45]/90";
19780
19954
  if (efficiency >= 70) return "bg-[#FFB020]/90";
19781
19955
  return "bg-[#E34329]/90";
@@ -19814,38 +19988,55 @@ var WorkspaceHistoryCalendar = ({
19814
19988
  ] })
19815
19989
  ] }) });
19816
19990
  };
19817
- const renderDayCell = React14.useCallback((day) => {
19818
- if (!day) return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full border border-gray-100 rounded-lg bg-gray-50 transition-all duration-200 ease-in-out" });
19991
+ const renderDayCell = React14.useCallback((day, dayNumber, index) => {
19992
+ if (dayNumber === null) {
19993
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full" });
19994
+ }
19995
+ const cellDate = dateFnsTz.toZonedTime(new Date(year, month, dayNumber), configuredTimezone);
19996
+ const isToday = isCurrentDate(cellDate);
19997
+ const isFuture = isFutureDate(cellDate);
19998
+ const isSunday = cellDate.getDay() === 0;
19999
+ if (!day) {
20000
+ let bgColor = "bg-gray-100";
20001
+ let textColor = "text-gray-400";
20002
+ if (isSunday) {
20003
+ bgColor = "bg-gray-200";
20004
+ textColor = "text-gray-500";
20005
+ }
20006
+ if (isFuture) {
20007
+ bgColor = "bg-gray-50";
20008
+ textColor = "text-gray-300";
20009
+ }
20010
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `h-full border border-gray-200 rounded-lg ${bgColor} ${animationComplete ? "transition-all duration-300 ease-in-out" : ""} cursor-not-allowed opacity-60`, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-2", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: `text-base font-medium ${textColor} ${isToday ? "text-blue-500" : ""}`, children: dayNumber }) }) });
20011
+ }
19819
20012
  const shiftData = selectedShift === "day" ? day.dayShift : day.nightShift;
19820
- const isToday = isCurrentDate(day.date);
19821
- const isFuture = isFutureDate(day.date);
19822
- const isInactive = shiftData.efficiency < 10;
19823
20013
  return /* @__PURE__ */ jsxRuntime.jsx(
19824
20014
  "div",
19825
20015
  {
19826
- className: `group h-full transition-all duration-200 ease-in-out ${!isFuture && !isInactive ? "cursor-pointer hover:opacity-90" : "cursor-not-allowed"}`,
19827
- onClick: () => !isFuture && !isInactive && handleDayClick(day, selectedShift),
20016
+ className: `group h-full ${animationComplete ? "transition-all duration-300 ease-in-out" : ""} ${!isFuture && animationComplete ? "cursor-pointer hover:opacity-90 hover:scale-105" : "cursor-not-allowed"}`,
20017
+ onClick: () => !isFuture && handleDayClick(day, selectedShift),
19828
20018
  children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `
19829
20019
  ${getPerformanceColor(shiftData.efficiency, day.date)}
19830
- rounded-lg h-full p-2 relative transition-all duration-200 ease-in-out
20020
+ rounded-lg h-full p-2 relative ${animationComplete ? "transition-all duration-300 ease-in-out" : ""} shadow-sm
19831
20021
  ${isToday ? "ring-2 ring-blue-500 ring-offset-2 shadow-md" : ""}
19832
20022
  `, children: [
19833
20023
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: `
19834
- text-base font-medium text-white flex items-center transition-all duration-200 ease-in-out
20024
+ text-base font-medium text-white flex items-center ${animationComplete ? "transition-all duration-300 ease-in-out" : ""}
19835
20025
  ${isToday ? "bg-blue-500 rounded-full w-7 h-7 justify-center" : ""}
19836
20026
  `, children: day.date.getDate() }),
19837
- !isFuture && !isInactive && renderStats(shiftData, day.date)
20027
+ !isFuture && animationComplete && renderStats(shiftData, day.date)
19838
20028
  ] })
19839
20029
  }
19840
20030
  );
19841
- }, [selectedShift, isCurrentDate, isFutureDate, getPerformanceColor, handleDayClick]);
19842
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `space-y-6 ${className || ""}`, children: [
20031
+ }, [selectedShift, isCurrentDate, isFutureDate, getPerformanceColor, handleDayClick, year, month, configuredTimezone, animationComplete]);
20032
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `calendar-wrapper space-y-6 ${className || ""} ${animationComplete ? "animation-complete" : ""}`, children: [
20033
+ /* @__PURE__ */ jsxRuntime.jsx("style", { dangerouslySetInnerHTML: { __html: styles } }),
19843
20034
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-1 border border-gray-200 rounded-lg p-1 bg-gray-50", children: [
19844
20035
  /* @__PURE__ */ jsxRuntime.jsx(
19845
20036
  "button",
19846
20037
  {
19847
20038
  onClick: () => handleShiftChange("day"),
19848
- className: `px-4 py-2 text-sm font-medium rounded-md transition-all duration-200 ${selectedShift === "day" ? "bg-white text-blue-600 shadow-sm ring-1 ring-gray-200" : "text-gray-600 hover:text-gray-900 hover:bg-gray-100"}`,
20039
+ className: `px-4 py-2 text-sm font-medium rounded-md ${animationComplete ? "transition-all duration-200" : ""} ${selectedShift === "day" ? "bg-white text-blue-600 shadow-sm ring-1 ring-gray-200" : `text-gray-600 ${animationComplete ? "hover:text-gray-900 hover:bg-gray-100" : ""}`}`,
19849
20040
  children: "Day Shift"
19850
20041
  }
19851
20042
  ),
@@ -19853,23 +20044,27 @@ var WorkspaceHistoryCalendar = ({
19853
20044
  "button",
19854
20045
  {
19855
20046
  onClick: () => handleShiftChange("night"),
19856
- className: `px-4 py-2 text-sm font-medium rounded-md transition-all duration-200 ${selectedShift === "night" ? "bg-white text-blue-600 shadow-sm ring-1 ring-gray-200" : "text-gray-600 hover:text-gray-900 hover:bg-gray-100"}`,
20047
+ className: `px-4 py-2 text-sm font-medium rounded-md ${animationComplete ? "transition-all duration-200" : ""} ${selectedShift === "night" ? "bg-white text-blue-600 shadow-sm ring-1 ring-gray-200" : `text-gray-600 ${animationComplete ? "hover:text-gray-900 hover:bg-gray-100" : ""}`}`,
19857
20048
  children: "Night Shift"
19858
20049
  }
19859
20050
  )
19860
20051
  ] }) }),
19861
20052
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-8", children: [
19862
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-100 p-6 transition-all duration-200 ease-in-out", children: [
20053
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "calendar-container bg-white rounded-xl shadow-sm border border-gray-100 p-6 transition-all duration-200 ease-in-out", children: [
19863
20054
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-6", children: [
19864
20055
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "font-semibold text-gray-900 text-lg", children: selectedShift === "day" ? "Day Shifts" : "Night Shifts" }),
19865
20056
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500 mt-1", children: "Calendar view of daily performance" })
19866
20057
  ] }),
19867
20058
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-6", children: [
19868
20059
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-7 gap-2", children: WEEKDAYS2.map((day) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-medium text-gray-600 text-center", children: day }, day)) }),
19869
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-7 gap-2", children: calendarData.map((day, index) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-square relative transition-all duration-200 ease-in-out", children: renderDayCell(day) }, index)) })
20060
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-7 gap-2", children: calendarData.calendar.map((day, index) => {
20061
+ const startOffset = calendarData.startOffset;
20062
+ const dayNumber = index >= startOffset ? index - startOffset + 1 : null;
20063
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-square relative transition-all duration-200 ease-in-out", children: renderDayCell(day, dayNumber && dayNumber <= new Date(year, month + 1, 0).getDate() ? dayNumber : null, index) }, index);
20064
+ }) })
19870
20065
  ] })
19871
20066
  ] }),
19872
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-100 p-6 transition-all duration-200 ease-in-out", children: [
20067
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "calendar-container bg-white rounded-xl shadow-sm border border-gray-100 p-6 transition-all duration-200 ease-in-out", children: [
19873
20068
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-6", children: [
19874
20069
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "font-semibold text-gray-900 text-lg", children: "Monthly Summary" }),
19875
20070
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500 mt-1", children: "Overview of monthly performance metrics" })
@@ -22327,12 +22522,12 @@ var getEfficiencyColor = (efficiency) => {
22327
22522
  return "bg-[#E34329]/90 hover:bg-[#E34329]/95";
22328
22523
  }
22329
22524
  };
22330
- var TREND_STYLES2 = {
22525
+ var TREND_STYLES = {
22331
22526
  0: { arrow: "\u2193", color: "text-red-500 font-bold text-shadow" },
22332
22527
  1: { arrow: "=", color: "text-gray-500 font-bold text-shadow" },
22333
22528
  2: { arrow: "\u2191", color: "text-green-500 font-bold text-shadow" }
22334
22529
  };
22335
- var getTrendArrowAndColor2 = (trend) => TREND_STYLES2[trend] || { arrow: "", color: "" };
22530
+ var getTrendArrowAndColor2 = (trend) => TREND_STYLES[trend] || { arrow: "", color: "" };
22336
22531
  var ARROW_POSITIONS = {
22337
22532
  top: "-bottom-6",
22338
22533
  "middle-top": "-bottom-6",
@@ -22440,7 +22635,7 @@ var WorkspaceGridItem = React14__namespace.default.memo(({
22440
22635
  }, [data.efficiency, isInactive, position.size, position.orientation]);
22441
22636
  const { arrow, color: arrowColor } = React14.useMemo(() => getTrendArrowAndColor2(data.trend_score), [data.trend_score]);
22442
22637
  const workspaceNumber = React14.useMemo(() => getWorkspaceNumber(data.workspace_name), [data.workspace_name]);
22443
- const styles = React14.useMemo(() => getWorkspaceStyles(position, isInactive), [position, isInactive]);
22638
+ const styles2 = React14.useMemo(() => getWorkspaceStyles(position, isInactive), [position, isInactive]);
22444
22639
  const arrowPosition = React14.useMemo(() => getArrowPositionClass(position), [position]);
22445
22640
  const handleClick = React14.useCallback((e) => {
22446
22641
  e.preventDefault();
@@ -22491,7 +22686,7 @@ var WorkspaceGridItem = React14__namespace.default.memo(({
22491
22686
  "button",
22492
22687
  {
22493
22688
  onClick: handleClick,
22494
- className: `${styles} ${colorClass} ${isBottleneck ? "ring-2 ring-red-500/70" : ""} ${isVeryLowEfficiency ? "ring-2 ring-red-500/50" : ""} ${isInactive ? "bg-gray-200" : ""} shadow-lg`,
22689
+ className: `${styles2} ${colorClass} ${isBottleneck ? "ring-2 ring-red-500/70" : ""} ${isVeryLowEfficiency ? "ring-2 ring-red-500/50" : ""} ${isInactive ? "bg-gray-200" : ""} shadow-lg`,
22495
22690
  "aria-label": isInactive ? `Inactive workspace ${workspaceNumber}` : `View details for workspace ${workspaceNumber}`,
22496
22691
  title: isInactive ? `Inactive: ${getWorkspaceDisplayName(data.workspace_name)}` : getWorkspaceDisplayName(data.workspace_name),
22497
22692
  children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: `font-semibold tracking-wide text-[min(4vw,2rem)] uppercase ${isInactive ? "text-gray-400" : "text-white"} drop-shadow-sm`, children: workspaceNumber })
@@ -30002,6 +30197,11 @@ var WorkspaceDetailView = ({
30002
30197
  return getOperationalDate();
30003
30198
  }, [isHistoricView, date]);
30004
30199
  const handleMonthlyDataLoaded = React14.useCallback((data) => {
30200
+ console.log("[handleMonthlyDataLoaded] Received data:", {
30201
+ dataLength: data?.length,
30202
+ isArray: Array.isArray(data),
30203
+ sample: data?.[0]
30204
+ });
30005
30205
  if (!data || !Array.isArray(data)) {
30006
30206
  console.error("Invalid monthly metrics data received:", data);
30007
30207
  setMonthlyData([]);
@@ -30019,22 +30219,35 @@ var WorkspaceDetailView = ({
30019
30219
  if (!dayEntry) {
30020
30220
  dayEntry = {
30021
30221
  date: dateObj,
30022
- dayShift: { efficiency: 0, output: 0, cycleTime: 0, pph: 0, pphThreshold: 0, idealOutput: 0, rank: 0, idleTime: 0 },
30023
- nightShift: { efficiency: 0, output: 0, cycleTime: 0, pph: 0, pphThreshold: 0, idealOutput: 0, rank: 0, idleTime: 0 }
30222
+ dayShift: null,
30223
+ // Start with null instead of zeros
30224
+ nightShift: null
30225
+ // Start with null instead of zeros
30024
30226
  };
30025
30227
  dayDataMap.set(dateKey, dayEntry);
30026
30228
  }
30027
- const shiftTarget = metric.shift_id === 0 ? dayEntry.dayShift : dayEntry.nightShift;
30028
- shiftTarget.efficiency = metric.avg_efficiency || 0;
30029
- shiftTarget.output = metric.total_output || 0;
30030
- shiftTarget.cycleTime = metric.avg_cycle_time || 0;
30031
- shiftTarget.pph = metric.avg_pph || 0;
30032
- shiftTarget.pphThreshold = metric.pph_threshold || 0;
30033
- shiftTarget.idealOutput = metric.ideal_output || 0;
30034
- shiftTarget.rank = metric.workspace_rank || 0;
30035
- shiftTarget.idleTime = metric.idle_time || 0;
30229
+ const shiftData = {
30230
+ efficiency: metric.avg_efficiency || 0,
30231
+ output: metric.total_output || 0,
30232
+ cycleTime: metric.avg_cycle_time || 0,
30233
+ pph: metric.avg_pph || 0,
30234
+ pphThreshold: metric.pph_threshold || 0,
30235
+ idealOutput: metric.ideal_output || 0,
30236
+ rank: metric.workspace_rank || 0,
30237
+ idleTime: metric.idle_time || 0
30238
+ };
30239
+ if (metric.shift_id === 0) {
30240
+ dayEntry.dayShift = shiftData;
30241
+ } else {
30242
+ dayEntry.nightShift = shiftData;
30243
+ }
30036
30244
  });
30037
- const processedData = Array.from(dayDataMap.values());
30245
+ const processedData = Array.from(dayDataMap.values()).filter((entry) => entry.dayShift !== null || entry.nightShift !== null).map((entry) => ({
30246
+ date: entry.date,
30247
+ // If a shift is null (no data), provide zeros for compatibility
30248
+ dayShift: entry.dayShift || { efficiency: 0, output: 0, cycleTime: 0, pph: 0, pphThreshold: 0, idealOutput: 0, rank: 0, idleTime: 0 },
30249
+ nightShift: entry.nightShift || { efficiency: 0, output: 0, cycleTime: 0, pph: 0, pphThreshold: 0, idealOutput: 0, rank: 0, idleTime: 0 }
30250
+ }));
30038
30251
  console.log(`[handleMonthlyDataLoaded] Transformed data for calendar:`, {
30039
30252
  count: processedData.length,
30040
30253
  sample: processedData.slice(0, 1)
@@ -30494,7 +30707,7 @@ var WorkspaceDetailView = ({
30494
30707
  }
30495
30708
  )
30496
30709
  ] }),
30497
- monthlyDataLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center h-[calc(100%-5rem)]", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xl text-gray-600", children: "Loading monthly data..." }) }) : /* @__PURE__ */ jsxRuntime.jsx(
30710
+ /* @__PURE__ */ jsxRuntime.jsx(
30498
30711
  WorkspaceHistoryCalendar,
30499
30712
  {
30500
30713
  data: monthlyData,
@@ -30803,6 +31016,7 @@ exports.DEFAULT_ENDPOINTS_CONFIG = DEFAULT_ENDPOINTS_CONFIG;
30803
31016
  exports.DEFAULT_ENTITY_CONFIG = DEFAULT_ENTITY_CONFIG;
30804
31017
  exports.DEFAULT_SHIFT_CONFIG = DEFAULT_SHIFT_CONFIG;
30805
31018
  exports.DEFAULT_THEME_CONFIG = DEFAULT_THEME_CONFIG;
31019
+ exports.DEFAULT_VIDEO_CONFIG = DEFAULT_VIDEO_CONFIG;
30806
31020
  exports.DEFAULT_WORKSPACE_CONFIG = DEFAULT_WORKSPACE_CONFIG;
30807
31021
  exports.DEFAULT_WORKSPACE_POSITIONS = DEFAULT_WORKSPACE_POSITIONS;
30808
31022
  exports.DashboardHeader = DashboardHeader;
@@ -30992,6 +31206,7 @@ exports.useFeatureFlags = useFeatureFlags;
30992
31206
  exports.useFormatNumber = useFormatNumber;
30993
31207
  exports.useHistoricWorkspaceMetrics = useHistoricWorkspaceMetrics;
30994
31208
  exports.useHlsStream = useHlsStream;
31209
+ exports.useHlsStreamWithCropping = useHlsStreamWithCropping;
30995
31210
  exports.useHookOverride = useHookOverride;
30996
31211
  exports.useLeaderboardMetrics = useLeaderboardMetrics;
30997
31212
  exports.useLineDetailedMetrics = useLineDetailedMetrics;
@@ -31013,6 +31228,7 @@ exports.useTargets = useTargets;
31013
31228
  exports.useTheme = useTheme;
31014
31229
  exports.useThemeConfig = useThemeConfig;
31015
31230
  exports.useThreads = useThreads;
31231
+ exports.useVideoConfig = useVideoConfig;
31016
31232
  exports.useWorkspaceConfig = useWorkspaceConfig;
31017
31233
  exports.useWorkspaceDetailedMetrics = useWorkspaceDetailedMetrics;
31018
31234
  exports.useWorkspaceDisplayName = useWorkspaceDisplayName;