@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.mjs CHANGED
@@ -6,11 +6,11 @@ import { toZonedTime, formatInTimeZone } from 'date-fns-tz';
6
6
  import { subDays, format, parseISO, isValid, isFuture, isToday } from 'date-fns';
7
7
  import mixpanel from 'mixpanel-browser';
8
8
  import { REALTIME_SUBSCRIBE_STATES, createClient } from '@supabase/supabase-js';
9
- import useSWR from 'swr';
10
9
  import Hls2 from 'hls.js';
10
+ import useSWR from 'swr';
11
11
  import { noop, warning, invariant, progress, secondsToMilliseconds, millisecondsToSeconds, memo as memo$1 } from 'motion-utils';
12
12
  import { getValueTransition, hover, press, isPrimaryPointer, GroupPlaybackControls, setDragLock, supportsLinearEasing, attachTimeline, isGenerator, calcGeneratorDuration, isWaapiSupportedEasing, mapEasingToNativeEasing, maxGeneratorDuration, generateLinearEasing, isBezierDefinition } from 'motion-dom';
13
- import { ResponsiveContainer, BarChart as BarChart$1, CartesianGrid, XAxis, YAxis, Tooltip, ReferenceLine, Bar, Cell, LabelList, PieChart, Pie, Legend, LineChart as LineChart$1, Line } from 'recharts';
13
+ import { Bar, LabelList, ResponsiveContainer, BarChart as BarChart$1, CartesianGrid, XAxis, YAxis, Tooltip, ReferenceLine, Cell, PieChart, Pie, Legend, LineChart as LineChart$1, Line } from 'recharts';
14
14
  import { Slot } from '@radix-ui/react-slot';
15
15
  import { Camera, ChevronDown, ChevronUp, Check, ShieldCheck, Star, Award, X, Coffee, Plus, Clock, Minus, ArrowDown, ArrowUp, Search, CheckCircle, AlertTriangle, Info, Share2, Download, User, XCircle, ChevronLeft, ChevronRight, AlertCircle, Sun, Moon, MessageSquare, Trash2, ArrowLeft, RefreshCw, Menu, Send, Copy, Edit2, UserCheck, Save, LogOut, Calendar, Settings, LifeBuoy, Loader2, ArrowLeftIcon as ArrowLeftIcon$1, Settings2, CheckCircle2, EyeOff, Eye, Zap, UserCircle } from 'lucide-react';
16
16
  import { DayPicker, useNavigation as useNavigation$1 } from 'react-day-picker';
@@ -125,6 +125,12 @@ var DEFAULT_ANALYTICS_CONFIG = {
125
125
  var DEFAULT_AUTH_CONFIG = {
126
126
  // Defaults related to auth providers, redirects etc.
127
127
  };
128
+ var DEFAULT_VIDEO_CONFIG = {
129
+ canvasConfig: {
130
+ fps: 30,
131
+ useRAF: true
132
+ }
133
+ };
128
134
  var LINE_1_UUID = "910a224b-0abc-459a-babb-4c899824cfe7";
129
135
  var DEFAULT_CONFIG = {
130
136
  apiBaseUrl: void 0,
@@ -138,7 +144,8 @@ var DEFAULT_CONFIG = {
138
144
  // Add entity config here
139
145
  shiftConfig: DEFAULT_SHIFT_CONFIG,
140
146
  workspaceConfig: DEFAULT_WORKSPACE_CONFIG,
141
- endpoints: DEFAULT_ENDPOINTS_CONFIG
147
+ endpoints: DEFAULT_ENDPOINTS_CONFIG,
148
+ videoConfig: DEFAULT_VIDEO_CONFIG
142
149
  };
143
150
 
144
151
  // src/lib/utils/config.ts
@@ -270,6 +277,10 @@ function useCustomConfig() {
270
277
  const { customConfig } = useDashboardConfig();
271
278
  return customConfig ?? {};
272
279
  }
280
+ function useVideoConfig() {
281
+ const { videoConfig } = useDashboardConfig();
282
+ return videoConfig ?? DEFAULT_VIDEO_CONFIG;
283
+ }
273
284
 
274
285
  // src/lib/internal/supabaseClientInstance.ts
275
286
  var supabaseInstance = null;
@@ -980,7 +991,7 @@ var dashboardService = {
980
991
  const formattedStartDate = formatDate(startDate);
981
992
  const formattedEndDate = formatDate(endDate);
982
993
  try {
983
- 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 });
994
+ 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 });
984
995
  if (error) throw error;
985
996
  if (!data) return [];
986
997
  const transformedData = data.map((item) => ({
@@ -993,8 +1004,7 @@ var dashboardService = {
993
1004
  ideal_output: item.ideal_output || 0,
994
1005
  avg_pph: item.avg_pph || 0,
995
1006
  pph_threshold: item.pph_threshold || 0,
996
- workspace_rank: item.workspace_rank || 0,
997
- idle_time: item.idle_time || 0
1007
+ workspace_rank: item.workspace_rank || 0
998
1008
  }));
999
1009
  return transformedData;
1000
1010
  } catch (err) {
@@ -4218,6 +4228,351 @@ var useWorkspaceOperators = (workspaceId, options) => {
4218
4228
  refetch: fetchData
4219
4229
  };
4220
4230
  };
4231
+
4232
+ // src/lib/utils/dashboardReload.ts
4233
+ var createThrottledReload = (interval = 5e3) => {
4234
+ let last = 0;
4235
+ let queued = false;
4236
+ const doReload = () => {
4237
+ if (typeof window !== "undefined") {
4238
+ window.location.reload();
4239
+ }
4240
+ };
4241
+ return () => {
4242
+ const now2 = Date.now();
4243
+ if (now2 - last >= interval) {
4244
+ last = now2;
4245
+ doReload();
4246
+ } else if (!queued) {
4247
+ queued = true;
4248
+ setTimeout(() => {
4249
+ queued = false;
4250
+ last = Date.now();
4251
+ doReload();
4252
+ }, interval - (now2 - last));
4253
+ }
4254
+ };
4255
+ };
4256
+ var throttledReloadDashboard = createThrottledReload(5e3);
4257
+
4258
+ // src/lib/hooks/useHlsStream.ts
4259
+ var HLS_CONFIG = {
4260
+ maxBufferLength: 8,
4261
+ maxMaxBufferLength: 15,
4262
+ lowLatencyMode: false,
4263
+ enableWorker: true,
4264
+ // Retry + timeout tuned for quick recovery
4265
+ manifestLoadingMaxRetry: 4,
4266
+ levelLoadingMaxRetry: 3,
4267
+ fragLoadingMaxRetry: 4,
4268
+ manifestLoadingRetryDelay: 500,
4269
+ levelLoadingRetryDelay: 500,
4270
+ fragLoadingRetryDelay: 500,
4271
+ manifestLoadingTimeOut: 1e4,
4272
+ levelLoadingTimeOut: 8e3,
4273
+ fragLoadingTimeOut: 1e4,
4274
+ liveSyncDurationCount: 2
4275
+ // Follow live edge aggressively
4276
+ };
4277
+ function useHlsStream(videoRef, { src, shouldPlay, onFatalError }) {
4278
+ const [restartKey, setRestartKey] = useState(0);
4279
+ const hlsRef = useRef(null);
4280
+ const stallCheckIntervalRef = useRef(null);
4281
+ const noProgressTimerRef = useRef(null);
4282
+ const lastTimeUpdateRef = useRef(0);
4283
+ const softRestartCountRef = useRef(0);
4284
+ const isNativeHlsRef = useRef(false);
4285
+ const waitingTimerRef = useRef(null);
4286
+ const cleanup = () => {
4287
+ if (stallCheckIntervalRef.current) {
4288
+ clearInterval(stallCheckIntervalRef.current);
4289
+ stallCheckIntervalRef.current = null;
4290
+ }
4291
+ if (noProgressTimerRef.current) {
4292
+ clearTimeout(noProgressTimerRef.current);
4293
+ noProgressTimerRef.current = null;
4294
+ }
4295
+ if (waitingTimerRef.current) {
4296
+ clearTimeout(waitingTimerRef.current);
4297
+ waitingTimerRef.current = null;
4298
+ }
4299
+ if (hlsRef.current) {
4300
+ hlsRef.current.destroy();
4301
+ hlsRef.current = null;
4302
+ }
4303
+ const video = videoRef.current;
4304
+ if (video) {
4305
+ video.pause();
4306
+ video.removeAttribute("src");
4307
+ video.load();
4308
+ video.removeEventListener("waiting", handleWaiting);
4309
+ video.removeEventListener("timeupdate", handleTimeUpdate);
4310
+ video.removeEventListener("error", handleNativeError);
4311
+ }
4312
+ lastTimeUpdateRef.current = 0;
4313
+ softRestartCountRef.current = 0;
4314
+ };
4315
+ const seekToLiveEdge = () => {
4316
+ const hls = hlsRef.current;
4317
+ const video = videoRef.current;
4318
+ if (!hls || !video) return;
4319
+ if (hls.liveSyncPosition !== null && hls.liveSyncPosition !== void 0) {
4320
+ video.currentTime = hls.liveSyncPosition;
4321
+ } else if (hls.levels?.[hls.currentLevel]?.details) {
4322
+ const levelDetails = hls.levels[hls.currentLevel].details;
4323
+ if (levelDetails && levelDetails.edge !== void 0) {
4324
+ video.currentTime = Math.max(0, levelDetails.edge - 5);
4325
+ }
4326
+ }
4327
+ };
4328
+ const softRestart = (reason) => {
4329
+ console.warn(`[HLS] Soft restart: ${reason}`);
4330
+ const hls = hlsRef.current;
4331
+ if (!hls) return;
4332
+ try {
4333
+ hls.stopLoad();
4334
+ hls.startLoad(-1);
4335
+ seekToLiveEdge();
4336
+ softRestartCountRef.current++;
4337
+ if (softRestartCountRef.current >= 5) {
4338
+ hardRestart(`${reason} (escalated after ${softRestartCountRef.current} soft restarts)`);
4339
+ }
4340
+ } catch (error) {
4341
+ console.error("[HLS] Soft restart failed:", error);
4342
+ hardRestart(`${reason} (soft restart error)`);
4343
+ }
4344
+ };
4345
+ const hardRestart = (reason) => {
4346
+ console.warn(`[HLS] Hard restart: ${reason}`);
4347
+ cleanup();
4348
+ setRestartKey((k) => k + 1);
4349
+ softRestartCountRef.current = 0;
4350
+ if (reason.includes("hard restart") || reason.includes("native video error")) {
4351
+ if (onFatalError) {
4352
+ onFatalError();
4353
+ } else {
4354
+ throttledReloadDashboard();
4355
+ }
4356
+ }
4357
+ };
4358
+ const handleWaiting = () => {
4359
+ if (isNativeHlsRef.current) return;
4360
+ console.log("[HLS] Video waiting (buffer underrun)");
4361
+ if (waitingTimerRef.current) {
4362
+ clearTimeout(waitingTimerRef.current);
4363
+ }
4364
+ waitingTimerRef.current = setTimeout(() => {
4365
+ const video = videoRef.current;
4366
+ if (video && video.readyState < 3) {
4367
+ softRestart("waiting timeout");
4368
+ }
4369
+ }, 1e4);
4370
+ };
4371
+ const handleTimeUpdate = () => {
4372
+ const video = videoRef.current;
4373
+ if (!video) return;
4374
+ lastTimeUpdateRef.current = video.currentTime;
4375
+ if (waitingTimerRef.current) {
4376
+ clearTimeout(waitingTimerRef.current);
4377
+ waitingTimerRef.current = null;
4378
+ }
4379
+ };
4380
+ const handleNativeError = () => {
4381
+ console.error("[HLS] Native video error");
4382
+ hardRestart("native video error");
4383
+ };
4384
+ const startStallDetection = () => {
4385
+ if (isNativeHlsRef.current) return;
4386
+ stallCheckIntervalRef.current = setInterval(() => {
4387
+ const video = videoRef.current;
4388
+ if (!video || video.paused || video.ended) return;
4389
+ const currentTime = video.currentTime;
4390
+ const lastTime = lastTimeUpdateRef.current;
4391
+ if (Math.abs(currentTime - lastTime) < 0.1 && video.readyState >= 2) {
4392
+ console.warn("[HLS] Playback stall detected");
4393
+ if (!noProgressTimerRef.current) {
4394
+ noProgressTimerRef.current = setTimeout(() => {
4395
+ softRestart("playback stall");
4396
+ noProgressTimerRef.current = null;
4397
+ }, 8e3);
4398
+ }
4399
+ } else {
4400
+ if (noProgressTimerRef.current) {
4401
+ clearTimeout(noProgressTimerRef.current);
4402
+ noProgressTimerRef.current = null;
4403
+ }
4404
+ }
4405
+ }, 7e3);
4406
+ };
4407
+ useEffect(() => {
4408
+ if (!src || !shouldPlay) {
4409
+ cleanup();
4410
+ return;
4411
+ }
4412
+ const video = videoRef.current;
4413
+ if (!video) return;
4414
+ isNativeHlsRef.current = video.canPlayType("application/vnd.apple.mpegurl") === "probably";
4415
+ if (Hls2.isSupported() && !isNativeHlsRef.current) {
4416
+ const hls = new Hls2(HLS_CONFIG);
4417
+ hlsRef.current = hls;
4418
+ hls.attachMedia(video);
4419
+ hls.loadSource(src);
4420
+ hls.on(Hls2.Events.ERROR, (_, data) => {
4421
+ if (!data.fatal) return;
4422
+ console.error("[HLS] Fatal error:", data.type, data.details);
4423
+ if (data.response?.code === 404) {
4424
+ hardRestart("404 hard restart");
4425
+ return;
4426
+ }
4427
+ switch (data.type) {
4428
+ case Hls2.ErrorTypes.NETWORK_ERROR:
4429
+ case Hls2.ErrorTypes.MEDIA_ERROR:
4430
+ softRestart(`${data.type}: ${data.details}`);
4431
+ break;
4432
+ default:
4433
+ hardRestart(`Fatal ${data.type}: ${data.details}`);
4434
+ break;
4435
+ }
4436
+ });
4437
+ hls.on(Hls2.Events.MANIFEST_PARSED, () => {
4438
+ video.play().catch((err) => {
4439
+ console.error("[HLS] Play failed:", err);
4440
+ });
4441
+ });
4442
+ video.addEventListener("waiting", handleWaiting);
4443
+ video.addEventListener("timeupdate", handleTimeUpdate);
4444
+ startStallDetection();
4445
+ } else if (isNativeHlsRef.current) {
4446
+ console.log("[HLS] Using native HLS");
4447
+ video.src = src;
4448
+ video.addEventListener("error", handleNativeError);
4449
+ video.play().catch((err) => {
4450
+ console.error("[HLS] Native play failed:", err);
4451
+ });
4452
+ } else {
4453
+ console.error("[HLS] HLS not supported");
4454
+ }
4455
+ return cleanup;
4456
+ }, [src, shouldPlay, restartKey, onFatalError]);
4457
+ return {
4458
+ restartKey,
4459
+ isNativeHls: isNativeHlsRef.current
4460
+ };
4461
+ }
4462
+ function useHlsStreamWithCropping(videoRef, canvasRef, options) {
4463
+ const { src, shouldPlay, cropping, canvasFps = 30, useRAF = true, onFatalError } = options;
4464
+ const animationFrameRef = useRef(null);
4465
+ const intervalRef = useRef(null);
4466
+ const isDrawingRef = useRef(false);
4467
+ const hlsState = useHlsStream(videoRef, { src, shouldPlay, onFatalError });
4468
+ const calculateCropRect = useCallback((video, cropping2) => {
4469
+ const videoWidth = video.videoWidth;
4470
+ const videoHeight = video.videoHeight;
4471
+ const sx = cropping2.x / 100 * videoWidth;
4472
+ const sy = cropping2.y / 100 * videoHeight;
4473
+ const sw = cropping2.width / 100 * videoWidth;
4474
+ const sh = cropping2.height / 100 * videoHeight;
4475
+ return { sx, sy, sw, sh };
4476
+ }, []);
4477
+ const drawFrame = useCallback(() => {
4478
+ const video = videoRef.current;
4479
+ const canvas = canvasRef.current;
4480
+ if (!video || !canvas || !cropping) return;
4481
+ const ctx = canvas.getContext("2d");
4482
+ if (!ctx) return;
4483
+ if (video.readyState < 2) return;
4484
+ try {
4485
+ const videoWidth = video.videoWidth;
4486
+ const videoHeight = video.videoHeight;
4487
+ if (!videoWidth || !videoHeight) return;
4488
+ const { sx, sy, sw, sh } = calculateCropRect(video, cropping);
4489
+ const canvasContainer = canvas.parentElement;
4490
+ if (canvasContainer) {
4491
+ const containerWidth = canvasContainer.clientWidth;
4492
+ const containerHeight = canvasContainer.clientHeight;
4493
+ if (canvas.width !== containerWidth || canvas.height !== containerHeight) {
4494
+ canvas.width = containerWidth;
4495
+ canvas.height = containerHeight;
4496
+ }
4497
+ }
4498
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
4499
+ ctx.drawImage(
4500
+ video,
4501
+ sx,
4502
+ sy,
4503
+ sw,
4504
+ sh,
4505
+ // Source rectangle (cropped portion)
4506
+ 0,
4507
+ 0,
4508
+ canvas.width,
4509
+ canvas.height
4510
+ // Destination rectangle (full canvas)
4511
+ );
4512
+ } catch (err) {
4513
+ console.warn("Canvas drawing error:", err);
4514
+ }
4515
+ }, [videoRef, canvasRef, cropping, calculateCropRect]);
4516
+ const startCanvasRendering = useCallback(() => {
4517
+ if (isDrawingRef.current) return;
4518
+ isDrawingRef.current = true;
4519
+ if (useRAF) {
4520
+ const animate = () => {
4521
+ drawFrame();
4522
+ animationFrameRef.current = requestAnimationFrame(animate);
4523
+ };
4524
+ animate();
4525
+ } else {
4526
+ const frameInterval = 1e3 / canvasFps;
4527
+ intervalRef.current = setInterval(drawFrame, frameInterval);
4528
+ }
4529
+ }, [drawFrame, canvasFps, useRAF]);
4530
+ const stopCanvasRendering = useCallback(() => {
4531
+ isDrawingRef.current = false;
4532
+ if (animationFrameRef.current) {
4533
+ cancelAnimationFrame(animationFrameRef.current);
4534
+ animationFrameRef.current = null;
4535
+ }
4536
+ if (intervalRef.current) {
4537
+ clearInterval(intervalRef.current);
4538
+ intervalRef.current = null;
4539
+ }
4540
+ }, []);
4541
+ useEffect(() => {
4542
+ const video = videoRef.current;
4543
+ if (!video || !cropping || !shouldPlay) {
4544
+ stopCanvasRendering();
4545
+ return;
4546
+ }
4547
+ const handlePlay = () => {
4548
+ if (cropping) {
4549
+ startCanvasRendering();
4550
+ }
4551
+ };
4552
+ const handlePause = () => {
4553
+ stopCanvasRendering();
4554
+ };
4555
+ const handleEnded = () => {
4556
+ stopCanvasRendering();
4557
+ };
4558
+ video.addEventListener("play", handlePlay);
4559
+ video.addEventListener("pause", handlePause);
4560
+ video.addEventListener("ended", handleEnded);
4561
+ if (!video.paused && video.readyState >= 2) {
4562
+ startCanvasRendering();
4563
+ }
4564
+ return () => {
4565
+ stopCanvasRendering();
4566
+ video.removeEventListener("play", handlePlay);
4567
+ video.removeEventListener("pause", handlePause);
4568
+ video.removeEventListener("ended", handleEnded);
4569
+ };
4570
+ }, [videoRef, cropping, shouldPlay, startCanvasRendering, stopCanvasRendering]);
4571
+ return {
4572
+ ...hlsState,
4573
+ isCanvasRendering: isDrawingRef.current
4574
+ };
4575
+ }
4221
4576
  function useThreads() {
4222
4577
  const supabase = _getSupabaseInstance();
4223
4578
  const fetcher = async (key) => {
@@ -4897,327 +5252,96 @@ function useNavigation(customNavigate) {
4897
5252
  goToProfile,
4898
5253
  navigate
4899
5254
  };
4900
- }
4901
- function useWorkspaceNavigation() {
4902
- const { defaultTimezone } = useDateTimeConfig();
4903
- const getWorkspaceNavigationParams3 = useCallback(
4904
- (workspaceId, options) => {
4905
- let dateToUse = options?.date;
4906
- if (!dateToUse && options?.useCurrentDate) {
4907
- dateToUse = getOperationalDate(defaultTimezone || "UTC");
4908
- }
4909
- return {
4910
- workspaceId,
4911
- date: dateToUse,
4912
- shift: options?.shift,
4913
- sourceType: options?.sourceType
4914
- };
4915
- },
4916
- [defaultTimezone]
4917
- );
4918
- return {
4919
- getWorkspaceNavigationParams: getWorkspaceNavigationParams3
4920
- };
4921
- }
4922
- function useDateFormatter() {
4923
- const { defaultTimezone, defaultLocale, dateFormatOptions, timeFormatOptions, dateTimeFormatOptions } = useDateTimeConfig();
4924
- const formatDate = useCallback(
4925
- (date, formatString) => {
4926
- const dateObj = typeof date === "string" ? parseISO(date) : date;
4927
- if (!isValid(dateObj)) return "Invalid Date";
4928
- const tz = defaultTimezone || "UTC";
4929
- if (formatString) {
4930
- return formatInTimeZone(dateObj, tz, formatString);
4931
- }
4932
- const effectiveOptions = dateFormatOptions || { year: "numeric", month: "short", day: "numeric" };
4933
- return new Intl.DateTimeFormat(defaultLocale || "en-US", effectiveOptions).format(dateObj);
4934
- },
4935
- [defaultTimezone, defaultLocale, dateFormatOptions]
4936
- );
4937
- const formatTime2 = useCallback(
4938
- (date, formatString) => {
4939
- const dateObj = typeof date === "string" ? parseISO(date) : date;
4940
- if (!isValid(dateObj)) return "Invalid Time";
4941
- const tz = defaultTimezone || "UTC";
4942
- if (formatString) {
4943
- return formatInTimeZone(dateObj, tz, formatString);
4944
- }
4945
- const effectiveOptions = timeFormatOptions || { hour: "numeric", minute: "numeric" };
4946
- return new Intl.DateTimeFormat(defaultLocale || "en-US", effectiveOptions).format(dateObj);
4947
- },
4948
- [defaultTimezone, defaultLocale, timeFormatOptions]
4949
- );
4950
- const formatDateTime = useCallback(
4951
- (date, formatString) => {
4952
- const dateObj = typeof date === "string" ? parseISO(date) : date;
4953
- if (!isValid(dateObj)) return "Invalid Date/Time";
4954
- const tz = defaultTimezone || "UTC";
4955
- if (formatString) {
4956
- return formatInTimeZone(dateObj, tz, formatString);
4957
- }
4958
- const effectiveOptions = dateTimeFormatOptions || { year: "numeric", month: "short", day: "numeric", hour: "numeric", minute: "numeric" };
4959
- return new Intl.DateTimeFormat(defaultLocale || "en-US", effectiveOptions).format(dateObj);
4960
- },
4961
- [defaultTimezone, defaultLocale, dateTimeFormatOptions]
4962
- );
4963
- const getNow = useCallback(() => {
4964
- return /* @__PURE__ */ new Date();
4965
- }, []);
4966
- return {
4967
- formatDate,
4968
- formatTime: formatTime2,
4969
- formatDateTime,
4970
- getNow,
4971
- timezone: defaultTimezone || "UTC",
4972
- locale: defaultLocale || "en-US"
4973
- };
4974
- }
4975
- var useFormatNumber = () => {
4976
- const { defaultLocale } = useDateTimeConfig();
4977
- const formatNumber = useCallback(
4978
- (value, options) => {
4979
- try {
4980
- return new Intl.NumberFormat(defaultLocale || "en-US", options).format(value);
4981
- } catch (error) {
4982
- console.error("Error formatting number:", error);
4983
- return String(value);
4984
- }
4985
- },
4986
- [defaultLocale]
4987
- );
4988
- return { formatNumber };
4989
- };
4990
-
4991
- // src/lib/utils/dashboardReload.ts
4992
- var createThrottledReload = (interval = 5e3) => {
4993
- let last = 0;
4994
- let queued = false;
4995
- const doReload = () => {
4996
- if (typeof window !== "undefined") {
4997
- window.location.reload();
4998
- }
4999
- };
5000
- return () => {
5001
- const now2 = Date.now();
5002
- if (now2 - last >= interval) {
5003
- last = now2;
5004
- doReload();
5005
- } else if (!queued) {
5006
- queued = true;
5007
- setTimeout(() => {
5008
- queued = false;
5009
- last = Date.now();
5010
- doReload();
5011
- }, interval - (now2 - last));
5012
- }
5013
- };
5014
- };
5015
- var throttledReloadDashboard = createThrottledReload(5e3);
5016
-
5017
- // src/lib/hooks/useHlsStream.ts
5018
- var HLS_CONFIG = {
5019
- maxBufferLength: 8,
5020
- maxMaxBufferLength: 15,
5021
- lowLatencyMode: false,
5022
- enableWorker: true,
5023
- // Retry + timeout tuned for quick recovery
5024
- manifestLoadingMaxRetry: 4,
5025
- levelLoadingMaxRetry: 3,
5026
- fragLoadingMaxRetry: 4,
5027
- manifestLoadingRetryDelay: 500,
5028
- levelLoadingRetryDelay: 500,
5029
- fragLoadingRetryDelay: 500,
5030
- manifestLoadingTimeOut: 1e4,
5031
- levelLoadingTimeOut: 8e3,
5032
- fragLoadingTimeOut: 1e4,
5033
- liveSyncDurationCount: 2
5034
- // Follow live edge aggressively
5035
- };
5036
- function useHlsStream(videoRef, { src, shouldPlay, onFatalError }) {
5037
- const [restartKey, setRestartKey] = useState(0);
5038
- const hlsRef = useRef(null);
5039
- const stallCheckIntervalRef = useRef(null);
5040
- const noProgressTimerRef = useRef(null);
5041
- const lastTimeUpdateRef = useRef(0);
5042
- const softRestartCountRef = useRef(0);
5043
- const isNativeHlsRef = useRef(false);
5044
- const waitingTimerRef = useRef(null);
5045
- const cleanup = () => {
5046
- if (stallCheckIntervalRef.current) {
5047
- clearInterval(stallCheckIntervalRef.current);
5048
- stallCheckIntervalRef.current = null;
5049
- }
5050
- if (noProgressTimerRef.current) {
5051
- clearTimeout(noProgressTimerRef.current);
5052
- noProgressTimerRef.current = null;
5053
- }
5054
- if (waitingTimerRef.current) {
5055
- clearTimeout(waitingTimerRef.current);
5056
- waitingTimerRef.current = null;
5057
- }
5058
- if (hlsRef.current) {
5059
- hlsRef.current.destroy();
5060
- hlsRef.current = null;
5061
- }
5062
- const video = videoRef.current;
5063
- if (video) {
5064
- video.pause();
5065
- video.removeAttribute("src");
5066
- video.load();
5067
- video.removeEventListener("waiting", handleWaiting);
5068
- video.removeEventListener("timeupdate", handleTimeUpdate);
5069
- video.removeEventListener("error", handleNativeError);
5070
- }
5071
- lastTimeUpdateRef.current = 0;
5072
- softRestartCountRef.current = 0;
5073
- };
5074
- const seekToLiveEdge = () => {
5075
- const hls = hlsRef.current;
5076
- const video = videoRef.current;
5077
- if (!hls || !video) return;
5078
- if (hls.liveSyncPosition !== null && hls.liveSyncPosition !== void 0) {
5079
- video.currentTime = hls.liveSyncPosition;
5080
- } else if (hls.levels?.[hls.currentLevel]?.details) {
5081
- const levelDetails = hls.levels[hls.currentLevel].details;
5082
- if (levelDetails && levelDetails.edge !== void 0) {
5083
- video.currentTime = Math.max(0, levelDetails.edge - 5);
5084
- }
5085
- }
5086
- };
5087
- const softRestart = (reason) => {
5088
- console.warn(`[HLS] Soft restart: ${reason}`);
5089
- const hls = hlsRef.current;
5090
- if (!hls) return;
5091
- try {
5092
- hls.stopLoad();
5093
- hls.startLoad(-1);
5094
- seekToLiveEdge();
5095
- softRestartCountRef.current++;
5096
- if (softRestartCountRef.current >= 5) {
5097
- hardRestart(`${reason} (escalated after ${softRestartCountRef.current} soft restarts)`);
5098
- }
5099
- } catch (error) {
5100
- console.error("[HLS] Soft restart failed:", error);
5101
- hardRestart(`${reason} (soft restart error)`);
5102
- }
5103
- };
5104
- const hardRestart = (reason) => {
5105
- console.warn(`[HLS] Hard restart: ${reason}`);
5106
- cleanup();
5107
- setRestartKey((k) => k + 1);
5108
- softRestartCountRef.current = 0;
5109
- if (reason.includes("hard restart") || reason.includes("native video error")) {
5110
- if (onFatalError) {
5111
- onFatalError();
5112
- } else {
5113
- throttledReloadDashboard();
5114
- }
5115
- }
5116
- };
5117
- const handleWaiting = () => {
5118
- if (isNativeHlsRef.current) return;
5119
- console.log("[HLS] Video waiting (buffer underrun)");
5120
- if (waitingTimerRef.current) {
5121
- clearTimeout(waitingTimerRef.current);
5122
- }
5123
- waitingTimerRef.current = setTimeout(() => {
5124
- const video = videoRef.current;
5125
- if (video && video.readyState < 3) {
5126
- softRestart("waiting timeout");
5127
- }
5128
- }, 1e4);
5129
- };
5130
- const handleTimeUpdate = () => {
5131
- const video = videoRef.current;
5132
- if (!video) return;
5133
- lastTimeUpdateRef.current = video.currentTime;
5134
- if (waitingTimerRef.current) {
5135
- clearTimeout(waitingTimerRef.current);
5136
- waitingTimerRef.current = null;
5137
- }
5138
- };
5139
- const handleNativeError = () => {
5140
- console.error("[HLS] Native video error");
5141
- hardRestart("native video error");
5142
- };
5143
- const startStallDetection = () => {
5144
- if (isNativeHlsRef.current) return;
5145
- stallCheckIntervalRef.current = setInterval(() => {
5146
- const video = videoRef.current;
5147
- if (!video || video.paused || video.ended) return;
5148
- const currentTime = video.currentTime;
5149
- const lastTime = lastTimeUpdateRef.current;
5150
- if (Math.abs(currentTime - lastTime) < 0.1 && video.readyState >= 2) {
5151
- console.warn("[HLS] Playback stall detected");
5152
- if (!noProgressTimerRef.current) {
5153
- noProgressTimerRef.current = setTimeout(() => {
5154
- softRestart("playback stall");
5155
- noProgressTimerRef.current = null;
5156
- }, 8e3);
5157
- }
5158
- } else {
5159
- if (noProgressTimerRef.current) {
5160
- clearTimeout(noProgressTimerRef.current);
5161
- noProgressTimerRef.current = null;
5162
- }
5255
+ }
5256
+ function useWorkspaceNavigation() {
5257
+ const { defaultTimezone } = useDateTimeConfig();
5258
+ const getWorkspaceNavigationParams3 = useCallback(
5259
+ (workspaceId, options) => {
5260
+ let dateToUse = options?.date;
5261
+ if (!dateToUse && options?.useCurrentDate) {
5262
+ dateToUse = getOperationalDate(defaultTimezone || "UTC");
5163
5263
  }
5164
- }, 7e3);
5264
+ return {
5265
+ workspaceId,
5266
+ date: dateToUse,
5267
+ shift: options?.shift,
5268
+ sourceType: options?.sourceType
5269
+ };
5270
+ },
5271
+ [defaultTimezone]
5272
+ );
5273
+ return {
5274
+ getWorkspaceNavigationParams: getWorkspaceNavigationParams3
5165
5275
  };
5166
- useEffect(() => {
5167
- if (!src || !shouldPlay) {
5168
- cleanup();
5169
- return;
5170
- }
5171
- const video = videoRef.current;
5172
- if (!video) return;
5173
- isNativeHlsRef.current = video.canPlayType("application/vnd.apple.mpegurl") === "probably";
5174
- if (Hls2.isSupported() && !isNativeHlsRef.current) {
5175
- const hls = new Hls2(HLS_CONFIG);
5176
- hlsRef.current = hls;
5177
- hls.attachMedia(video);
5178
- hls.loadSource(src);
5179
- hls.on(Hls2.Events.ERROR, (_, data) => {
5180
- if (!data.fatal) return;
5181
- console.error("[HLS] Fatal error:", data.type, data.details);
5182
- if (data.response?.code === 404) {
5183
- hardRestart("404 hard restart");
5184
- return;
5185
- }
5186
- switch (data.type) {
5187
- case Hls2.ErrorTypes.NETWORK_ERROR:
5188
- case Hls2.ErrorTypes.MEDIA_ERROR:
5189
- softRestart(`${data.type}: ${data.details}`);
5190
- break;
5191
- default:
5192
- hardRestart(`Fatal ${data.type}: ${data.details}`);
5193
- break;
5194
- }
5195
- });
5196
- hls.on(Hls2.Events.MANIFEST_PARSED, () => {
5197
- video.play().catch((err) => {
5198
- console.error("[HLS] Play failed:", err);
5199
- });
5200
- });
5201
- video.addEventListener("waiting", handleWaiting);
5202
- video.addEventListener("timeupdate", handleTimeUpdate);
5203
- startStallDetection();
5204
- } else if (isNativeHlsRef.current) {
5205
- console.log("[HLS] Using native HLS");
5206
- video.src = src;
5207
- video.addEventListener("error", handleNativeError);
5208
- video.play().catch((err) => {
5209
- console.error("[HLS] Native play failed:", err);
5210
- });
5211
- } else {
5212
- console.error("[HLS] HLS not supported");
5213
- }
5214
- return cleanup;
5215
- }, [src, shouldPlay, restartKey, onFatalError]);
5276
+ }
5277
+ function useDateFormatter() {
5278
+ const { defaultTimezone, defaultLocale, dateFormatOptions, timeFormatOptions, dateTimeFormatOptions } = useDateTimeConfig();
5279
+ const formatDate = useCallback(
5280
+ (date, formatString) => {
5281
+ const dateObj = typeof date === "string" ? parseISO(date) : date;
5282
+ if (!isValid(dateObj)) return "Invalid Date";
5283
+ const tz = defaultTimezone || "UTC";
5284
+ if (formatString) {
5285
+ return formatInTimeZone(dateObj, tz, formatString);
5286
+ }
5287
+ const effectiveOptions = dateFormatOptions || { year: "numeric", month: "short", day: "numeric" };
5288
+ return new Intl.DateTimeFormat(defaultLocale || "en-US", effectiveOptions).format(dateObj);
5289
+ },
5290
+ [defaultTimezone, defaultLocale, dateFormatOptions]
5291
+ );
5292
+ const formatTime2 = useCallback(
5293
+ (date, formatString) => {
5294
+ const dateObj = typeof date === "string" ? parseISO(date) : date;
5295
+ if (!isValid(dateObj)) return "Invalid Time";
5296
+ const tz = defaultTimezone || "UTC";
5297
+ if (formatString) {
5298
+ return formatInTimeZone(dateObj, tz, formatString);
5299
+ }
5300
+ const effectiveOptions = timeFormatOptions || { hour: "numeric", minute: "numeric" };
5301
+ return new Intl.DateTimeFormat(defaultLocale || "en-US", effectiveOptions).format(dateObj);
5302
+ },
5303
+ [defaultTimezone, defaultLocale, timeFormatOptions]
5304
+ );
5305
+ const formatDateTime = useCallback(
5306
+ (date, formatString) => {
5307
+ const dateObj = typeof date === "string" ? parseISO(date) : date;
5308
+ if (!isValid(dateObj)) return "Invalid Date/Time";
5309
+ const tz = defaultTimezone || "UTC";
5310
+ if (formatString) {
5311
+ return formatInTimeZone(dateObj, tz, formatString);
5312
+ }
5313
+ const effectiveOptions = dateTimeFormatOptions || { year: "numeric", month: "short", day: "numeric", hour: "numeric", minute: "numeric" };
5314
+ return new Intl.DateTimeFormat(defaultLocale || "en-US", effectiveOptions).format(dateObj);
5315
+ },
5316
+ [defaultTimezone, defaultLocale, dateTimeFormatOptions]
5317
+ );
5318
+ const getNow = useCallback(() => {
5319
+ return /* @__PURE__ */ new Date();
5320
+ }, []);
5216
5321
  return {
5217
- restartKey,
5218
- isNativeHls: isNativeHlsRef.current
5322
+ formatDate,
5323
+ formatTime: formatTime2,
5324
+ formatDateTime,
5325
+ getNow,
5326
+ timezone: defaultTimezone || "UTC",
5327
+ locale: defaultLocale || "en-US"
5219
5328
  };
5220
5329
  }
5330
+ var useFormatNumber = () => {
5331
+ const { defaultLocale } = useDateTimeConfig();
5332
+ const formatNumber = useCallback(
5333
+ (value, options) => {
5334
+ try {
5335
+ return new Intl.NumberFormat(defaultLocale || "en-US", options).format(value);
5336
+ } catch (error) {
5337
+ console.error("Error formatting number:", error);
5338
+ return String(value);
5339
+ }
5340
+ },
5341
+ [defaultLocale]
5342
+ );
5343
+ return { formatNumber };
5344
+ };
5221
5345
 
5222
5346
  // src/lib/utils/api.ts
5223
5347
  var apiUtils = {
@@ -14577,16 +14701,16 @@ function createProjectionNode2({ attachResizeListener, defaultParent, measureScr
14577
14701
  if (!this.isVisible) {
14578
14702
  return hiddenVisibility;
14579
14703
  }
14580
- const styles = {
14704
+ const styles2 = {
14581
14705
  visibility: ""
14582
14706
  };
14583
14707
  const transformTemplate = this.getTransformTemplate();
14584
14708
  if (this.needsReset) {
14585
14709
  this.needsReset = false;
14586
- styles.opacity = "";
14587
- styles.pointerEvents = resolveMotionValue(styleProp === null || styleProp === void 0 ? void 0 : styleProp.pointerEvents) || "";
14588
- styles.transform = transformTemplate ? transformTemplate(this.latestValues, "") : "none";
14589
- return styles;
14710
+ styles2.opacity = "";
14711
+ styles2.pointerEvents = resolveMotionValue(styleProp === null || styleProp === void 0 ? void 0 : styleProp.pointerEvents) || "";
14712
+ styles2.transform = transformTemplate ? transformTemplate(this.latestValues, "") : "none";
14713
+ return styles2;
14590
14714
  }
14591
14715
  const lead = this.getLead();
14592
14716
  if (!this.projectionDelta || !this.layout || !lead.target) {
@@ -14603,35 +14727,35 @@ function createProjectionNode2({ attachResizeListener, defaultParent, measureScr
14603
14727
  }
14604
14728
  const valuesToRender = lead.animationValues || lead.latestValues;
14605
14729
  this.applyTransformsToTarget();
14606
- styles.transform = buildProjectionTransform(this.projectionDeltaWithTransform, this.treeScale, valuesToRender);
14730
+ styles2.transform = buildProjectionTransform(this.projectionDeltaWithTransform, this.treeScale, valuesToRender);
14607
14731
  if (transformTemplate) {
14608
- styles.transform = transformTemplate(valuesToRender, styles.transform);
14732
+ styles2.transform = transformTemplate(valuesToRender, styles2.transform);
14609
14733
  }
14610
14734
  const { x, y } = this.projectionDelta;
14611
- styles.transformOrigin = `${x.origin * 100}% ${y.origin * 100}% 0`;
14735
+ styles2.transformOrigin = `${x.origin * 100}% ${y.origin * 100}% 0`;
14612
14736
  if (lead.animationValues) {
14613
- 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;
14737
+ 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;
14614
14738
  } else {
14615
- styles.opacity = lead === this ? valuesToRender.opacity !== void 0 ? valuesToRender.opacity : "" : valuesToRender.opacityExit !== void 0 ? valuesToRender.opacityExit : 0;
14739
+ styles2.opacity = lead === this ? valuesToRender.opacity !== void 0 ? valuesToRender.opacity : "" : valuesToRender.opacityExit !== void 0 ? valuesToRender.opacityExit : 0;
14616
14740
  }
14617
14741
  for (const key in scaleCorrectors) {
14618
14742
  if (valuesToRender[key] === void 0)
14619
14743
  continue;
14620
14744
  const { correct, applyTo } = scaleCorrectors[key];
14621
- const corrected = styles.transform === "none" ? valuesToRender[key] : correct(valuesToRender[key], lead);
14745
+ const corrected = styles2.transform === "none" ? valuesToRender[key] : correct(valuesToRender[key], lead);
14622
14746
  if (applyTo) {
14623
14747
  const num = applyTo.length;
14624
14748
  for (let i = 0; i < num; i++) {
14625
- styles[applyTo[i]] = corrected;
14749
+ styles2[applyTo[i]] = corrected;
14626
14750
  }
14627
14751
  } else {
14628
- styles[key] = corrected;
14752
+ styles2[key] = corrected;
14629
14753
  }
14630
14754
  }
14631
14755
  if (this.options.layoutId) {
14632
- styles.pointerEvents = lead === this ? resolveMotionValue(styleProp === null || styleProp === void 0 ? void 0 : styleProp.pointerEvents) || "" : "none";
14756
+ styles2.pointerEvents = lead === this ? resolveMotionValue(styleProp === null || styleProp === void 0 ? void 0 : styleProp.pointerEvents) || "" : "none";
14633
14757
  }
14634
- return styles;
14758
+ return styles2;
14635
14759
  }
14636
14760
  clearSnapshot() {
14637
14761
  this.resumeFrom = this.snapshot = void 0;
@@ -16738,23 +16862,35 @@ var HourlyOutputChart = ({
16738
16862
  const [animatedData, setAnimatedData] = React14__default.useState(Array(SHIFT_DURATION).fill(0));
16739
16863
  const prevDataRef = React14__default.useRef(Array(SHIFT_DURATION).fill(0));
16740
16864
  const animationFrameRef = React14__default.useRef(null);
16741
- const [shouldAnimateIdle, setShouldAnimateIdle] = React14__default.useState(false);
16865
+ const [idleBarState, setIdleBarState] = React14__default.useState({
16866
+ visible: showIdleTime,
16867
+ key: 0,
16868
+ shouldAnimate: false
16869
+ });
16742
16870
  const prevShowIdleTimeRef = React14__default.useRef(showIdleTime);
16743
- const animationTimeoutRef = React14__default.useRef(null);
16871
+ const stateUpdateTimeoutRef = React14__default.useRef(null);
16744
16872
  React14__default.useEffect(() => {
16873
+ if (stateUpdateTimeoutRef.current) {
16874
+ clearTimeout(stateUpdateTimeoutRef.current);
16875
+ }
16745
16876
  if (showIdleTime && !prevShowIdleTimeRef.current) {
16746
- setShouldAnimateIdle(true);
16747
- if (animationTimeoutRef.current) {
16748
- clearTimeout(animationTimeoutRef.current);
16749
- }
16750
- animationTimeoutRef.current = setTimeout(() => {
16751
- setShouldAnimateIdle(false);
16752
- }, 1e3);
16877
+ requestAnimationFrame(() => {
16878
+ setIdleBarState({
16879
+ visible: true,
16880
+ key: Date.now(),
16881
+ shouldAnimate: true
16882
+ });
16883
+ stateUpdateTimeoutRef.current = setTimeout(() => {
16884
+ setIdleBarState((prev) => ({ ...prev, shouldAnimate: false }));
16885
+ }, 1100);
16886
+ });
16887
+ } else if (!showIdleTime && prevShowIdleTimeRef.current) {
16888
+ setIdleBarState((prev) => ({ ...prev, visible: false }));
16753
16889
  }
16754
16890
  prevShowIdleTimeRef.current = showIdleTime;
16755
16891
  return () => {
16756
- if (animationTimeoutRef.current) {
16757
- clearTimeout(animationTimeoutRef.current);
16892
+ if (stateUpdateTimeoutRef.current) {
16893
+ clearTimeout(stateUpdateTimeoutRef.current);
16758
16894
  }
16759
16895
  };
16760
16896
  }, [showIdleTime]);
@@ -16847,6 +16983,55 @@ var HourlyOutputChart = ({
16847
16983
  };
16848
16984
  });
16849
16985
  }, [animatedData, data, pphThreshold, idleTimeHourly, shiftStartTime.hour, formatHour, formatTimeRange]);
16986
+ const IdleBar = React14__default.useMemo(() => {
16987
+ if (!idleBarState.visible) return null;
16988
+ return /* @__PURE__ */ jsx(
16989
+ Bar,
16990
+ {
16991
+ dataKey: "idleMinutes",
16992
+ yAxisId: "idle",
16993
+ maxBarSize: 35,
16994
+ radius: [10, 10, 0, 0],
16995
+ fill: "url(#idlePattern)",
16996
+ opacity: 0.7,
16997
+ isAnimationActive: idleBarState.shouldAnimate,
16998
+ animationBegin: 0,
16999
+ animationDuration: 1e3,
17000
+ animationEasing: "ease-out",
17001
+ children: /* @__PURE__ */ jsx(
17002
+ LabelList,
17003
+ {
17004
+ dataKey: "idleMinutes",
17005
+ position: "top",
17006
+ content: (props) => {
17007
+ const { x, y, width, value } = props;
17008
+ if (!value || value === 0) return null;
17009
+ return /* @__PURE__ */ jsxs(
17010
+ "text",
17011
+ {
17012
+ x: x + width / 2,
17013
+ y: y - 2,
17014
+ textAnchor: "middle",
17015
+ fontSize: "9",
17016
+ fontWeight: "600",
17017
+ fill: "#6b7280",
17018
+ style: {
17019
+ opacity: 1,
17020
+ pointerEvents: "none"
17021
+ },
17022
+ children: [
17023
+ value,
17024
+ "m"
17025
+ ]
17026
+ }
17027
+ );
17028
+ }
17029
+ }
17030
+ )
17031
+ },
17032
+ `idle-bar-${idleBarState.key}`
17033
+ );
17034
+ }, [idleBarState.visible, idleBarState.key, idleBarState.shouldAnimate]);
16850
17035
  const maxYValue = Math.ceil(pphThreshold * 1.5);
16851
17036
  const generateYAxisTicks = () => {
16852
17037
  const targetValue = Math.round(pphThreshold);
@@ -17104,51 +17289,7 @@ var HourlyOutputChart = ({
17104
17289
  ]
17105
17290
  }
17106
17291
  ),
17107
- showIdleTime && /* @__PURE__ */ jsx(
17108
- Bar,
17109
- {
17110
- dataKey: "idleMinutes",
17111
- yAxisId: "idle",
17112
- maxBarSize: 35,
17113
- radius: [10, 10, 0, 0],
17114
- fill: "url(#idlePattern)",
17115
- opacity: 0.7,
17116
- isAnimationActive: shouldAnimateIdle,
17117
- animationBegin: shouldAnimateIdle ? 200 : 0,
17118
- animationDuration: shouldAnimateIdle ? 800 : 0,
17119
- animationEasing: "ease-out",
17120
- children: /* @__PURE__ */ jsx(
17121
- LabelList,
17122
- {
17123
- dataKey: "idleMinutes",
17124
- position: "top",
17125
- content: (props) => {
17126
- const { x, y, width, value } = props;
17127
- if (!value || value === 0) return null;
17128
- return /* @__PURE__ */ jsxs(
17129
- "text",
17130
- {
17131
- x: x + width / 2,
17132
- y: y - 2,
17133
- textAnchor: "middle",
17134
- fontSize: "9",
17135
- fontWeight: "600",
17136
- fill: "#6b7280",
17137
- style: {
17138
- opacity: 1,
17139
- pointerEvents: "none"
17140
- },
17141
- children: [
17142
- value,
17143
- "m"
17144
- ]
17145
- }
17146
- );
17147
- }
17148
- }
17149
- )
17150
- }
17151
- ),
17292
+ IdleBar,
17152
17293
  /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs("pattern", { id: "idlePattern", patternUnits: "userSpaceOnUse", width: "4", height: "4", children: [
17153
17294
  /* @__PURE__ */ jsx("rect", { width: "4", height: "4", fill: "#9ca3af", opacity: "0.2" }),
17154
17295
  /* @__PURE__ */ 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" })
@@ -17159,15 +17300,15 @@ var HourlyOutputChart = ({
17159
17300
  renderLegend()
17160
17301
  ] });
17161
17302
  };
17162
- var TREND_STYLES = {
17163
- 0: { arrow: "\u2193", color: "text-red-500 font-bold text-shadow" },
17164
- // Down
17165
- 1: { arrow: "\u2013", color: "text-gray-500 font-bold text-shadow" },
17166
- // Unchanged
17167
- 2: { arrow: "\u2191", color: "text-green-500 font-bold text-shadow" }
17168
- // Up
17169
- };
17170
- var getTrendArrowAndColor = (trend) => TREND_STYLES[trend] || { arrow: "", color: "" };
17303
+ function getTrendArrowAndColor(trend) {
17304
+ if (trend > 0) {
17305
+ return { arrow: "\u2191", color: "text-green-400" };
17306
+ } else if (trend < 0) {
17307
+ return { arrow: "\u2193", color: "text-red-400" };
17308
+ } else {
17309
+ return { arrow: "\u2192", color: "text-gray-400" };
17310
+ }
17311
+ }
17171
17312
  var VideoCard = React14__default.memo(({
17172
17313
  workspace,
17173
17314
  hlsUrl,
@@ -17175,14 +17316,29 @@ var VideoCard = React14__default.memo(({
17175
17316
  onClick,
17176
17317
  onFatalError,
17177
17318
  isVeryLowEfficiency = false,
17319
+ cropping,
17320
+ canvasFps = 30,
17321
+ useRAF = true,
17178
17322
  className = ""
17179
17323
  }) => {
17180
17324
  const videoRef = useRef(null);
17181
- useHlsStream(videoRef, {
17182
- src: hlsUrl,
17183
- shouldPlay,
17184
- onFatalError: onFatalError ?? (() => throttledReloadDashboard())
17185
- });
17325
+ const canvasRef = useRef(null);
17326
+ if (cropping) {
17327
+ useHlsStreamWithCropping(videoRef, canvasRef, {
17328
+ src: hlsUrl,
17329
+ shouldPlay,
17330
+ cropping,
17331
+ canvasFps,
17332
+ useRAF,
17333
+ onFatalError: onFatalError ?? (() => throttledReloadDashboard())
17334
+ });
17335
+ } else {
17336
+ useHlsStream(videoRef, {
17337
+ src: hlsUrl,
17338
+ shouldPlay,
17339
+ onFatalError: onFatalError ?? (() => throttledReloadDashboard())
17340
+ });
17341
+ }
17186
17342
  const displayName = getWorkspaceDisplayName(workspace.workspace_name);
17187
17343
  workspace.workspace_uuid || workspace.workspace_name;
17188
17344
  const getEfficiencyOverlayColor = (efficiency) => {
@@ -17237,17 +17393,26 @@ var VideoCard = React14__default.memo(({
17237
17393
  /* @__PURE__ */ jsx(Camera, { className: "w-6 h-6 text-gray-500" }),
17238
17394
  /* @__PURE__ */ jsx("span", { className: "text-xs text-gray-500 mt-1", children: "Loading..." })
17239
17395
  ] }) }),
17240
- /* @__PURE__ */ jsx("div", { className: "absolute inset-0 z-10", children: /* @__PURE__ */ jsx(
17241
- "video",
17242
- {
17243
- ref: videoRef,
17244
- className: "h-full w-full object-cover",
17245
- playsInline: true,
17246
- muted: true,
17247
- disablePictureInPicture: true,
17248
- controlsList: "nodownload noplaybackrate"
17249
- }
17250
- ) }),
17396
+ /* @__PURE__ */ jsxs("div", { className: "absolute inset-0 z-10", children: [
17397
+ /* @__PURE__ */ jsx(
17398
+ "video",
17399
+ {
17400
+ ref: videoRef,
17401
+ className: `h-full w-full object-cover ${cropping ? "hidden" : ""}`,
17402
+ playsInline: true,
17403
+ muted: true,
17404
+ disablePictureInPicture: true,
17405
+ controlsList: "nodownload noplaybackrate"
17406
+ }
17407
+ ),
17408
+ cropping && /* @__PURE__ */ jsx(
17409
+ "canvas",
17410
+ {
17411
+ ref: canvasRef,
17412
+ className: "h-full w-full object-cover"
17413
+ }
17414
+ )
17415
+ ] }),
17251
17416
  /* @__PURE__ */ jsx("div", { className: `absolute inset-0 z-20 pointer-events-none ${efficiencyOverlayClass}` }),
17252
17417
  /* @__PURE__ */ 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: [
17253
17418
  Math.round(workspace.efficiency),
@@ -17283,7 +17448,7 @@ var VideoCard = React14__default.memo(({
17283
17448
  }
17284
17449
  );
17285
17450
  }, (prevProps, nextProps) => {
17286
- 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;
17451
+ 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;
17287
17452
  });
17288
17453
  VideoCard.displayName = "VideoCard";
17289
17454
  var DEFAULT_WORKSPACE_HLS_URLS = {
@@ -17311,6 +17476,8 @@ var VideoGridView = React14__default.memo(({
17311
17476
  const observerRef = useRef(null);
17312
17477
  const [gridCols, setGridCols] = useState(4);
17313
17478
  const [visibleWorkspaces, setVisibleWorkspaces] = useState(/* @__PURE__ */ new Set());
17479
+ const videoConfig = useVideoConfig();
17480
+ const { cropping, canvasConfig } = videoConfig;
17314
17481
  const mergedVideoSources = {
17315
17482
  defaultHlsUrl: videoSources.defaultHlsUrl || DEFAULT_HLS_URL,
17316
17483
  workspaceHlsUrls: { ...DEFAULT_WORKSPACE_HLS_URLS, ...videoSources.workspaceHlsUrls }
@@ -17319,6 +17486,13 @@ var VideoGridView = React14__default.memo(({
17319
17486
  const wsName = workspaceName.toUpperCase();
17320
17487
  return mergedVideoSources.workspaceHlsUrls[wsName] || mergedVideoSources.defaultHlsUrl;
17321
17488
  }, [mergedVideoSources]);
17489
+ const getWorkspaceCropping = useCallback((workspaceName) => {
17490
+ if (!cropping) return void 0;
17491
+ if (cropping.workspaceOverrides?.[workspaceName]) {
17492
+ return cropping.workspaceOverrides[workspaceName];
17493
+ }
17494
+ return cropping.default;
17495
+ }, [cropping]);
17322
17496
  const veryLowEfficiencyWorkspaces = useMemo(() => {
17323
17497
  return new Set(
17324
17498
  workspaces.filter((w) => w.efficiency < 50 && w.efficiency >= 10).map((w) => w.workspace_name)
@@ -17457,6 +17631,7 @@ var VideoGridView = React14__default.memo(({
17457
17631
  const workspaceId = workspace.workspace_uuid || workspace.workspace_name;
17458
17632
  const isVisible = visibleWorkspaces.has(workspaceId);
17459
17633
  const isVeryLowEfficiency = veryLowEfficiencyWorkspaces.has(workspace.workspace_name);
17634
+ const workspaceCropping = getWorkspaceCropping(workspace.workspace_name);
17460
17635
  return /* @__PURE__ */ jsx(
17461
17636
  "div",
17462
17637
  {
@@ -17471,7 +17646,10 @@ var VideoGridView = React14__default.memo(({
17471
17646
  shouldPlay: isVisible,
17472
17647
  onClick: () => handleWorkspaceClick(workspace),
17473
17648
  onFatalError: throttledReloadDashboard,
17474
- isVeryLowEfficiency
17649
+ isVeryLowEfficiency,
17650
+ cropping: workspaceCropping,
17651
+ canvasFps: canvasConfig?.fps,
17652
+ useRAF: canvasConfig?.useRAF
17475
17653
  }
17476
17654
  )
17477
17655
  },
@@ -19584,6 +19762,32 @@ var WorkspaceCard = ({
19584
19762
  }
19585
19763
  );
19586
19764
  };
19765
+ var styles = `
19766
+ @keyframes fadeIn {
19767
+ from {
19768
+ opacity: 0;
19769
+ transform: translateY(-10px);
19770
+ }
19771
+ to {
19772
+ opacity: 1;
19773
+ transform: translateY(0);
19774
+ }
19775
+ }
19776
+
19777
+ .calendar-container {
19778
+ will-change: transform, opacity;
19779
+ animation: fadeIn 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
19780
+ }
19781
+
19782
+ .calendar-wrapper:not(.animation-complete) * {
19783
+ transition: none !important;
19784
+ }
19785
+
19786
+ .calendar-wrapper:not(.animation-complete) .group:hover {
19787
+ opacity: 1 !important;
19788
+ transform: scale(1) !important;
19789
+ }
19790
+ `;
19587
19791
  var WEEKDAYS2 = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
19588
19792
  var getTimeInZoneAsDate = (timezone) => {
19589
19793
  const time2 = getCurrentTimeInZone(timezone);
@@ -19602,20 +19806,20 @@ var WorkspaceHistoryCalendar = ({
19602
19806
  }) => {
19603
19807
  const { dateTimeConfig } = useDashboardConfig();
19604
19808
  const configuredTimezone = dateTimeConfig?.defaultTimezone || "Asia/Kolkata";
19809
+ const [animationComplete, setAnimationComplete] = useState(false);
19810
+ useEffect(() => {
19811
+ setAnimationComplete(false);
19812
+ const timer = setTimeout(() => {
19813
+ setAnimationComplete(true);
19814
+ }, 600);
19815
+ return () => clearTimeout(timer);
19816
+ }, [month, year]);
19605
19817
  const calendarData = useMemo(() => {
19606
19818
  const startOfMonth = toZonedTime(new Date(year, month, 1), configuredTimezone);
19607
19819
  const endOfMonth = toZonedTime(new Date(year, month + 1, 0), configuredTimezone);
19608
19820
  const totalDays = endOfMonth.getDate();
19609
19821
  let startOffset = startOfMonth.getDay() - 1;
19610
19822
  if (startOffset === -1) startOffset = 6;
19611
- console.log("Calendar generation for:", {
19612
- month: month + 1,
19613
- year,
19614
- startOffset,
19615
- totalDays,
19616
- configuredTimezone
19617
- });
19618
- console.log("Data points received:", data);
19619
19823
  const calendar = Array(startOffset).fill(null);
19620
19824
  for (let day = 1; day <= totalDays; day++) {
19621
19825
  const currentDate = new Date(year, month, day);
@@ -19629,15 +19833,6 @@ var WorkspaceHistoryCalendar = ({
19629
19833
  const dataDate = new Date(d.date);
19630
19834
  const dataDateStr = dataDate.toISOString().split("T")[0];
19631
19835
  const matches = dataDateStr === dateToMatch;
19632
- if (day <= 3 && matches) {
19633
- console.log(`Found data match for day ${day}:`, {
19634
- dataDate: dataDate.toISOString(),
19635
- currentDate: currentTimezoneDate.toISOString(),
19636
- dataDateStr,
19637
- dateToMatch,
19638
- data: d
19639
- });
19640
- }
19641
19836
  return matches;
19642
19837
  });
19643
19838
  if (matchingData.length > 0) {
@@ -19648,32 +19843,10 @@ var WorkspaceHistoryCalendar = ({
19648
19843
  // Use the timezone-adjusted date for display
19649
19844
  });
19650
19845
  } else {
19651
- calendar.push({
19652
- date: currentTimezoneDate,
19653
- dayShift: {
19654
- efficiency: 0,
19655
- output: 0,
19656
- cycleTime: 0,
19657
- pph: 0,
19658
- pphThreshold: 0,
19659
- idealOutput: 0,
19660
- rank: 0,
19661
- idleTime: 0
19662
- },
19663
- nightShift: {
19664
- efficiency: 0,
19665
- output: 0,
19666
- cycleTime: 0,
19667
- pph: 0,
19668
- pphThreshold: 0,
19669
- idealOutput: 0,
19670
- rank: 0,
19671
- idleTime: 0
19672
- }
19673
- });
19846
+ calendar.push(null);
19674
19847
  }
19675
19848
  }
19676
- return calendar;
19849
+ return { calendar, startOffset };
19677
19850
  }, [data, month, year, configuredTimezone]);
19678
19851
  const monthlyMetrics = useMemo(() => {
19679
19852
  const validDays = data.filter((day) => {
@@ -19690,15 +19863,17 @@ var WorkspaceHistoryCalendar = ({
19690
19863
  return [];
19691
19864
  }
19692
19865
  return [day.dayShift, day.nightShift];
19693
- }).filter((shift) => shift.efficiency > 0);
19866
+ });
19694
19867
  if (validShifts.length === 0) return null;
19695
19868
  const badShiftsCount = validShifts.filter((shift) => shift.efficiency < 75).length;
19869
+ const totalIdleTime = validShifts.reduce((sum, shift) => sum + shift.idleTime, 0);
19870
+ const avgIdleTime = Math.round(totalIdleTime / validShifts.length);
19696
19871
  return {
19697
19872
  avgEfficiency: Math.round(validShifts.reduce((sum, shift) => sum + shift.efficiency, 0) / validShifts.length),
19698
19873
  avgCycleTime: Math.round(validShifts.reduce((sum, shift) => sum + shift.cycleTime, 0) / validShifts.length),
19874
+ avgIdleTime,
19699
19875
  badDaysCount: badShiftsCount,
19700
- totalDays: validShifts.length,
19701
- avgIdleTime: Math.round(validShifts.reduce((sum, shift) => sum + (shift.idleTime || 0), 0) / validShifts.length)
19876
+ totalDays: validShifts.length
19702
19877
  };
19703
19878
  }, [data, month, year, configuredTimezone]);
19704
19879
  const handleDayClick = useCallback((day, shift) => {
@@ -19746,7 +19921,6 @@ var WorkspaceHistoryCalendar = ({
19746
19921
  compareDate.setHours(0, 0, 0, 0);
19747
19922
  if (compareDate.getDay() === 0) return "bg-gray-300";
19748
19923
  if (compareDate > istNow) return "bg-gray-200";
19749
- if (efficiency < 10) return "bg-gray-300";
19750
19924
  if (efficiency >= 80) return "bg-[#00AB45]/90";
19751
19925
  if (efficiency >= 70) return "bg-[#FFB020]/90";
19752
19926
  return "bg-[#E34329]/90";
@@ -19785,38 +19959,55 @@ var WorkspaceHistoryCalendar = ({
19785
19959
  ] })
19786
19960
  ] }) });
19787
19961
  };
19788
- const renderDayCell = useCallback((day) => {
19789
- if (!day) return /* @__PURE__ */ jsx("div", { className: "h-full border border-gray-100 rounded-lg bg-gray-50 transition-all duration-200 ease-in-out" });
19962
+ const renderDayCell = useCallback((day, dayNumber, index) => {
19963
+ if (dayNumber === null) {
19964
+ return /* @__PURE__ */ jsx("div", { className: "h-full" });
19965
+ }
19966
+ const cellDate = toZonedTime(new Date(year, month, dayNumber), configuredTimezone);
19967
+ const isToday = isCurrentDate(cellDate);
19968
+ const isFuture = isFutureDate(cellDate);
19969
+ const isSunday = cellDate.getDay() === 0;
19970
+ if (!day) {
19971
+ let bgColor = "bg-gray-100";
19972
+ let textColor = "text-gray-400";
19973
+ if (isSunday) {
19974
+ bgColor = "bg-gray-200";
19975
+ textColor = "text-gray-500";
19976
+ }
19977
+ if (isFuture) {
19978
+ bgColor = "bg-gray-50";
19979
+ textColor = "text-gray-300";
19980
+ }
19981
+ return /* @__PURE__ */ 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__ */ jsx("div", { className: "p-2", children: /* @__PURE__ */ jsx("div", { className: `text-base font-medium ${textColor} ${isToday ? "text-blue-500" : ""}`, children: dayNumber }) }) });
19982
+ }
19790
19983
  const shiftData = selectedShift === "day" ? day.dayShift : day.nightShift;
19791
- const isToday = isCurrentDate(day.date);
19792
- const isFuture = isFutureDate(day.date);
19793
- const isInactive = shiftData.efficiency < 10;
19794
19984
  return /* @__PURE__ */ jsx(
19795
19985
  "div",
19796
19986
  {
19797
- className: `group h-full transition-all duration-200 ease-in-out ${!isFuture && !isInactive ? "cursor-pointer hover:opacity-90" : "cursor-not-allowed"}`,
19798
- onClick: () => !isFuture && !isInactive && handleDayClick(day, selectedShift),
19987
+ 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"}`,
19988
+ onClick: () => !isFuture && handleDayClick(day, selectedShift),
19799
19989
  children: /* @__PURE__ */ jsxs("div", { className: `
19800
19990
  ${getPerformanceColor(shiftData.efficiency, day.date)}
19801
- rounded-lg h-full p-2 relative transition-all duration-200 ease-in-out
19991
+ rounded-lg h-full p-2 relative ${animationComplete ? "transition-all duration-300 ease-in-out" : ""} shadow-sm
19802
19992
  ${isToday ? "ring-2 ring-blue-500 ring-offset-2 shadow-md" : ""}
19803
19993
  `, children: [
19804
19994
  /* @__PURE__ */ jsx("div", { className: `
19805
- text-base font-medium text-white flex items-center transition-all duration-200 ease-in-out
19995
+ text-base font-medium text-white flex items-center ${animationComplete ? "transition-all duration-300 ease-in-out" : ""}
19806
19996
  ${isToday ? "bg-blue-500 rounded-full w-7 h-7 justify-center" : ""}
19807
19997
  `, children: day.date.getDate() }),
19808
- !isFuture && !isInactive && renderStats(shiftData, day.date)
19998
+ !isFuture && animationComplete && renderStats(shiftData, day.date)
19809
19999
  ] })
19810
20000
  }
19811
20001
  );
19812
- }, [selectedShift, isCurrentDate, isFutureDate, getPerformanceColor, handleDayClick]);
19813
- return /* @__PURE__ */ jsxs("div", { className: `space-y-6 ${className || ""}`, children: [
20002
+ }, [selectedShift, isCurrentDate, isFutureDate, getPerformanceColor, handleDayClick, year, month, configuredTimezone, animationComplete]);
20003
+ return /* @__PURE__ */ jsxs("div", { className: `calendar-wrapper space-y-6 ${className || ""} ${animationComplete ? "animation-complete" : ""}`, children: [
20004
+ /* @__PURE__ */ jsx("style", { dangerouslySetInnerHTML: { __html: styles } }),
19814
20005
  /* @__PURE__ */ jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsxs("div", { className: "flex gap-1 border border-gray-200 rounded-lg p-1 bg-gray-50", children: [
19815
20006
  /* @__PURE__ */ jsx(
19816
20007
  "button",
19817
20008
  {
19818
20009
  onClick: () => handleShiftChange("day"),
19819
- 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"}`,
20010
+ 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" : ""}`}`,
19820
20011
  children: "Day Shift"
19821
20012
  }
19822
20013
  ),
@@ -19824,23 +20015,27 @@ var WorkspaceHistoryCalendar = ({
19824
20015
  "button",
19825
20016
  {
19826
20017
  onClick: () => handleShiftChange("night"),
19827
- 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"}`,
20018
+ 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" : ""}`}`,
19828
20019
  children: "Night Shift"
19829
20020
  }
19830
20021
  )
19831
20022
  ] }) }),
19832
20023
  /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-8", children: [
19833
- /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-100 p-6 transition-all duration-200 ease-in-out", children: [
20024
+ /* @__PURE__ */ 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: [
19834
20025
  /* @__PURE__ */ jsxs("div", { className: "mb-6", children: [
19835
20026
  /* @__PURE__ */ jsx("h3", { className: "font-semibold text-gray-900 text-lg", children: selectedShift === "day" ? "Day Shifts" : "Night Shifts" }),
19836
20027
  /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500 mt-1", children: "Calendar view of daily performance" })
19837
20028
  ] }),
19838
20029
  /* @__PURE__ */ jsxs("div", { className: "grid gap-6", children: [
19839
20030
  /* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 gap-2", children: WEEKDAYS2.map((day) => /* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-gray-600 text-center", children: day }, day)) }),
19840
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 gap-2", children: calendarData.map((day, index) => /* @__PURE__ */ jsx("div", { className: "aspect-square relative transition-all duration-200 ease-in-out", children: renderDayCell(day) }, index)) })
20031
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 gap-2", children: calendarData.calendar.map((day, index) => {
20032
+ const startOffset = calendarData.startOffset;
20033
+ const dayNumber = index >= startOffset ? index - startOffset + 1 : null;
20034
+ return /* @__PURE__ */ 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);
20035
+ }) })
19841
20036
  ] })
19842
20037
  ] }),
19843
- /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-100 p-6 transition-all duration-200 ease-in-out", children: [
20038
+ /* @__PURE__ */ 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: [
19844
20039
  /* @__PURE__ */ jsxs("div", { className: "mb-6", children: [
19845
20040
  /* @__PURE__ */ jsx("h3", { className: "font-semibold text-gray-900 text-lg", children: "Monthly Summary" }),
19846
20041
  /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500 mt-1", children: "Overview of monthly performance metrics" })
@@ -22298,12 +22493,12 @@ var getEfficiencyColor = (efficiency) => {
22298
22493
  return "bg-[#E34329]/90 hover:bg-[#E34329]/95";
22299
22494
  }
22300
22495
  };
22301
- var TREND_STYLES2 = {
22496
+ var TREND_STYLES = {
22302
22497
  0: { arrow: "\u2193", color: "text-red-500 font-bold text-shadow" },
22303
22498
  1: { arrow: "=", color: "text-gray-500 font-bold text-shadow" },
22304
22499
  2: { arrow: "\u2191", color: "text-green-500 font-bold text-shadow" }
22305
22500
  };
22306
- var getTrendArrowAndColor2 = (trend) => TREND_STYLES2[trend] || { arrow: "", color: "" };
22501
+ var getTrendArrowAndColor2 = (trend) => TREND_STYLES[trend] || { arrow: "", color: "" };
22307
22502
  var ARROW_POSITIONS = {
22308
22503
  top: "-bottom-6",
22309
22504
  "middle-top": "-bottom-6",
@@ -22411,7 +22606,7 @@ var WorkspaceGridItem = React14__default.memo(({
22411
22606
  }, [data.efficiency, isInactive, position.size, position.orientation]);
22412
22607
  const { arrow, color: arrowColor } = useMemo(() => getTrendArrowAndColor2(data.trend_score), [data.trend_score]);
22413
22608
  const workspaceNumber = useMemo(() => getWorkspaceNumber(data.workspace_name), [data.workspace_name]);
22414
- const styles = useMemo(() => getWorkspaceStyles(position, isInactive), [position, isInactive]);
22609
+ const styles2 = useMemo(() => getWorkspaceStyles(position, isInactive), [position, isInactive]);
22415
22610
  const arrowPosition = useMemo(() => getArrowPositionClass(position), [position]);
22416
22611
  const handleClick = useCallback((e) => {
22417
22612
  e.preventDefault();
@@ -22462,7 +22657,7 @@ var WorkspaceGridItem = React14__default.memo(({
22462
22657
  "button",
22463
22658
  {
22464
22659
  onClick: handleClick,
22465
- className: `${styles} ${colorClass} ${isBottleneck ? "ring-2 ring-red-500/70" : ""} ${isVeryLowEfficiency ? "ring-2 ring-red-500/50" : ""} ${isInactive ? "bg-gray-200" : ""} shadow-lg`,
22660
+ className: `${styles2} ${colorClass} ${isBottleneck ? "ring-2 ring-red-500/70" : ""} ${isVeryLowEfficiency ? "ring-2 ring-red-500/50" : ""} ${isInactive ? "bg-gray-200" : ""} shadow-lg`,
22466
22661
  "aria-label": isInactive ? `Inactive workspace ${workspaceNumber}` : `View details for workspace ${workspaceNumber}`,
22467
22662
  title: isInactive ? `Inactive: ${getWorkspaceDisplayName(data.workspace_name)}` : getWorkspaceDisplayName(data.workspace_name),
22468
22663
  children: /* @__PURE__ */ jsx("div", { className: `font-semibold tracking-wide text-[min(4vw,2rem)] uppercase ${isInactive ? "text-gray-400" : "text-white"} drop-shadow-sm`, children: workspaceNumber })
@@ -29973,6 +30168,11 @@ var WorkspaceDetailView = ({
29973
30168
  return getOperationalDate();
29974
30169
  }, [isHistoricView, date]);
29975
30170
  const handleMonthlyDataLoaded = useCallback((data) => {
30171
+ console.log("[handleMonthlyDataLoaded] Received data:", {
30172
+ dataLength: data?.length,
30173
+ isArray: Array.isArray(data),
30174
+ sample: data?.[0]
30175
+ });
29976
30176
  if (!data || !Array.isArray(data)) {
29977
30177
  console.error("Invalid monthly metrics data received:", data);
29978
30178
  setMonthlyData([]);
@@ -29990,22 +30190,35 @@ var WorkspaceDetailView = ({
29990
30190
  if (!dayEntry) {
29991
30191
  dayEntry = {
29992
30192
  date: dateObj,
29993
- dayShift: { efficiency: 0, output: 0, cycleTime: 0, pph: 0, pphThreshold: 0, idealOutput: 0, rank: 0, idleTime: 0 },
29994
- nightShift: { efficiency: 0, output: 0, cycleTime: 0, pph: 0, pphThreshold: 0, idealOutput: 0, rank: 0, idleTime: 0 }
30193
+ dayShift: null,
30194
+ // Start with null instead of zeros
30195
+ nightShift: null
30196
+ // Start with null instead of zeros
29995
30197
  };
29996
30198
  dayDataMap.set(dateKey, dayEntry);
29997
30199
  }
29998
- const shiftTarget = metric.shift_id === 0 ? dayEntry.dayShift : dayEntry.nightShift;
29999
- shiftTarget.efficiency = metric.avg_efficiency || 0;
30000
- shiftTarget.output = metric.total_output || 0;
30001
- shiftTarget.cycleTime = metric.avg_cycle_time || 0;
30002
- shiftTarget.pph = metric.avg_pph || 0;
30003
- shiftTarget.pphThreshold = metric.pph_threshold || 0;
30004
- shiftTarget.idealOutput = metric.ideal_output || 0;
30005
- shiftTarget.rank = metric.workspace_rank || 0;
30006
- shiftTarget.idleTime = metric.idle_time || 0;
30200
+ const shiftData = {
30201
+ efficiency: metric.avg_efficiency || 0,
30202
+ output: metric.total_output || 0,
30203
+ cycleTime: metric.avg_cycle_time || 0,
30204
+ pph: metric.avg_pph || 0,
30205
+ pphThreshold: metric.pph_threshold || 0,
30206
+ idealOutput: metric.ideal_output || 0,
30207
+ rank: metric.workspace_rank || 0,
30208
+ idleTime: metric.idle_time || 0
30209
+ };
30210
+ if (metric.shift_id === 0) {
30211
+ dayEntry.dayShift = shiftData;
30212
+ } else {
30213
+ dayEntry.nightShift = shiftData;
30214
+ }
30007
30215
  });
30008
- const processedData = Array.from(dayDataMap.values());
30216
+ const processedData = Array.from(dayDataMap.values()).filter((entry) => entry.dayShift !== null || entry.nightShift !== null).map((entry) => ({
30217
+ date: entry.date,
30218
+ // If a shift is null (no data), provide zeros for compatibility
30219
+ dayShift: entry.dayShift || { efficiency: 0, output: 0, cycleTime: 0, pph: 0, pphThreshold: 0, idealOutput: 0, rank: 0, idleTime: 0 },
30220
+ nightShift: entry.nightShift || { efficiency: 0, output: 0, cycleTime: 0, pph: 0, pphThreshold: 0, idealOutput: 0, rank: 0, idleTime: 0 }
30221
+ }));
30009
30222
  console.log(`[handleMonthlyDataLoaded] Transformed data for calendar:`, {
30010
30223
  count: processedData.length,
30011
30224
  sample: processedData.slice(0, 1)
@@ -30465,7 +30678,7 @@ var WorkspaceDetailView = ({
30465
30678
  }
30466
30679
  )
30467
30680
  ] }),
30468
- monthlyDataLoading ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center h-[calc(100%-5rem)]", children: /* @__PURE__ */ jsx("div", { className: "text-xl text-gray-600", children: "Loading monthly data..." }) }) : /* @__PURE__ */ jsx(
30681
+ /* @__PURE__ */ jsx(
30469
30682
  WorkspaceHistoryCalendar,
30470
30683
  {
30471
30684
  data: monthlyData,
@@ -30744,4 +30957,4 @@ var S3Service = class {
30744
30957
  }
30745
30958
  };
30746
30959
 
30747
- export { ACTION_NAMES, AIAgentView_default as AIAgentView, AuthCallback, AuthCallbackView_default as AuthCallbackView, AuthProvider, AuthenticatedFactoryView, AuthenticatedHelpView, AuthenticatedHomeView, AuthenticatedTargetsView, BarChart, BaseHistoryCalendar, BottlenecksContent, BreakNotificationPopup, Card2 as Card, CardContent2 as CardContent, CardDescription2 as CardDescription, CardFooter2 as CardFooter, CardHeader2 as CardHeader, CardTitle2 as CardTitle, CycleTimeChart, CycleTimeOverTimeChart, DEFAULT_ANALYTICS_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_CONFIG, DEFAULT_DATABASE_CONFIG, DEFAULT_DATE_TIME_CONFIG, DEFAULT_ENDPOINTS_CONFIG, DEFAULT_ENTITY_CONFIG, DEFAULT_SHIFT_CONFIG, DEFAULT_THEME_CONFIG, DEFAULT_WORKSPACE_CONFIG, DEFAULT_WORKSPACE_POSITIONS, DashboardHeader, DashboardLayout, DashboardOverridesProvider, DashboardProvider, DateDisplay_default as DateDisplay, DateTimeDisplay, DebugAuth, DebugAuthView_default as DebugAuthView, EmptyStateMessage, FactoryView_default as FactoryView, GridComponentsPlaceholder, Header, HelpView_default as HelpView, HomeView_default as HomeView, HourlyOutputChart2 as HourlyOutputChart, ISTTimer_default as ISTTimer, KPICard, KPIDetailView_default as KPIDetailView, KPIGrid, KPIHeader, KPISection, KPIsOverviewView_default as KPIsOverviewView, LINE_1_UUID, LargeOutputProgressChart, LeaderboardDetailView_default as LeaderboardDetailView, Legend5 as Legend, LineChart, LineHistoryCalendar, LineMonthlyHistory, LineMonthlyPdfGenerator, LinePdfExportButton, LinePdfGenerator, LineWhatsAppShareButton, LiveTimer, LoadingOverlay_default as LoadingOverlay, LoadingPage_default as LoadingPage, LoadingSpinner_default as LoadingSpinner, LoginPage, LoginView_default as LoginView, MainLayout, MetricCard_default as MetricCard, NoWorkspaceData, OptifyeAgentClient, OutputProgressChart, PageHeader, ProfileView_default as ProfileView, RegistryProvider, S3Service, SOPComplianceChart, SSEChatClient, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, ShiftDisplay_default as ShiftDisplay, ShiftsView_default as ShiftsView, SideNavBar, SingleVideoStream_default as SingleVideoStream, Skeleton, SlackAPI, SupabaseProvider, TargetWorkspaceGrid, TargetsView_default as TargetsView, ThreadSidebar, TimeDisplay_default as TimeDisplay, TimePickerDropdown, VideoCard, VideoGridView, VideoPreloader, WORKSPACE_POSITIONS, WhatsAppShareButton, WorkspaceCard, WorkspaceDetailView_default as WorkspaceDetailView, WorkspaceGrid, WorkspaceGridItem, WorkspaceHistoryCalendar, WorkspaceMetricCards, WorkspaceMonthlyDataFetcher, WorkspaceMonthlyPdfGenerator, WorkspacePdfExportButton, WorkspacePdfGenerator, WorkspaceWhatsAppShareButton, actionService, apiUtils, authCoreService, authOTPService, authRateLimitService, checkRateLimit2 as checkRateLimit, clearAllRateLimits2 as clearAllRateLimits, clearRateLimit2 as clearRateLimit, clearS3VideoCache, clearS3VideoFromCache, clearWorkspaceDisplayNamesCache, cn, createStreamProxyHandler, createSupabaseClient, createThrottledReload, dashboardService, deleteThread, forceRefreshWorkspaceDisplayNames, formatDateInZone, formatDateTimeInZone, formatISTDate, formatIdleTime, formatTimeInZone, fromUrlFriendlyName, getAllThreadMessages, getAllWorkspaceDisplayNamesAsync, getAnonClient, getCameraNumber, getCompanyMetricsTableName, getConfigurableShortWorkspaceDisplayName, getConfigurableWorkspaceDisplayName, getCurrentShift, getCurrentTimeInZone, getDashboardHeaderTimeInZone, getDaysDifferenceInZone, getDefaultCameraStreamUrl, getDefaultTabForWorkspace, getManufacturingInsights, getMetricsTablePrefix, getOperationalDate, getS3SignedUrl, getS3VideoSrc, getShortWorkspaceDisplayName, getShortWorkspaceDisplayNameAsync, getStoredWorkspaceMappings, getThreadMessages, getUserThreads, getUserThreadsPaginated, getWorkspaceDisplayName, getWorkspaceDisplayNameAsync, getWorkspaceDisplayNamesMap, getWorkspaceFromUrl, getWorkspaceNavigationParams, identifyCoreUser, initializeCoreMixpanel, isTransitionPeriod, isValidLineInfoPayload, isValidWorkspaceDetailedMetricsPayload, isValidWorkspaceMetricsPayload, isWorkspaceDisplayNamesLoaded, isWorkspaceDisplayNamesLoading, mergeWithDefaultConfig, optifyeAgentClient, preInitializeWorkspaceDisplayNames, preloadS3Video, preloadS3VideoUrl, preloadS3VideosUrl, preloadVideoUrl, preloadVideosUrl, qualityService, realtimeService, refreshWorkspaceDisplayNames, resetCoreMixpanel, s3VideoPreloader, storeWorkspaceMapping, streamProxyConfig, throttledReloadDashboard, toUrlFriendlyName, trackCoreEvent, trackCorePageView, updateThreadTitle, useActiveBreaks, useAnalyticsConfig, useAuth, useAuthConfig, useComponentOverride, useCustomConfig, useDashboardConfig, useDashboardMetrics, useDatabaseConfig, useDateFormatter, useDateTimeConfig, useEndpointsConfig, useEntityConfig, useFactoryOverviewMetrics, useFeatureFlags, useFormatNumber, useHistoricWorkspaceMetrics, useHlsStream, useHookOverride, useLeaderboardMetrics, useLineDetailedMetrics, useLineKPIs, useLineMetrics, useLineWorkspaceMetrics, useMessages, useMetrics, useNavigation, useOverrides, usePageOverride, useRealtimeLineMetrics, useRegistry, useShiftConfig, useShifts, useSupabase, useSupabaseClient, useTargets, useTheme, useThemeConfig, useThreads, useWorkspaceConfig, useWorkspaceDetailedMetrics, useWorkspaceDisplayName, useWorkspaceDisplayNames, useWorkspaceDisplayNamesMap, useWorkspaceMetrics, useWorkspaceNavigation, useWorkspaceOperators, videoPreloader, whatsappService, withAuth, withRegistry, workspaceService };
30960
+ export { ACTION_NAMES, AIAgentView_default as AIAgentView, AuthCallback, AuthCallbackView_default as AuthCallbackView, AuthProvider, AuthenticatedFactoryView, AuthenticatedHelpView, AuthenticatedHomeView, AuthenticatedTargetsView, BarChart, BaseHistoryCalendar, BottlenecksContent, BreakNotificationPopup, Card2 as Card, CardContent2 as CardContent, CardDescription2 as CardDescription, CardFooter2 as CardFooter, CardHeader2 as CardHeader, CardTitle2 as CardTitle, CycleTimeChart, CycleTimeOverTimeChart, DEFAULT_ANALYTICS_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_CONFIG, DEFAULT_DATABASE_CONFIG, DEFAULT_DATE_TIME_CONFIG, DEFAULT_ENDPOINTS_CONFIG, DEFAULT_ENTITY_CONFIG, DEFAULT_SHIFT_CONFIG, DEFAULT_THEME_CONFIG, DEFAULT_VIDEO_CONFIG, DEFAULT_WORKSPACE_CONFIG, DEFAULT_WORKSPACE_POSITIONS, DashboardHeader, DashboardLayout, DashboardOverridesProvider, DashboardProvider, DateDisplay_default as DateDisplay, DateTimeDisplay, DebugAuth, DebugAuthView_default as DebugAuthView, EmptyStateMessage, FactoryView_default as FactoryView, GridComponentsPlaceholder, Header, HelpView_default as HelpView, HomeView_default as HomeView, HourlyOutputChart2 as HourlyOutputChart, ISTTimer_default as ISTTimer, KPICard, KPIDetailView_default as KPIDetailView, KPIGrid, KPIHeader, KPISection, KPIsOverviewView_default as KPIsOverviewView, LINE_1_UUID, LargeOutputProgressChart, LeaderboardDetailView_default as LeaderboardDetailView, Legend5 as Legend, LineChart, LineHistoryCalendar, LineMonthlyHistory, LineMonthlyPdfGenerator, LinePdfExportButton, LinePdfGenerator, LineWhatsAppShareButton, LiveTimer, LoadingOverlay_default as LoadingOverlay, LoadingPage_default as LoadingPage, LoadingSpinner_default as LoadingSpinner, LoginPage, LoginView_default as LoginView, MainLayout, MetricCard_default as MetricCard, NoWorkspaceData, OptifyeAgentClient, OutputProgressChart, PageHeader, ProfileView_default as ProfileView, RegistryProvider, S3Service, SOPComplianceChart, SSEChatClient, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, ShiftDisplay_default as ShiftDisplay, ShiftsView_default as ShiftsView, SideNavBar, SingleVideoStream_default as SingleVideoStream, Skeleton, SlackAPI, SupabaseProvider, TargetWorkspaceGrid, TargetsView_default as TargetsView, ThreadSidebar, TimeDisplay_default as TimeDisplay, TimePickerDropdown, VideoCard, VideoGridView, VideoPreloader, WORKSPACE_POSITIONS, WhatsAppShareButton, WorkspaceCard, WorkspaceDetailView_default as WorkspaceDetailView, WorkspaceGrid, WorkspaceGridItem, WorkspaceHistoryCalendar, WorkspaceMetricCards, WorkspaceMonthlyDataFetcher, WorkspaceMonthlyPdfGenerator, WorkspacePdfExportButton, WorkspacePdfGenerator, WorkspaceWhatsAppShareButton, actionService, apiUtils, authCoreService, authOTPService, authRateLimitService, checkRateLimit2 as checkRateLimit, clearAllRateLimits2 as clearAllRateLimits, clearRateLimit2 as clearRateLimit, clearS3VideoCache, clearS3VideoFromCache, clearWorkspaceDisplayNamesCache, cn, createStreamProxyHandler, createSupabaseClient, createThrottledReload, dashboardService, deleteThread, forceRefreshWorkspaceDisplayNames, formatDateInZone, formatDateTimeInZone, formatISTDate, formatIdleTime, formatTimeInZone, fromUrlFriendlyName, getAllThreadMessages, getAllWorkspaceDisplayNamesAsync, getAnonClient, getCameraNumber, getCompanyMetricsTableName, getConfigurableShortWorkspaceDisplayName, getConfigurableWorkspaceDisplayName, getCurrentShift, getCurrentTimeInZone, getDashboardHeaderTimeInZone, getDaysDifferenceInZone, getDefaultCameraStreamUrl, getDefaultTabForWorkspace, getManufacturingInsights, getMetricsTablePrefix, getOperationalDate, getS3SignedUrl, getS3VideoSrc, getShortWorkspaceDisplayName, getShortWorkspaceDisplayNameAsync, getStoredWorkspaceMappings, getThreadMessages, getUserThreads, getUserThreadsPaginated, getWorkspaceDisplayName, getWorkspaceDisplayNameAsync, getWorkspaceDisplayNamesMap, getWorkspaceFromUrl, getWorkspaceNavigationParams, identifyCoreUser, initializeCoreMixpanel, isTransitionPeriod, isValidLineInfoPayload, isValidWorkspaceDetailedMetricsPayload, isValidWorkspaceMetricsPayload, isWorkspaceDisplayNamesLoaded, isWorkspaceDisplayNamesLoading, mergeWithDefaultConfig, optifyeAgentClient, preInitializeWorkspaceDisplayNames, preloadS3Video, preloadS3VideoUrl, preloadS3VideosUrl, preloadVideoUrl, preloadVideosUrl, qualityService, realtimeService, refreshWorkspaceDisplayNames, resetCoreMixpanel, s3VideoPreloader, storeWorkspaceMapping, streamProxyConfig, throttledReloadDashboard, toUrlFriendlyName, trackCoreEvent, trackCorePageView, updateThreadTitle, useActiveBreaks, useAnalyticsConfig, useAuth, useAuthConfig, useComponentOverride, useCustomConfig, useDashboardConfig, useDashboardMetrics, useDatabaseConfig, useDateFormatter, useDateTimeConfig, useEndpointsConfig, useEntityConfig, useFactoryOverviewMetrics, useFeatureFlags, useFormatNumber, useHistoricWorkspaceMetrics, useHlsStream, useHlsStreamWithCropping, useHookOverride, useLeaderboardMetrics, useLineDetailedMetrics, useLineKPIs, useLineMetrics, useLineWorkspaceMetrics, useMessages, useMetrics, useNavigation, useOverrides, usePageOverride, useRealtimeLineMetrics, useRegistry, useShiftConfig, useShifts, useSupabase, useSupabaseClient, useTargets, useTheme, useThemeConfig, useThreads, useVideoConfig, useWorkspaceConfig, useWorkspaceDetailedMetrics, useWorkspaceDisplayName, useWorkspaceDisplayNames, useWorkspaceDisplayNamesMap, useWorkspaceMetrics, useWorkspaceNavigation, useWorkspaceOperators, videoPreloader, whatsappService, withAuth, withRegistry, workspaceService };