@optifye/dashboard-core 6.5.0 → 6.5.2

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
@@ -13,7 +13,7 @@ import { noop, warning, invariant, progress, secondsToMilliseconds, milliseconds
13
13
  import { getValueTransition, hover, press, isPrimaryPointer, GroupPlaybackControls, setDragLock, supportsLinearEasing, attachTimeline, isGenerator, calcGeneratorDuration, isWaapiSupportedEasing, mapEasingToNativeEasing, maxGeneratorDuration, generateLinearEasing, isBezierDefinition } from 'motion-dom';
14
14
  import { BarChart as BarChart$1, CartesianGrid, XAxis, YAxis, Tooltip, Legend, Bar, LabelList, ResponsiveContainer, LineChart as LineChart$1, Line, PieChart, Pie, Cell, ReferenceLine, ComposedChart, Area, ScatterChart, Scatter } from 'recharts';
15
15
  import { Slot } from '@radix-ui/react-slot';
16
- import { Camera, ChevronDown, ChevronUp, Check, ShieldCheck, Star, Award, X, Coffee, Plus, ArrowLeft, Clock, Calendar, Save, Minus, ArrowDown, ArrowUp, Settings2, CheckCircle2, Search, Loader2, AlertCircle, Edit2, CheckCircle, AlertTriangle, Info, Share2, Trophy, Target, Download, User, XCircle, ChevronLeft, ChevronRight, WifiOff, Wifi, Grid3x3, List, Sun, Moon, MessageSquare, Trash2, RefreshCw, Menu, Send, Copy, UserCheck, LogOut, Package, TrendingUp, TrendingDown, Activity, Settings, LifeBuoy, EyeOff, Eye, Zap, UserCircle } from 'lucide-react';
16
+ import { Camera, ChevronDown, ChevronUp, Check, ShieldCheck, Star, Award, X, Coffee, Plus, ArrowLeft, Clock, Calendar, Save, Minus, ArrowDown, ArrowUp, Settings2, CheckCircle2, Search, Loader2, AlertCircle, Edit2, CheckCircle, AlertTriangle, Info, Share2, Trophy, Target, Download, User, XCircle, ChevronLeft, ChevronRight, Sun, Moon, MessageSquare, Trash2, RefreshCw, Menu, Send, Copy, UserCheck, LogOut, Package, TrendingUp, TrendingDown, Activity, Settings, LifeBuoy, EyeOff, Eye, Zap, UserCircle } from 'lucide-react';
17
17
  import { DayPicker, useNavigation as useNavigation$1 } from 'react-day-picker';
18
18
  import { XMarkIcon, ArrowRightIcon, HomeIcon, TrophyIcon, ChartBarIcon, AdjustmentsHorizontalIcon, ClockIcon, CubeIcon, SparklesIcon, QuestionMarkCircleIcon, HeartIcon, UserCircleIcon, ExclamationCircleIcon, EnvelopeIcon, DocumentTextIcon, ChevronUpIcon, ChevronDownIcon, Bars3Icon, CheckCircleIcon, ChatBubbleLeftRightIcon, XCircleIcon, InformationCircleIcon, ChevronLeftIcon, ChevronRightIcon } from '@heroicons/react/24/outline';
19
19
  import { CheckIcon } from '@heroicons/react/24/solid';
@@ -3256,35 +3256,6 @@ var useAudioService = () => {
3256
3256
  };
3257
3257
 
3258
3258
  // src/lib/utils/dateShiftUtils.ts
3259
- function getOperationalDate2(timezone = "Asia/Kolkata") {
3260
- const now2 = /* @__PURE__ */ new Date();
3261
- const localTime = new Date(now2.toLocaleString("en-US", { timeZone: timezone }));
3262
- const hour = localTime.getHours();
3263
- if (hour < 6) {
3264
- localTime.setDate(localTime.getDate() - 1);
3265
- }
3266
- return localTime.toISOString().split("T")[0];
3267
- }
3268
- function getCurrentShift2(timezone = "Asia/Kolkata") {
3269
- const now2 = /* @__PURE__ */ new Date();
3270
- const localTime = new Date(now2.toLocaleString("en-US", { timeZone: timezone }));
3271
- const hour = localTime.getHours();
3272
- if (hour >= 6 && hour < 18) {
3273
- return {
3274
- shiftId: 0,
3275
- shiftName: "Day Shift",
3276
- startTime: "06:00",
3277
- endTime: "18:00"
3278
- };
3279
- } else {
3280
- return {
3281
- shiftId: 1,
3282
- shiftName: "Night Shift",
3283
- startTime: "18:00",
3284
- endTime: "06:00"
3285
- };
3286
- }
3287
- }
3288
3259
  function isValidDateFormat(date) {
3289
3260
  return /^\d{4}-\d{2}-\d{2}$/.test(date);
3290
3261
  }
@@ -4151,6 +4122,23 @@ var S3ClipsAPIClient = class {
4151
4122
  };
4152
4123
  });
4153
4124
  }
4125
+ /**
4126
+ * Batch fetch multiple videos in parallel
4127
+ */
4128
+ async batchFetchVideos(workspaceId, date, shiftId, requests) {
4129
+ const batchKey = `batch:${workspaceId}:${date}:${shiftId}:${requests.length}`;
4130
+ return this.deduplicate(batchKey, async () => {
4131
+ const response = await this.fetchWithAuth("/api/clips/batch", {
4132
+ workspaceId,
4133
+ date,
4134
+ shift: shiftId.toString(),
4135
+ requests,
4136
+ sopCategories: this.sopCategories
4137
+ });
4138
+ console.log(`[S3ClipsAPIClient] Batch fetched ${response.videos.length} videos in ${response.performance.duration}ms`);
4139
+ return response.videos;
4140
+ });
4141
+ }
4154
4142
  /**
4155
4143
  * Convert S3 URI to CloudFront URL
4156
4144
  * In the API client, URLs are already signed from the server
@@ -4398,7 +4386,11 @@ var S3ClipsService = class {
4398
4386
  if (!workspaceId) {
4399
4387
  throw new Error("Valid Workspace ID is required");
4400
4388
  }
4401
- const date = inputDate || getOperationalDate2(this.config.dateTimeConfig?.defaultTimezone);
4389
+ const date = inputDate || getOperationalDate(
4390
+ this.config.dateTimeConfig?.defaultTimezone || "Asia/Kolkata",
4391
+ /* @__PURE__ */ new Date(),
4392
+ this.config.shiftConfig?.dayShift?.startTime || "06:00"
4393
+ );
4402
4394
  if (!isValidDateFormat(date)) {
4403
4395
  throw new Error("Invalid date format. Use YYYY-MM-DD.");
4404
4396
  }
@@ -4409,7 +4401,10 @@ var S3ClipsService = class {
4409
4401
  }
4410
4402
  shiftId = parseInt(shift, 10);
4411
4403
  } else {
4412
- const { shiftId: currentShiftId } = getCurrentShift2(this.config.dateTimeConfig?.defaultTimezone);
4404
+ const { shiftId: currentShiftId } = getCurrentShift(
4405
+ this.config.dateTimeConfig?.defaultTimezone || "Asia/Kolkata",
4406
+ this.config.shiftConfig
4407
+ );
4413
4408
  shiftId = currentShiftId;
4414
4409
  }
4415
4410
  console.log(`[S3ClipsService] Fetching clips for workspace ${workspaceId}`);
@@ -4427,6 +4422,23 @@ var S3ClipsService = class {
4427
4422
  );
4428
4423
  return result.videos;
4429
4424
  }
4425
+ /**
4426
+ * Batch fetch multiple videos in parallel
4427
+ */
4428
+ async batchFetchVideos(workspaceId, date, shiftId, requests) {
4429
+ try {
4430
+ const results = await this.apiClient.batchFetchVideos(
4431
+ workspaceId,
4432
+ date,
4433
+ shiftId,
4434
+ requests
4435
+ );
4436
+ return results.map((r2) => r2.video).filter((v) => v !== null);
4437
+ } catch (error) {
4438
+ console.error("[S3ClipsService] Error batch fetching videos:", error);
4439
+ return [];
4440
+ }
4441
+ }
4430
4442
  /**
4431
4443
  * Get videos page using pagination API
4432
4444
  */
@@ -8049,7 +8061,7 @@ var useActiveBreaks = (lineIds) => {
8049
8061
  }
8050
8062
  return { elapsedMinutes, remainingMinutes };
8051
8063
  };
8052
- const getCurrentShift3 = (currentMinutes, dayStart, nightStart) => {
8064
+ const getCurrentShift2 = (currentMinutes, dayStart, nightStart) => {
8053
8065
  const dayStartMinutes = parseTimeToMinutes2(dayStart);
8054
8066
  const nightStartMinutes = parseTimeToMinutes2(nightStart);
8055
8067
  if (nightStartMinutes < dayStartMinutes) {
@@ -8081,7 +8093,7 @@ var useActiveBreaks = (lineIds) => {
8081
8093
  const dayShift = dayShifts?.find((s) => s.line_id === lineId);
8082
8094
  const nightShift = nightShifts?.find((s) => s.line_id === lineId);
8083
8095
  if (!dayShift || !nightShift) continue;
8084
- const currentShift = getCurrentShift3(
8096
+ const currentShift = getCurrentShift2(
8085
8097
  currentMinutes,
8086
8098
  dayShift.start_time || "06:00",
8087
8099
  nightShift.start_time || "18:00"
@@ -11578,9 +11590,12 @@ var usePrefetchClipCounts = ({
11578
11590
  shiftStr = "0";
11579
11591
  console.log(`[usePrefetchClipCounts] No shift provided for historical date ${date}, defaulting to day shift (0)`);
11580
11592
  } else {
11581
- const currentShift = getCurrentShift2();
11593
+ const currentShift = getCurrentShift(
11594
+ dashboardConfig.dateTimeConfig?.defaultTimezone || "Asia/Kolkata",
11595
+ dashboardConfig.shiftConfig
11596
+ );
11582
11597
  shiftStr = currentShift.shiftId.toString();
11583
- console.log(`[usePrefetchClipCounts] Using current operational shift: ${shiftStr} (${currentShift.shiftName})`);
11598
+ console.log(`[usePrefetchClipCounts] Using current operational shift: ${shiftStr}`);
11584
11599
  }
11585
11600
  return {
11586
11601
  workspaceId: workspaceId || "",
@@ -26917,11 +26932,14 @@ var BottlenecksContent = ({
26917
26932
  console.log(`[BottlenecksContent] No shift provided for historical date ${date}, defaulting to day shift (0)`);
26918
26933
  return "0";
26919
26934
  } else {
26920
- const currentShift = getCurrentShift2();
26921
- console.log(`[BottlenecksContent] Using current operational shift: ${currentShift.shiftId} (${currentShift.shiftName})`);
26935
+ const currentShift = getCurrentShift(
26936
+ dashboardConfig.dateTimeConfig?.defaultTimezone || "Asia/Kolkata",
26937
+ dashboardConfig.shiftConfig
26938
+ );
26939
+ console.log(`[BottlenecksContent] Using current operational shift: ${currentShift.shiftId}`);
26922
26940
  return currentShift.shiftId.toString();
26923
26941
  }
26924
- }, [shift, date]);
26942
+ }, [shift, date, dashboardConfig]);
26925
26943
  const {
26926
26944
  data: prefetchData,
26927
26945
  isFullyIndexed,
@@ -27010,44 +27028,73 @@ var BottlenecksContent = ({
27010
27028
  if (indicesToLoad.length === 0) return;
27011
27029
  console.log(`[ensureVideosLoaded] Preloading ${indicesToLoad.length} videos around index ${centerIndex}: [${indicesToLoad.join(", ")}]`);
27012
27030
  indicesToLoad.forEach((idx) => loadingVideosRef.current.add(idx));
27013
- const loadPromises = indicesToLoad.map(async (index) => {
27014
- try {
27015
- let video = null;
27016
- if (!video) {
27017
- const operationalDate = date || getOperationalDate();
27018
- const shiftStr = effectiveShift;
27019
- video = await s3ClipsService.getClipByIndex(
27031
+ const operationalDate = date || getOperationalDate();
27032
+ const shiftStr = effectiveShift;
27033
+ console.log(`[ensureVideosLoaded] Batch fetching ${indicesToLoad.length} videos in parallel`);
27034
+ try {
27035
+ const batchRequests = indicesToLoad.map((index) => ({
27036
+ category: effectiveFilter,
27037
+ index,
27038
+ includeMetadata: false
27039
+ // No metadata during bulk preloading
27040
+ }));
27041
+ const videos = await s3ClipsService.batchFetchVideos(
27042
+ workspaceId,
27043
+ operationalDate,
27044
+ shiftStr,
27045
+ batchRequests
27046
+ );
27047
+ if (videos.length > 0 && isMountedRef.current) {
27048
+ videos.forEach((video, idx) => {
27049
+ if (video) {
27050
+ setAllVideos((prev) => {
27051
+ const exists = prev.some((v) => v.id === video.id);
27052
+ if (!exists) {
27053
+ return [...prev, video];
27054
+ }
27055
+ return prev;
27056
+ });
27057
+ const originalIndex = indicesToLoad[idx];
27058
+ loadedIndices.add(originalIndex);
27059
+ preloadVideoUrl(video.src);
27060
+ }
27061
+ });
27062
+ console.log(`[ensureVideosLoaded] Successfully loaded ${videos.length} videos in batch`);
27063
+ }
27064
+ } catch (error2) {
27065
+ console.error("[ensureVideosLoaded] Batch fetch failed:", error2);
27066
+ const loadPromises = indicesToLoad.map(async (index) => {
27067
+ try {
27068
+ const video = await s3ClipsService.getClipByIndex(
27020
27069
  workspaceId,
27021
27070
  operationalDate,
27022
27071
  shiftStr,
27023
27072
  effectiveFilter,
27024
27073
  index,
27025
27074
  true,
27026
- // includeCycleTime - OK for preloading
27075
+ // includeCycleTime
27027
27076
  false
27028
- // includeMetadata - NO metadata during bulk preloading to prevent flooding
27077
+ // includeMetadata
27029
27078
  );
27079
+ if (video && isMountedRef.current) {
27080
+ setAllVideos((prev) => {
27081
+ const exists = prev.some((v) => v.id === video.id);
27082
+ if (!exists) {
27083
+ return [...prev, video];
27084
+ }
27085
+ return prev;
27086
+ });
27087
+ loadedIndices.add(index);
27088
+ preloadVideoUrl(video.src);
27089
+ }
27090
+ } catch (err) {
27091
+ console.warn(`[ensureVideosLoaded] Failed to load video at index ${index}:`, err);
27030
27092
  }
27031
- if (video && isMountedRef.current) {
27032
- setAllVideos((prev) => {
27033
- const exists = prev.some((v) => v.id === video.id);
27034
- if (!exists) {
27035
- return [...prev, video];
27036
- }
27037
- return prev;
27038
- });
27039
- loadedIndices.add(index);
27040
- preloadVideoUrl(video.src);
27041
- }
27042
- } catch (error2) {
27043
- console.warn(`[ensureVideosLoaded] Failed to load video at index ${index}:`, error2);
27044
- } finally {
27045
- loadingVideosRef.current.delete(index);
27046
- }
27047
- });
27048
- Promise.all(loadPromises).catch((err) => {
27049
- console.warn("[ensureVideosLoaded] Some videos failed to preload:", err);
27050
- });
27093
+ });
27094
+ await Promise.all(loadPromises);
27095
+ } finally {
27096
+ indicesToLoad.forEach((idx) => loadingVideosRef.current.delete(idx));
27097
+ }
27051
27098
  }, [s3ClipsService, workspaceId, clipCounts, sopCategories, date, effectiveShift]);
27052
27099
  const loadFirstVideoForCategory = useCallback(async (category) => {
27053
27100
  if (!workspaceId || !s3ClipsService || !isMountedRef.current) return;
@@ -28633,7 +28680,6 @@ var WorkspaceHealthCard = ({
28633
28680
  };
28634
28681
  const config = getStatusConfig();
28635
28682
  const StatusIcon = config.icon;
28636
- workspace.isStale ? WifiOff : Wifi;
28637
28683
  const handleClick = () => {
28638
28684
  if (onClick) {
28639
28685
  onClick(workspace);
@@ -28681,34 +28727,13 @@ var WorkspaceHealthCard = ({
28681
28727
  /* @__PURE__ */ jsx("span", { children: config.statusText })
28682
28728
  ] })
28683
28729
  ] }),
28684
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
28685
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
28686
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
28687
- /* @__PURE__ */ jsx(Clock, { className: "h-3.5 w-3.5 text-gray-400" }),
28688
- /* @__PURE__ */ jsxs("span", { className: "text-sm text-gray-600 dark:text-gray-400 whitespace-nowrap", children: [
28689
- "Last seen: ",
28690
- /* @__PURE__ */ jsx("span", { className: "font-medium", children: formatTimeAgo(workspace.timeSinceLastUpdate) })
28691
- ] })
28692
- ] }),
28693
- workspace.isStale && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
28694
- /* @__PURE__ */ jsx(WifiOff, { className: "h-3.5 w-3.5 text-amber-500" }),
28695
- /* @__PURE__ */ jsx("span", { className: "text-amber-600 dark:text-amber-400 text-xs", children: "No recent updates" })
28696
- ] })
28697
- ] }),
28698
- config.pulse && !workspace.isStale && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
28699
- /* @__PURE__ */ jsxs("div", { className: "relative flex h-2 w-2", children: [
28700
- /* @__PURE__ */ jsx("span", { className: clsx(
28701
- "animate-ping absolute inline-flex h-full w-full rounded-full opacity-75",
28702
- workspace.status === "healthy" ? "bg-emerald-400" : "bg-amber-400"
28703
- ) }),
28704
- /* @__PURE__ */ jsx("span", { className: clsx(
28705
- "relative inline-flex rounded-full h-2 w-2",
28706
- workspace.status === "healthy" ? "bg-emerald-500" : "bg-amber-500"
28707
- ) })
28708
- ] }),
28709
- /* @__PURE__ */ jsx("span", { className: "text-xs text-gray-500 dark:text-gray-400", children: "Live" })
28730
+ /* @__PURE__ */ jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-1", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
28731
+ /* @__PURE__ */ jsx(Clock, { className: "h-3.5 w-3.5 text-gray-400" }),
28732
+ /* @__PURE__ */ jsxs("span", { className: "text-sm text-gray-600 dark:text-gray-400 whitespace-nowrap", children: [
28733
+ "Last seen: ",
28734
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: formatTimeAgo(workspace.timeSinceLastUpdate) })
28710
28735
  ] })
28711
- ] })
28736
+ ] }) }) })
28712
28737
  ] })
28713
28738
  }
28714
28739
  );
@@ -28789,16 +28814,16 @@ var CompactWorkspaceHealthCard = ({
28789
28814
  var HealthStatusGrid = ({
28790
28815
  workspaces,
28791
28816
  onWorkspaceClick,
28792
- viewMode: initialViewMode = "grid",
28793
28817
  showFilters = true,
28794
28818
  groupBy: initialGroupBy = "none",
28795
28819
  className = ""
28796
28820
  }) => {
28797
- const [viewMode, setViewMode] = useState(initialViewMode);
28798
28821
  const [searchTerm, setSearchTerm] = useState("");
28799
28822
  const [statusFilter, setStatusFilter] = useState("all");
28800
28823
  const [groupBy, setGroupBy] = useState(initialGroupBy);
28801
28824
  const [expandedGroups, setExpandedGroups] = useState(/* @__PURE__ */ new Set());
28825
+ const lastGroupByRef = useRef(initialGroupBy);
28826
+ const hasInitializedGroupsRef = useRef(false);
28802
28827
  const filteredWorkspaces = useMemo(() => {
28803
28828
  let filtered = [...workspaces];
28804
28829
  if (searchTerm) {
@@ -28839,7 +28864,17 @@ var HealthStatusGrid = ({
28839
28864
  return sortedGroups;
28840
28865
  }, [filteredWorkspaces, groupBy]);
28841
28866
  useEffect(() => {
28842
- if (groupBy !== "none") {
28867
+ if (groupBy !== lastGroupByRef.current) {
28868
+ lastGroupByRef.current = groupBy;
28869
+ hasInitializedGroupsRef.current = false;
28870
+ if (groupBy === "none") {
28871
+ setExpandedGroups(/* @__PURE__ */ new Set());
28872
+ }
28873
+ }
28874
+ }, [groupBy]);
28875
+ useEffect(() => {
28876
+ if (groupBy !== "none" && !hasInitializedGroupsRef.current && Object.keys(groupedWorkspaces).length > 0) {
28877
+ hasInitializedGroupsRef.current = true;
28843
28878
  setExpandedGroups(new Set(Object.keys(groupedWorkspaces)));
28844
28879
  }
28845
28880
  }, [groupBy, groupedWorkspaces]);
@@ -28883,13 +28918,13 @@ var HealthStatusGrid = ({
28883
28918
  ] }) }),
28884
28919
  /* @__PURE__ */ jsxs(Select, { value: statusFilter, onValueChange: (value) => setStatusFilter(value), children: [
28885
28920
  /* @__PURE__ */ jsx(SelectTrigger, { className: "w-full sm:w-[180px] bg-white border-gray-200", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "All statuses" }) }),
28886
- /* @__PURE__ */ jsxs(SelectContent, { children: [
28887
- /* @__PURE__ */ jsxs(SelectItem, { value: "all", children: [
28921
+ /* @__PURE__ */ jsxs(SelectContent, { className: "bg-white border border-gray-200 shadow-lg", children: [
28922
+ /* @__PURE__ */ jsxs(SelectItem, { value: "all", className: "text-gray-700 hover:bg-gray-50 focus:bg-gray-50 border-b border-gray-100 last:border-b-0", children: [
28888
28923
  "All (",
28889
28924
  workspaces.length,
28890
28925
  ")"
28891
28926
  ] }),
28892
- /* @__PURE__ */ jsx(SelectItem, { value: "healthy", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
28927
+ /* @__PURE__ */ jsx(SelectItem, { value: "healthy", className: "text-gray-700 hover:bg-gray-50 focus:bg-gray-50 border-b border-gray-100 last:border-b-0", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
28893
28928
  /* @__PURE__ */ jsx("div", { className: "h-2 w-2 rounded-full bg-green-500" }),
28894
28929
  /* @__PURE__ */ jsxs("span", { children: [
28895
28930
  "Healthy (",
@@ -28897,7 +28932,7 @@ var HealthStatusGrid = ({
28897
28932
  ")"
28898
28933
  ] })
28899
28934
  ] }) }),
28900
- /* @__PURE__ */ jsx(SelectItem, { value: "unhealthy", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
28935
+ /* @__PURE__ */ jsx(SelectItem, { value: "unhealthy", className: "text-gray-700 hover:bg-gray-50 focus:bg-gray-50 border-b border-gray-100 last:border-b-0", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
28901
28936
  /* @__PURE__ */ jsx("div", { className: "h-2 w-2 rounded-full bg-red-500" }),
28902
28937
  /* @__PURE__ */ jsxs("span", { children: [
28903
28938
  "Unhealthy (",
@@ -28905,57 +28940,23 @@ var HealthStatusGrid = ({
28905
28940
  ")"
28906
28941
  ] })
28907
28942
  ] }) }),
28908
- /* @__PURE__ */ jsx(SelectItem, { value: "warning", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
28943
+ /* @__PURE__ */ jsx(SelectItem, { value: "warning", className: "text-gray-700 hover:bg-gray-50 focus:bg-gray-50 border-b border-gray-100 last:border-b-0", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
28909
28944
  /* @__PURE__ */ jsx("div", { className: "h-2 w-2 rounded-full bg-yellow-500" }),
28910
28945
  /* @__PURE__ */ jsxs("span", { children: [
28911
28946
  "Warning (",
28912
28947
  statusCounts.warning,
28913
28948
  ")"
28914
28949
  ] })
28915
- ] }) }),
28916
- /* @__PURE__ */ jsx(SelectItem, { value: "unknown", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
28917
- /* @__PURE__ */ jsx("div", { className: "h-2 w-2 rounded-full bg-gray-400" }),
28918
- /* @__PURE__ */ jsxs("span", { children: [
28919
- "Unknown (",
28920
- statusCounts.unknown,
28921
- ")"
28922
- ] })
28923
28950
  ] }) })
28924
28951
  ] })
28925
28952
  ] }),
28926
28953
  /* @__PURE__ */ jsxs(Select, { value: groupBy, onValueChange: (value) => setGroupBy(value), children: [
28927
28954
  /* @__PURE__ */ jsx(SelectTrigger, { className: "w-full sm:w-[160px] bg-white border-gray-200", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Group by" }) }),
28928
- /* @__PURE__ */ jsxs(SelectContent, { children: [
28929
- /* @__PURE__ */ jsx(SelectItem, { value: "none", children: "No grouping" }),
28930
- /* @__PURE__ */ jsx(SelectItem, { value: "line", children: "Group by Line" }),
28931
- /* @__PURE__ */ jsx(SelectItem, { value: "status", children: "Group by Status" })
28955
+ /* @__PURE__ */ jsxs(SelectContent, { className: "bg-white border border-gray-200 shadow-lg", children: [
28956
+ /* @__PURE__ */ jsx(SelectItem, { value: "none", className: "text-gray-700 hover:bg-gray-50 focus:bg-gray-50 border-b border-gray-100 last:border-b-0", children: "No grouping" }),
28957
+ /* @__PURE__ */ jsx(SelectItem, { value: "line", className: "text-gray-700 hover:bg-gray-50 focus:bg-gray-50 border-b border-gray-100 last:border-b-0", children: "Group by Line" }),
28958
+ /* @__PURE__ */ jsx(SelectItem, { value: "status", className: "text-gray-700 hover:bg-gray-50 focus:bg-gray-50 border-b border-gray-100 last:border-b-0", children: "Group by Status" })
28932
28959
  ] })
28933
- ] }),
28934
- /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
28935
- /* @__PURE__ */ jsx(
28936
- "button",
28937
- {
28938
- onClick: () => setViewMode("grid"),
28939
- className: clsx(
28940
- "p-2 rounded-lg transition-colors",
28941
- viewMode === "grid" ? "bg-blue-100 dark:bg-blue-900/30 text-blue-600 dark:text-blue-400" : "text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700"
28942
- ),
28943
- "aria-label": "Grid view",
28944
- children: /* @__PURE__ */ jsx(Grid3x3, { className: "h-5 w-5" })
28945
- }
28946
- ),
28947
- /* @__PURE__ */ jsx(
28948
- "button",
28949
- {
28950
- onClick: () => setViewMode("list"),
28951
- className: clsx(
28952
- "p-2 rounded-lg transition-colors",
28953
- viewMode === "list" ? "bg-blue-100 dark:bg-blue-900/30 text-blue-600 dark:text-blue-400" : "text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700"
28954
- ),
28955
- "aria-label": "List view",
28956
- children: /* @__PURE__ */ jsx(List, { className: "h-5 w-5" })
28957
- }
28958
- )
28959
28960
  ] })
28960
28961
  ] }),
28961
28962
  /* @__PURE__ */ jsxs("div", { className: "mt-3 text-sm text-gray-500 dark:text-gray-400", children: [
@@ -28995,32 +28996,15 @@ var HealthStatusGrid = ({
28995
28996
  ]
28996
28997
  }
28997
28998
  ),
28998
- isExpanded && /* @__PURE__ */ jsx(
28999
- "div",
28999
+ isExpanded && /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4", children: groupWorkspaces.map((workspace) => /* @__PURE__ */ jsx(
29000
+ WorkspaceHealthCard,
29000
29001
  {
29001
- className: clsx(
29002
- viewMode === "grid" ? "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4" : "space-y-2"
29003
- ),
29004
- children: groupWorkspaces.map(
29005
- (workspace) => viewMode === "grid" ? /* @__PURE__ */ jsx(
29006
- WorkspaceHealthCard,
29007
- {
29008
- workspace,
29009
- onClick: onWorkspaceClick,
29010
- showDetails: true
29011
- },
29012
- workspace.workspace_id
29013
- ) : /* @__PURE__ */ jsx(
29014
- CompactWorkspaceHealthCard,
29015
- {
29016
- workspace,
29017
- onClick: onWorkspaceClick
29018
- },
29019
- workspace.workspace_id
29020
- )
29021
- )
29022
- }
29023
- )
29002
+ workspace,
29003
+ onClick: onWorkspaceClick,
29004
+ showDetails: true
29005
+ },
29006
+ workspace.workspace_id
29007
+ )) })
29024
29008
  ] }, groupName);
29025
29009
  }) }),
29026
29010
  filteredWorkspaces.length === 0 && /* @__PURE__ */ jsx("div", { className: "text-center py-12", children: /* @__PURE__ */ jsx("p", { className: "text-gray-500 dark:text-gray-400", children: searchTerm || statusFilter !== "all" ? "No workspaces found matching your filters." : "No workspaces available." }) })
@@ -37742,7 +37726,23 @@ var WorkspaceDetailView = ({
37742
37726
  "aria-label": "Navigate back to previous page"
37743
37727
  }
37744
37728
  ) }),
37745
- /* @__PURE__ */ jsx("div", { className: "absolute left-1/2 transform -translate-x-1/2", children: /* @__PURE__ */ jsx("h1", { className: "text-3xl font-semibold text-gray-900", children: formattedWorkspaceName }) }),
37729
+ /* @__PURE__ */ jsx("div", { className: "absolute left-1/2 transform -translate-x-1/2", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
37730
+ /* @__PURE__ */ jsx("h1", { className: "text-3xl font-semibold text-gray-900", children: formattedWorkspaceName }),
37731
+ workspaceHealth && /* @__PURE__ */ jsxs("div", { className: "relative flex h-2.5 w-2.5", children: [
37732
+ /* @__PURE__ */ jsx("span", { className: clsx(
37733
+ "animate-ping absolute inline-flex h-full w-full rounded-full opacity-75",
37734
+ workspaceHealth.status === "healthy" ? "bg-green-400" : "bg-red-400"
37735
+ ) }),
37736
+ /* @__PURE__ */ jsx("span", { className: clsx(
37737
+ "relative inline-flex rounded-full h-2.5 w-2.5",
37738
+ workspaceHealth.status === "healthy" ? "bg-green-500" : "bg-red-500"
37739
+ ) })
37740
+ ] })
37741
+ ] }) }),
37742
+ workspaceHealth && activeTab !== "monthly_history" && /* @__PURE__ */ jsx("div", { className: "absolute right-0 top-0 flex items-center h-8", children: /* @__PURE__ */ jsxs("span", { className: "text-xs text-gray-500", children: [
37743
+ "Last update: ",
37744
+ workspaceHealth.timeSinceLastUpdate
37745
+ ] }) }),
37746
37746
  /* @__PURE__ */ jsx("div", { className: "w-full h-8" })
37747
37747
  ] }),
37748
37748
  activeTab !== "monthly_history" && /* @__PURE__ */ jsx("div", { className: "mt-3 bg-blue-50 px-3 py-2 rounded-lg", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-4", children: [
@@ -37770,19 +37770,6 @@ var WorkspaceDetailView = ({
37770
37770
  workspace.shift_type,
37771
37771
  " Shift"
37772
37772
  ] })
37773
- ] }),
37774
- workspaceHealth && /* @__PURE__ */ jsxs(Fragment, { children: [
37775
- /* @__PURE__ */ jsx("div", { className: "w-px h-4 bg-blue-300" }),
37776
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
37777
- /* @__PURE__ */ jsx("div", { className: clsx(
37778
- "h-1.5 w-1.5 rounded-full",
37779
- workspaceHealth.status === "healthy" ? "bg-green-600" : workspaceHealth.status === "unhealthy" ? "bg-red-600" : workspaceHealth.status === "warning" ? "bg-amber-600" : "bg-gray-500"
37780
- ) }),
37781
- /* @__PURE__ */ jsxs("span", { className: "text-xs text-blue-700", children: [
37782
- "Last update: ",
37783
- workspaceHealth.timeSinceLastUpdate
37784
- ] })
37785
- ] })
37786
37773
  ] })
37787
37774
  ] }) }),
37788
37775
  /* @__PURE__ */ jsxs("div", { className: "mt-1 sm:mt-1.5 lg:mt-2 flex items-center justify-between", children: [
@@ -38326,7 +38313,6 @@ var WorkspaceHealthView = ({
38326
38313
  className = ""
38327
38314
  }) => {
38328
38315
  const router = useRouter();
38329
- const [viewMode, setViewMode] = useState("grid");
38330
38316
  const [groupBy, setGroupBy] = useState("line");
38331
38317
  const operationalDate = getOperationalDate();
38332
38318
  const currentHour = (/* @__PURE__ */ new Date()).getHours();
@@ -38491,7 +38477,7 @@ var WorkspaceHealthView = ({
38491
38477
  transition: { duration: 0.3, delay: 0.1 },
38492
38478
  className: "grid grid-cols-2 sm:grid-cols-2 md:grid-cols-5 gap-2 sm:gap-3 lg:gap-4",
38493
38479
  children: [
38494
- /* @__PURE__ */ jsxs(Card2, { className: "col-span-2 sm:col-span-2 md:col-span-2", children: [
38480
+ /* @__PURE__ */ jsxs(Card2, { className: "col-span-2 sm:col-span-2 md:col-span-2 bg-white", children: [
38495
38481
  /* @__PURE__ */ jsx(CardHeader2, { className: "pb-3", children: /* @__PURE__ */ jsx(CardTitle2, { className: "text-sm font-medium text-gray-500 dark:text-gray-400", children: "Overall System Status" }) }),
38496
38482
  /* @__PURE__ */ jsxs(CardContent2, { children: [
38497
38483
  /* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-2", children: [
@@ -38509,33 +38495,33 @@ var WorkspaceHealthView = ({
38509
38495
  ] })
38510
38496
  ] })
38511
38497
  ] }),
38512
- /* @__PURE__ */ jsxs(Card2, { children: [
38498
+ /* @__PURE__ */ jsxs(Card2, { className: "bg-white", children: [
38513
38499
  /* @__PURE__ */ jsx(CardHeader2, { className: "pb-3", children: /* @__PURE__ */ jsxs(CardTitle2, { className: "text-sm font-medium text-gray-500 dark:text-gray-400 flex items-center gap-2", children: [
38514
38500
  getStatusIcon("healthy"),
38515
38501
  "Healthy"
38516
38502
  ] }) }),
38517
38503
  /* @__PURE__ */ jsxs(CardContent2, { children: [
38518
- /* @__PURE__ */ jsx("p", { className: "text-2xl font-bold text-green-600 dark:text-green-400", children: summary.healthyWorkspaces }),
38504
+ /* @__PURE__ */ jsx("p", { className: "text-2xl font-bold text-gray-900 dark:text-gray-50", children: summary.healthyWorkspaces }),
38519
38505
  /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: "Operating normally" })
38520
38506
  ] })
38521
38507
  ] }),
38522
- /* @__PURE__ */ jsxs(Card2, { children: [
38508
+ /* @__PURE__ */ jsxs(Card2, { className: "bg-white", children: [
38523
38509
  /* @__PURE__ */ jsx(CardHeader2, { className: "pb-3", children: /* @__PURE__ */ jsxs(CardTitle2, { className: "text-sm font-medium text-gray-500 dark:text-gray-400 flex items-center gap-2", children: [
38524
38510
  getStatusIcon("warning"),
38525
38511
  "Warning"
38526
38512
  ] }) }),
38527
38513
  /* @__PURE__ */ jsxs(CardContent2, { children: [
38528
- /* @__PURE__ */ jsx("p", { className: "text-2xl font-bold text-yellow-600 dark:text-yellow-400", children: summary.warningWorkspaces }),
38514
+ /* @__PURE__ */ jsx("p", { className: "text-2xl font-bold text-gray-900 dark:text-gray-50", children: summary.warningWorkspaces }),
38529
38515
  /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: "Delayed updates" })
38530
38516
  ] })
38531
38517
  ] }),
38532
- /* @__PURE__ */ jsxs(Card2, { children: [
38518
+ /* @__PURE__ */ jsxs(Card2, { className: "bg-white", children: [
38533
38519
  /* @__PURE__ */ jsx(CardHeader2, { className: "pb-3", children: /* @__PURE__ */ jsxs(CardTitle2, { className: "text-sm font-medium text-gray-500 dark:text-gray-400 flex items-center gap-2", children: [
38534
38520
  getStatusIcon("unhealthy"),
38535
38521
  "Unhealthy"
38536
38522
  ] }) }),
38537
38523
  /* @__PURE__ */ jsxs(CardContent2, { children: [
38538
- /* @__PURE__ */ jsx("p", { className: "text-2xl font-bold text-red-600 dark:text-red-400", children: summary.unhealthyWorkspaces }),
38524
+ /* @__PURE__ */ jsx("p", { className: "text-2xl font-bold text-gray-900 dark:text-gray-50", children: summary.unhealthyWorkspaces }),
38539
38525
  /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: "Requires attention" })
38540
38526
  ] })
38541
38527
  ] })
@@ -38553,7 +38539,6 @@ var WorkspaceHealthView = ({
38553
38539
  {
38554
38540
  workspaces,
38555
38541
  onWorkspaceClick: handleWorkspaceClick,
38556
- viewMode,
38557
38542
  showFilters: true,
38558
38543
  groupBy
38559
38544
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optifye/dashboard-core",
3
- "version": "6.5.0",
3
+ "version": "6.5.2",
4
4
  "description": "Reusable UI & logic for Optifye dashboard",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",