@optifye/dashboard-core 6.11.7 → 6.11.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.css CHANGED
@@ -565,9 +565,6 @@ body {
565
565
  .right-1\/2 {
566
566
  right: 50%;
567
567
  }
568
- .right-16 {
569
- right: 4rem;
570
- }
571
568
  .right-2 {
572
569
  right: 0.5rem;
573
570
  }
@@ -1110,18 +1107,12 @@ body {
1110
1107
  .h-\[calc\(100\%-3rem\)\] {
1111
1108
  height: calc(100% - 3rem);
1112
1109
  }
1113
- .h-\[calc\(100\%-8rem\)\] {
1114
- height: calc(100% - 8rem);
1115
- }
1116
1110
  .h-\[calc\(100vh-100px\)\] {
1117
1111
  height: calc(100vh - 100px);
1118
1112
  }
1119
1113
  .h-\[calc\(100vh-10rem\)\] {
1120
1114
  height: calc(100vh - 10rem);
1121
1115
  }
1122
- .h-\[calc\(100vh-12rem\)\] {
1123
- height: calc(100vh - 12rem);
1124
- }
1125
1116
  .h-\[calc\(100vh-64px\)\] {
1126
1117
  height: calc(100vh - 64px);
1127
1118
  }
@@ -1230,6 +1221,9 @@ body {
1230
1221
  .min-h-\[300px\] {
1231
1222
  min-height: 300px;
1232
1223
  }
1224
+ .min-h-\[320px\] {
1225
+ min-height: 320px;
1226
+ }
1233
1227
  .min-h-\[400px\] {
1234
1228
  min-height: 400px;
1235
1229
  }
@@ -1245,6 +1239,9 @@ body {
1245
1239
  .min-h-\[80px\] {
1246
1240
  min-height: 80px;
1247
1241
  }
1242
+ .min-h-\[calc\(100dvh-12rem\)\] {
1243
+ min-height: calc(100dvh - 12rem);
1244
+ }
1248
1245
  .min-h-\[calc\(100vh-100px\)\] {
1249
1246
  min-height: calc(100vh - 100px);
1250
1247
  }
@@ -1380,15 +1377,9 @@ body {
1380
1377
  .w-\[22vw\] {
1381
1378
  width: 22vw;
1382
1379
  }
1383
- .w-\[240px\] {
1384
- width: 240px;
1385
- }
1386
1380
  .w-\[27vw\] {
1387
1381
  width: 27vw;
1388
1382
  }
1389
- .w-\[280px\] {
1390
- width: 280px;
1391
- }
1392
1383
  .w-\[340px\] {
1393
1384
  width: 340px;
1394
1385
  }
@@ -1404,6 +1395,12 @@ body {
1404
1395
  .w-\[520px\] {
1405
1396
  width: 520px;
1406
1397
  }
1398
+ .w-\[min\(88vw\,240px\)\] {
1399
+ width: min(88vw, 240px);
1400
+ }
1401
+ .w-\[min\(92vw\,280px\)\] {
1402
+ width: min(92vw, 280px);
1403
+ }
1407
1404
  .w-auto {
1408
1405
  width: auto;
1409
1406
  }
@@ -5548,6 +5545,9 @@ input[type=range]:active::-moz-range-thumb {
5548
5545
  --tw-bg-opacity: 1;
5549
5546
  background-color: rgb(30 41 59 / var(--tw-bg-opacity, 1));
5550
5547
  }
5548
+ .hover\:bg-transparent:hover {
5549
+ background-color: transparent;
5550
+ }
5551
5551
  .hover\:bg-white:hover {
5552
5552
  --tw-bg-opacity: 1;
5553
5553
  background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1));
@@ -5984,6 +5984,9 @@ input[type=range]:active::-moz-range-thumb {
5984
5984
  --tw-bg-opacity: 1;
5985
5985
  background-color: rgb(96 165 250 / var(--tw-bg-opacity, 1));
5986
5986
  }
5987
+ .disabled\:opacity-40:disabled {
5988
+ opacity: 0.4;
5989
+ }
5987
5990
  .disabled\:opacity-50:disabled {
5988
5991
  opacity: 0.5;
5989
5992
  }
@@ -6152,6 +6155,9 @@ input[type=range]:active::-moz-range-thumb {
6152
6155
  .sm\:right-0 {
6153
6156
  right: 0px;
6154
6157
  }
6158
+ .sm\:right-16 {
6159
+ right: 4rem;
6160
+ }
6155
6161
  .sm\:right-3 {
6156
6162
  right: 0.75rem;
6157
6163
  }
@@ -6309,6 +6315,9 @@ input[type=range]:active::-moz-range-thumb {
6309
6315
  .sm\:min-h-\[300px\] {
6310
6316
  min-height: 300px;
6311
6317
  }
6318
+ .sm\:min-h-\[320px\] {
6319
+ min-height: 320px;
6320
+ }
6312
6321
  .sm\:w-12 {
6313
6322
  width: 3rem;
6314
6323
  }
@@ -7025,6 +7034,9 @@ input[type=range]:active::-moz-range-thumb {
7025
7034
  .lg\:hidden {
7026
7035
  display: none;
7027
7036
  }
7037
+ .lg\:aspect-auto {
7038
+ aspect-ratio: auto;
7039
+ }
7028
7040
  .lg\:h-14 {
7029
7041
  height: 3.5rem;
7030
7042
  }
@@ -7046,6 +7058,9 @@ input[type=range]:active::-moz-range-thumb {
7046
7058
  .lg\:h-\[220px\] {
7047
7059
  height: 220px;
7048
7060
  }
7061
+ .lg\:h-\[calc\(100dvh-12rem\)\] {
7062
+ height: calc(100dvh - 12rem);
7063
+ }
7049
7064
  .lg\:h-\[calc\(100vh-12rem\)\] {
7050
7065
  height: calc(100vh - 12rem);
7051
7066
  }
@@ -7100,6 +7115,9 @@ input[type=range]:active::-moz-range-thumb {
7100
7115
  .lg\:flex-\[3\] {
7101
7116
  flex: 3;
7102
7117
  }
7118
+ .lg\:flex-shrink-0 {
7119
+ flex-shrink: 0;
7120
+ }
7103
7121
  .lg\:grid-cols-1 {
7104
7122
  grid-template-columns: repeat(1, minmax(0, 1fr));
7105
7123
  }
package/dist/index.d.mts CHANGED
@@ -1305,6 +1305,7 @@ interface BottleneckVideoData {
1305
1305
  description: string;
1306
1306
  type: VideoType;
1307
1307
  originalUri: string;
1308
+ capture_fps?: number;
1308
1309
  cycle_time_seconds?: number;
1309
1310
  creation_timestamp?: string;
1310
1311
  percentile?: number;
@@ -1339,6 +1340,10 @@ interface VideoMetadata {
1339
1340
  camera_id?: string;
1340
1341
  workspace_id?: string;
1341
1342
  cycle_time?: number;
1343
+ playlist?: {
1344
+ fps?: number | string;
1345
+ [key: string]: any;
1346
+ };
1342
1347
  sop_name?: string;
1343
1348
  violation_start_frame?: number;
1344
1349
  violation_end_frame?: number;
package/dist/index.d.ts CHANGED
@@ -1305,6 +1305,7 @@ interface BottleneckVideoData {
1305
1305
  description: string;
1306
1306
  type: VideoType;
1307
1307
  originalUri: string;
1308
+ capture_fps?: number;
1308
1309
  cycle_time_seconds?: number;
1309
1310
  creation_timestamp?: string;
1310
1311
  percentile?: number;
@@ -1339,6 +1340,10 @@ interface VideoMetadata {
1339
1340
  camera_id?: string;
1340
1341
  workspace_id?: string;
1341
1342
  cycle_time?: number;
1343
+ playlist?: {
1344
+ fps?: number | string;
1345
+ [key: string]: any;
1346
+ };
1342
1347
  sop_name?: string;
1343
1348
  violation_start_frame?: number;
1344
1349
  violation_end_frame?: number;
package/dist/index.js CHANGED
@@ -7438,6 +7438,16 @@ var getClipCycleTimeFrames = (metadata) => {
7438
7438
  }
7439
7439
  return parseFiniteNumber(metadata?.request?.metadata?.cycle_time);
7440
7440
  };
7441
+ var getClipCaptureFps = (clip) => {
7442
+ return parseFiniteNumber(clip?.capture_fps) ?? parseFiniteNumber(clip?.metadata?.playlist?.fps) ?? 20;
7443
+ };
7444
+ var getClipCycleTimeSeconds = (clip) => {
7445
+ const cycleTimeFrames = getClipCycleTimeFrames(clip?.metadata);
7446
+ if (cycleTimeFrames === void 0) {
7447
+ return void 0;
7448
+ }
7449
+ return cycleTimeFrames / getClipCaptureFps(clip);
7450
+ };
7441
7451
  var getSupabaseClient = () => {
7442
7452
  const existing = _getSupabaseInstanceOptional();
7443
7453
  if (existing) {
@@ -7950,10 +7960,10 @@ var S3ClipsSupabaseService = class {
7950
7960
  const transformedClips = (response.clips || []).map((clip) => {
7951
7961
  const clipId = clip.id ?? clip.clip_id;
7952
7962
  const clipType = clip.type ?? clip.clip_type_name;
7953
- const cycleTimeSeconds = parseFiniteNumber(clip.cycle_time_seconds) ?? (() => {
7954
- const cycleTimeFrames = getClipCycleTimeFrames(clip.metadata);
7955
- return cycleTimeFrames !== void 0 ? cycleTimeFrames / (clip.metadata?.playlist?.fps || 20) : void 0;
7956
- })();
7963
+ const cycleTimeSeconds = parseFiniteNumber(clip.cycle_time_seconds) ?? getClipCycleTimeSeconds({
7964
+ capture_fps: clip.capture_fps,
7965
+ metadata: clip.metadata
7966
+ });
7957
7967
  return {
7958
7968
  id: clipId,
7959
7969
  src: clip.src ?? clip.playlist,
@@ -39503,7 +39513,7 @@ var FileManagerFilters = ({
39503
39513
  ] }) })
39504
39514
  ] }, node.id);
39505
39515
  };
39506
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `relative bg-white rounded-2xl shadow-lg border border-gray-100 h-full hover:shadow-xl transition-all duration-300 ease-out backdrop-blur-sm ${className}`, children: [
39516
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `relative flex h-full min-h-[320px] flex-col bg-white rounded-2xl shadow-lg border border-gray-100 hover:shadow-xl transition-all duration-300 ease-out backdrop-blur-sm ${className}`, children: [
39507
39517
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4 border-b border-gray-50 bg-gradient-to-br from-slate-50/80 via-white to-blue-50/30", children: [
39508
39518
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
39509
39519
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", children: [
@@ -39585,7 +39595,7 @@ var FileManagerFilters = ({
39585
39595
  /* @__PURE__ */ jsxRuntime.jsxs(
39586
39596
  "div",
39587
39597
  {
39588
- className: "absolute top-14 right-16 z-50 bg-white rounded-xl shadow-xl border border-slate-200 w-[240px] animate-in slide-in-from-top-2 duration-200",
39598
+ className: "absolute top-14 right-2 z-50 bg-white rounded-xl shadow-xl border border-slate-200 w-[min(88vw,240px)] sm:right-16 animate-in slide-in-from-top-2 duration-200",
39589
39599
  onClick: (e) => e.stopPropagation(),
39590
39600
  children: [
39591
39601
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-4 py-3 flex items-center justify-between border-b border-slate-200", children: [
@@ -39642,7 +39652,7 @@ var FileManagerFilters = ({
39642
39652
  /* @__PURE__ */ jsxRuntime.jsxs(
39643
39653
  "div",
39644
39654
  {
39645
- className: "absolute top-14 right-4 z-50 bg-white rounded-xl shadow-xl border border-slate-200 w-[280px] animate-in slide-in-from-top-2 duration-200",
39655
+ className: "absolute top-14 right-2 z-50 bg-white rounded-xl shadow-xl border border-slate-200 w-[min(92vw,280px)] sm:right-4 animate-in slide-in-from-top-2 duration-200",
39646
39656
  onClick: (e) => e.stopPropagation(),
39647
39657
  children: [
39648
39658
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-4 py-3 flex items-center justify-between border-b border-slate-200", children: [
@@ -39759,7 +39769,7 @@ var FileManagerFilters = ({
39759
39769
  }
39760
39770
  )
39761
39771
  ] }),
39762
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-4 py-3 h-[calc(100%-8rem)] overflow-y-auto scrollbar-thin", children: [
39772
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-4 py-3 flex-1 min-h-0 overflow-y-auto scrollbar-thin", children: [
39763
39773
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: filterTree.map((node) => renderNode(node)) }),
39764
39774
  filterTree.length === 0 && isTimeFilterActive && (startTime || endTime) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center py-12", children: [
39765
39775
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-slate-300 mb-4", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "h-12 w-12 mx-auto" }) }),
@@ -41870,17 +41880,17 @@ var BottlenecksContent = ({
41870
41880
  }
41871
41881
  }, [workspaceTargetCycleTime]);
41872
41882
  if (!dashboardConfig?.s3Config) {
41873
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-grow p-4 flex flex-col items-center justify-center h-[calc(100vh-12rem)] text-center", children: [
41883
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-grow p-4 flex flex-col items-center justify-center min-h-[calc(100dvh-12rem)] text-center", children: [
41874
41884
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.XCircle, { className: "w-12 h-12 text-red-400 mb-3" }),
41875
41885
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-red-700 mb-1", children: "S3 Configuration Missing" }),
41876
41886
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-600 max-w-md", children: "S3 configuration is required to load video clips. Please check your dashboard configuration." })
41877
41887
  ] });
41878
41888
  }
41879
41889
  if (clipTypesLoading && allVideos.length === 0 && Object.keys(mergedCounts).length === 0) {
41880
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-grow p-4 flex items-center justify-center h-[calc(100vh-12rem)]", children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "lg", message: "Loading clips..." }) });
41890
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-grow p-4 flex items-center justify-center min-h-[calc(100dvh-12rem)]", children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "lg", message: "Loading clips..." }) });
41881
41891
  }
41882
41892
  if (error && error.type === "fatal" && !hasInitialLoad || clipTypesError) {
41883
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-grow p-4 flex flex-col items-center justify-center h-[calc(100vh-12rem)] text-center", children: [
41893
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-grow p-4 flex flex-col items-center justify-center min-h-[calc(100dvh-12rem)] text-center", children: [
41884
41894
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.XCircle, { className: "w-12 h-12 text-red-400 mb-3" }),
41885
41895
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-red-700 mb-1", children: "Error Loading Clips" }),
41886
41896
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-600 max-w-md", children: error?.message || clipTypesError })
@@ -41895,7 +41905,7 @@ var BottlenecksContent = ({
41895
41905
  clipTypesError,
41896
41906
  mergedCounts
41897
41907
  });
41898
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-grow p-1.5 sm:p-2 lg:p-4 h-[calc(100vh-12rem)] relative", children: [
41908
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-grow p-1.5 sm:p-2 lg:p-4 min-h-[calc(100dvh-12rem)] lg:h-[calc(100dvh-12rem)] relative", children: [
41899
41909
  hasNewClips && newClipsNotification && /* @__PURE__ */ jsxRuntime.jsx(
41900
41910
  NewClipsNotification,
41901
41911
  {
@@ -41904,8 +41914,8 @@ var BottlenecksContent = ({
41904
41914
  onDismiss: clearNotification
41905
41915
  }
41906
41916
  ),
41907
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col lg:flex-row gap-4 h-full", children: [
41908
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-w-0 lg:flex-[3]", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-white rounded-lg shadow-sm overflow-hidden h-full", children: filteredVideos.length > 0 && currentVideo && !isFullscreen ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 h-full", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative h-full group", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full h-full overflow-hidden rounded-md shadow-inner bg-gray-900", children: [
41917
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4 lg:flex-row lg:h-full", children: [
41918
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-0 w-full lg:flex-[3] lg:h-full", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-white rounded-lg shadow-sm overflow-hidden lg:h-full", children: filteredVideos.length > 0 && currentVideo && !isFullscreen ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 lg:h-full", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative group lg:h-full", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full aspect-video lg:aspect-auto lg:h-full overflow-hidden rounded-md shadow-inner bg-gray-900", children: [
41909
41919
  /* @__PURE__ */ jsxRuntime.jsx(
41910
41920
  "div",
41911
41921
  {
@@ -42055,23 +42065,23 @@ var BottlenecksContent = ({
42055
42065
  )
42056
42066
  ] }) }) }) : (
42057
42067
  /* Priority 5: Show "no clips found" only if we have counts and there are truly no clips for workspace */
42058
- hasInitialLoad && Object.keys(mergedCounts).length > 0 && Object.values(mergedCounts).every((count) => count === 0) ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center h-full", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center p-8", children: [
42068
+ hasInitialLoad && Object.keys(mergedCounts).length > 0 && Object.values(mergedCounts).every((count) => count === 0) ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center min-h-[220px] sm:min-h-[320px] lg:h-full", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center p-8", children: [
42059
42069
  /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-16 h-16 text-gray-300 mx-auto mb-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z" }) }),
42060
42070
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-xl font-medium text-gray-700 mb-2", children: "No Clips Found" }),
42061
42071
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-500", children: "There were no video clips found for this workspace today." })
42062
42072
  ] }) }) : (
42063
42073
  /* Priority 6: Show "no matching clips" only if we have data loaded and specifically no clips for this filter */
42064
- hasInitialLoad && (mergedCounts[activeFilter] || 0) === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center h-full", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center p-8", children: [
42074
+ hasInitialLoad && (mergedCounts[activeFilter] || 0) === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center min-h-[220px] sm:min-h-[320px] lg:h-full", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center p-8", children: [
42065
42075
  /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-16 h-16 text-gray-300 mx-auto mb-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z" }) }),
42066
42076
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-xl font-medium text-gray-700 mb-2", children: "No Matching Clips" }),
42067
42077
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-500", children: "There are no clips matching the selected filter." })
42068
42078
  ] }) }) : (
42069
42079
  /* Priority 7: Default loading state for any other case */
42070
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 h-full", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative h-full", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative w-full h-full overflow-hidden rounded-md shadow-inner bg-gray-900 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading..." }) }) }) })
42080
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 lg:h-full", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative lg:h-full", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative w-full min-h-[220px] sm:min-h-[320px] lg:min-h-0 lg:h-full overflow-hidden rounded-md shadow-inner bg-gray-900 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading..." }) }) }) })
42071
42081
  )
42072
42082
  )
42073
42083
  ) }) }),
42074
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0 lg:flex-[1] lg:min-w-[280px] lg:max-w-[320px]", children: triageMode ? (
42084
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full lg:flex-shrink-0 lg:flex-[1] lg:min-w-[280px] lg:max-w-[320px] lg:h-full", children: triageMode ? (
42075
42085
  /* Triage Mode - Direct tile view for cycle completions and idle time */
42076
42086
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-lg shadow-sm h-full overflow-hidden flex flex-col", children: [
42077
42087
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-4 py-3 border-b border-gray-100", children: [
@@ -74657,33 +74667,51 @@ var OverviewImprovementsSkeleton = () => /* @__PURE__ */ jsxRuntime.jsx("div", {
74657
74667
  var OperationsOverviewHeader = React141__namespace.default.memo(({
74658
74668
  dateRange,
74659
74669
  trendMode,
74670
+ lineOptions,
74671
+ selectedLineIds,
74660
74672
  appTimezone,
74661
74673
  mobileMenuContext,
74662
74674
  onDateRangeChange,
74663
- onTrendModeChange
74675
+ onTrendModeChange,
74676
+ onSelectedLineIdsChange
74664
74677
  }) => {
74665
74678
  bumpRenderCounter();
74666
74679
  const [isFilterOpen, setIsFilterOpen] = React141__namespace.default.useState(false);
74680
+ const [isLinesDropdownOpen, setIsLinesDropdownOpen] = React141__namespace.default.useState(false);
74667
74681
  const filterRef = React141__namespace.default.useRef(null);
74668
74682
  const filterButtonRef = React141__namespace.default.useRef(null);
74669
74683
  const mobileFilterButtonRef = React141__namespace.default.useRef(null);
74670
- const currentWeekRange = React141__namespace.default.useMemo(
74671
- () => getCurrentWeekToDateRange(appTimezone),
74672
- [appTimezone]
74673
- );
74674
- const isCurrentWeekToDateRange = dateRange.startKey === currentWeekRange.startKey && dateRange.endKey === currentWeekRange.endKey;
74684
+ const linesDropdownRef = React141__namespace.default.useRef(null);
74675
74685
  const mobileSubtitle = React141__namespace.default.useMemo(() => {
74676
- if (isCurrentWeekToDateRange) {
74677
- return `Week of ${dateFns.format(parseDateKeyToDate(dateRange.startKey), "do MMM")}`;
74686
+ if (dateRange.startKey === dateRange.endKey) {
74687
+ return dateFns.format(parseDateKeyToDate(dateRange.startKey), "do MMM, yyyy");
74678
74688
  }
74679
74689
  return `${dateFns.format(parseDateKeyToDate(dateRange.startKey), "do MMM")} - ${dateFns.format(parseDateKeyToDate(dateRange.endKey), "do MMM, yyyy")}`;
74680
- }, [dateRange.endKey, dateRange.startKey, isCurrentWeekToDateRange]);
74690
+ }, [dateRange.endKey, dateRange.startKey]);
74681
74691
  const desktopSubtitle = React141__namespace.default.useMemo(() => {
74682
- if (isCurrentWeekToDateRange) {
74683
- return `Week of ${dateFns.format(parseDateKeyToDate(dateRange.startKey), "do MMMM, yyyy")}`;
74692
+ if (dateRange.startKey === dateRange.endKey) {
74693
+ return dateFns.format(parseDateKeyToDate(dateRange.startKey), "do MMMM, yyyy");
74684
74694
  }
74685
74695
  return `${dateFns.format(parseDateKeyToDate(dateRange.startKey), "do MMMM, yyyy")} - ${dateFns.format(parseDateKeyToDate(dateRange.endKey), "do MMMM, yyyy")}`;
74686
- }, [dateRange.endKey, dateRange.startKey, isCurrentWeekToDateRange]);
74696
+ }, [dateRange.endKey, dateRange.startKey]);
74697
+ const availableLineIds = React141__namespace.default.useMemo(
74698
+ () => lineOptions.map((line) => line.id),
74699
+ [lineOptions]
74700
+ );
74701
+ const selectedLineIdSet = React141__namespace.default.useMemo(
74702
+ () => new Set(selectedLineIds),
74703
+ [selectedLineIds]
74704
+ );
74705
+ const isAllLinesSelected = React141__namespace.default.useMemo(() => {
74706
+ if (availableLineIds.length === 0) return true;
74707
+ return availableLineIds.every((lineId) => selectedLineIdSet.has(lineId));
74708
+ }, [availableLineIds, selectedLineIdSet]);
74709
+ const activeFilterCount = React141__namespace.default.useMemo(() => {
74710
+ let count = 0;
74711
+ if (trendMode !== "all") count += 1;
74712
+ if (!isAllLinesSelected) count += 1;
74713
+ return count;
74714
+ }, [isAllLinesSelected, trendMode]);
74687
74715
  const handleFilterToggle = React141__namespace.default.useCallback(() => {
74688
74716
  trackCoreEvent("Operations Overview Filter Toggled", {
74689
74717
  action: !isFilterOpen ? "open" : "close"
@@ -74697,10 +74725,43 @@ var OperationsOverviewHeader = React141__namespace.default.memo(({
74697
74725
  });
74698
74726
  onTrendModeChange(nextMode);
74699
74727
  }, [onTrendModeChange]);
74728
+ const handleAllLinesToggle = React141__namespace.default.useCallback(() => {
74729
+ trackCoreEvent("Operations Overview Line Filter Changed", {
74730
+ selected_line_ids: availableLineIds,
74731
+ selected_line_count: availableLineIds.length,
74732
+ mode: "all"
74733
+ });
74734
+ onSelectedLineIdsChange(availableLineIds);
74735
+ }, [availableLineIds, onSelectedLineIdsChange]);
74736
+ const handleLineToggle = React141__namespace.default.useCallback((lineId) => {
74737
+ const current = new Set(selectedLineIds);
74738
+ if (current.has(lineId)) {
74739
+ if (current.size <= 1) return;
74740
+ current.delete(lineId);
74741
+ } else {
74742
+ current.add(lineId);
74743
+ }
74744
+ const next = availableLineIds.filter((id3) => current.has(id3));
74745
+ trackCoreEvent("Operations Overview Line Filter Changed", {
74746
+ selected_line_ids: next,
74747
+ selected_line_count: next.length,
74748
+ mode: "custom"
74749
+ });
74750
+ onSelectedLineIdsChange(next);
74751
+ }, [availableLineIds, onSelectedLineIdsChange, selectedLineIds]);
74752
+ const handleClearAllFilters = React141__namespace.default.useCallback(() => {
74753
+ onTrendModeChange("all");
74754
+ onSelectedLineIdsChange(availableLineIds);
74755
+ setIsFilterOpen(false);
74756
+ }, [availableLineIds, onSelectedLineIdsChange, onTrendModeChange]);
74700
74757
  React141__namespace.default.useEffect(() => {
74701
74758
  const handleClickOutside = (event) => {
74702
- if (filterRef.current && !filterRef.current.contains(event.target) && filterButtonRef.current && !filterButtonRef.current.contains(event.target) && mobileFilterButtonRef.current && !mobileFilterButtonRef.current.contains(event.target)) {
74759
+ const target = event.target;
74760
+ if (filterRef.current && !filterRef.current.contains(target) && filterButtonRef.current && !filterButtonRef.current.contains(target) && mobileFilterButtonRef.current && !mobileFilterButtonRef.current.contains(target)) {
74703
74761
  setIsFilterOpen(false);
74762
+ setIsLinesDropdownOpen(false);
74763
+ } else if (linesDropdownRef.current && !linesDropdownRef.current.contains(target)) {
74764
+ setIsLinesDropdownOpen(false);
74704
74765
  }
74705
74766
  };
74706
74767
  document.addEventListener("mousedown", handleClickOutside);
@@ -74713,7 +74774,8 @@ var OperationsOverviewHeader = React141__namespace.default.memo(({
74713
74774
  mobileMenuContext ? /* @__PURE__ */ jsxRuntime.jsx(
74714
74775
  HamburgerButton,
74715
74776
  {
74716
- onClick: mobileMenuContext.onMobileMenuOpen,
74777
+ onClick: mobileMenuContext.onMobileMenuOpen || (() => {
74778
+ }),
74717
74779
  className: "flex-shrink-0 -ml-1"
74718
74780
  }
74719
74781
  ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-8 flex-shrink-0" }),
@@ -74738,11 +74800,11 @@ var OperationsOverviewHeader = React141__namespace.default.memo(({
74738
74800
  {
74739
74801
  ref: mobileFilterButtonRef,
74740
74802
  onClick: handleFilterToggle,
74741
- className: `p-2 rounded-full transition-colors relative ${isFilterOpen || trendMode !== "all" ? "bg-blue-50" : "active:bg-gray-100"}`,
74803
+ className: `p-2 rounded-full transition-colors relative ${isFilterOpen || activeFilterCount > 0 ? "bg-blue-50" : "active:bg-gray-100"}`,
74742
74804
  "aria-label": "Open filters",
74743
74805
  children: [
74744
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Filter, { className: `w-5 h-5 ${trendMode !== "all" ? "text-blue-600" : "text-gray-700"}` }),
74745
- trendMode !== "all" ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute -top-1 -right-1 flex items-center justify-center w-4 h-4 bg-blue-600 text-white text-[10px] rounded-full font-bold", children: "1" }) : null
74806
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Filter, { className: `w-5 h-5 ${activeFilterCount > 0 ? "text-blue-600" : "text-gray-700"}` }),
74807
+ activeFilterCount > 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute -top-1 -right-1 flex items-center justify-center w-4 h-4 bg-blue-600 text-white text-[10px] rounded-full font-bold", children: activeFilterCount }) : null
74746
74808
  ]
74747
74809
  }
74748
74810
  )
@@ -74770,10 +74832,10 @@ var OperationsOverviewHeader = React141__namespace.default.memo(({
74770
74832
  {
74771
74833
  ref: filterButtonRef,
74772
74834
  onClick: handleFilterToggle,
74773
- className: `flex items-center gap-2 px-3 py-1.5 rounded-lg border text-sm font-medium transition-all shadow-sm ${isFilterOpen || trendMode !== "all" ? "border-blue-500 bg-blue-50 text-blue-700 ring-1 ring-blue-500" : "border-slate-200 bg-white text-slate-700 hover:bg-slate-50"}`,
74835
+ className: `flex items-center gap-2 px-3 py-1.5 rounded-lg border text-sm font-medium transition-all shadow-sm ${isFilterOpen || activeFilterCount > 0 ? "border-blue-500 bg-blue-50 text-blue-700 ring-1 ring-blue-500" : "border-slate-200 bg-white text-slate-700 hover:bg-slate-50"}`,
74774
74836
  "aria-label": "Open filters",
74775
74837
  children: [
74776
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Filter, { className: `w-[18px] h-[18px] ${trendMode !== "all" ? "text-blue-600" : "text-slate-500"}` }),
74838
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Filter, { className: `w-[18px] h-[18px] ${activeFilterCount > 0 ? "text-blue-600" : "text-slate-500"}` }),
74777
74839
  "Filters",
74778
74840
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: `w-4 h-4 ml-0.5 transition-transform duration-200 ${isFilterOpen ? "rotate-180" : ""}` })
74779
74841
  ]
@@ -74784,40 +74846,94 @@ var OperationsOverviewHeader = React141__namespace.default.memo(({
74784
74846
  isFilterOpen ? /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: filterRef, className: "absolute right-3 sm:right-4 md:right-5 lg:right-6 top-full mt-2 w-72 bg-white rounded-xl shadow-xl border border-gray-100 p-4 z-50", children: [
74785
74847
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-3", children: [
74786
74848
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-semibold text-gray-900", children: "Filter View" }),
74787
- trendMode !== "all" ? /* @__PURE__ */ jsxRuntime.jsx(
74849
+ activeFilterCount > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
74788
74850
  "button",
74789
74851
  {
74790
- onClick: () => {
74791
- onTrendModeChange("all");
74792
- setIsFilterOpen(false);
74793
- },
74852
+ onClick: handleClearAllFilters,
74794
74853
  className: "text-xs text-red-600 hover:text-red-700 font-medium",
74795
74854
  children: "Clear all"
74796
74855
  }
74797
74856
  ) : null
74798
74857
  ] }),
74799
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-3", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
74800
- /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-xs font-medium text-gray-500 uppercase tracking-wide ml-1", children: "Shift" }),
74801
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative", children: /* @__PURE__ */ jsxRuntime.jsxs(
74802
- "select",
74803
- {
74804
- value: trendMode,
74805
- onChange: handleTrendModeChange,
74806
- className: "w-full appearance-none pl-3 pr-8 py-2 text-sm bg-gray-50 border border-gray-200 hover:border-gray-300 rounded-lg text-gray-900 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all cursor-pointer",
74807
- style: {
74808
- backgroundImage: `url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e")`,
74809
- backgroundPosition: "right 0.75rem center",
74810
- backgroundRepeat: "no-repeat",
74811
- backgroundSize: "1.2em 1.2em"
74812
- },
74813
- children: [
74814
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "all", children: "All Shifts" }),
74815
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "day", children: "Day Shift" }),
74816
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "night", children: "Night Shift" })
74817
- ]
74818
- }
74819
- ) })
74820
- ] }) })
74858
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
74859
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
74860
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-xs font-medium text-gray-500 uppercase tracking-wide ml-1", children: "Shift" }),
74861
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative", children: /* @__PURE__ */ jsxRuntime.jsxs(
74862
+ "select",
74863
+ {
74864
+ value: trendMode,
74865
+ onChange: handleTrendModeChange,
74866
+ className: "w-full appearance-none pl-3 pr-8 py-2 text-sm bg-gray-50 border border-gray-200 hover:border-gray-300 rounded-lg text-gray-900 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all cursor-pointer",
74867
+ style: {
74868
+ backgroundImage: `url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e")`,
74869
+ backgroundPosition: "right 0.75rem center",
74870
+ backgroundRepeat: "no-repeat",
74871
+ backgroundSize: "1.2em 1.2em"
74872
+ },
74873
+ children: [
74874
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "all", children: "All Shifts" }),
74875
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "day", children: "Day Shift" }),
74876
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "night", children: "Night Shift" })
74877
+ ]
74878
+ }
74879
+ ) })
74880
+ ] }),
74881
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", ref: linesDropdownRef, children: [
74882
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-xs font-medium text-gray-500 uppercase tracking-wide ml-1", children: "Lines" }),
74883
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
74884
+ /* @__PURE__ */ jsxRuntime.jsxs(
74885
+ "button",
74886
+ {
74887
+ type: "button",
74888
+ onClick: () => setIsLinesDropdownOpen((prev) => !prev),
74889
+ className: "w-full flex items-center justify-between pl-3 pr-3 py-2 text-sm bg-gray-50 border border-gray-200 hover:border-gray-300 rounded-lg text-gray-900 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all cursor-pointer text-left",
74890
+ children: [
74891
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate pr-2", children: isAllLinesSelected ? "All Lines" : selectedLineIds.length === 1 ? lineOptions.find((l) => l.id === selectedLineIds[0])?.name || "1 Line Selected" : `${selectedLineIds.length} Lines Selected` }),
74892
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: `w-4 h-4 text-gray-500 flex-shrink-0 transition-transform ${isLinesDropdownOpen ? "rotate-180" : ""}` })
74893
+ ]
74894
+ }
74895
+ ),
74896
+ isLinesDropdownOpen ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute top-full left-0 right-0 mt-1 max-h-48 overflow-y-auto rounded-lg border border-gray-200 bg-white shadow-lg p-1.5 space-y-0.5 z-[60]", children: [
74897
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-2.5 rounded-md px-2 py-1.5 text-sm text-gray-900 hover:bg-gray-50 cursor-pointer", children: [
74898
+ /* @__PURE__ */ jsxRuntime.jsx(
74899
+ "input",
74900
+ {
74901
+ type: "checkbox",
74902
+ className: "h-4 w-4 rounded border-slate-300 text-blue-600 focus:ring-blue-500",
74903
+ checked: isAllLinesSelected,
74904
+ onChange: handleAllLinesToggle
74905
+ }
74906
+ ),
74907
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "All Lines" })
74908
+ ] }),
74909
+ lineOptions.map((line) => {
74910
+ const isChecked = selectedLineIdSet.has(line.id);
74911
+ const disableUncheck = isChecked && selectedLineIds.length <= 1;
74912
+ return /* @__PURE__ */ jsxRuntime.jsxs(
74913
+ "label",
74914
+ {
74915
+ className: `flex items-center gap-2.5 rounded-md px-2 py-1.5 text-sm cursor-pointer transition-colors ${disableUncheck ? "text-gray-400 hover:bg-transparent" : "text-gray-800 hover:bg-gray-50"}`,
74916
+ children: [
74917
+ /* @__PURE__ */ jsxRuntime.jsx(
74918
+ "input",
74919
+ {
74920
+ type: "checkbox",
74921
+ className: "h-4 w-4 rounded border-slate-300 text-blue-600 focus:ring-blue-500 disabled:opacity-40",
74922
+ checked: isChecked,
74923
+ disabled: disableUncheck,
74924
+ onChange: () => handleLineToggle(line.id)
74925
+ }
74926
+ ),
74927
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: line.name })
74928
+ ]
74929
+ },
74930
+ line.id
74931
+ );
74932
+ })
74933
+ ] }) : null
74934
+ ] })
74935
+ ] })
74936
+ ] })
74821
74937
  ] }) : null
74822
74938
  ] }) });
74823
74939
  });
@@ -75175,7 +75291,7 @@ var IdleBreakdownCard = React141__namespace.default.memo(({
75175
75291
  }, [idle.scope.idle_time_vlm_enabled_line_count, scopedLineCount, showInitialSkeleton]);
75176
75292
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-slate-100 flex flex-col overflow-hidden text-left", children: [
75177
75293
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-5 py-4 flex-none flex justify-between items-center border-b border-slate-50/50 relative", children: /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-semibold text-gray-700", children: "Idle Time Breakdown" }) }),
75178
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-0 p-4 pt-2 relative", children: showInitialSkeleton ? /* @__PURE__ */ jsxRuntime.jsx(OverviewIdleBreakdownSkeleton, {}) : showIdleModuleNotEnabledState ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full flex items-center justify-center rounded-xl border border-dashed border-slate-200 bg-slate-50/80 px-6 text-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
75294
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-[250px] p-4 pt-2 relative", children: showInitialSkeleton ? /* @__PURE__ */ jsxRuntime.jsx(OverviewIdleBreakdownSkeleton, {}) : showIdleModuleNotEnabledState ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full flex items-center justify-center rounded-xl border border-dashed border-slate-200 bg-slate-50/80 px-6 text-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
75179
75295
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-slate-700", children: "Module not enabled" }),
75180
75296
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-xs text-slate-500", children: "Enable idle-time classification on at least one line to view this breakdown." })
75181
75297
  ] }) }) : /* @__PURE__ */ jsxRuntime.jsx(
@@ -75249,7 +75365,7 @@ var EfficiencyTrendCard = React141__namespace.default.memo(({
75249
75365
  }, []);
75250
75366
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-xl shadow-[0_2px_10px_-3px_rgba(6,81,237,0.1)] border border-slate-100 flex flex-col overflow-hidden text-left", children: [
75251
75367
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-5 flex-none flex justify-between items-center border-b border-slate-50/50", children: /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-semibold text-gray-700", children: "Efficiency Trend" }) }),
75252
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-0 w-full p-4 pt-4 relative", children: showInitialSkeleton ? /* @__PURE__ */ jsxRuntime.jsx(OverviewChartSkeleton, {}) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 pb-2 pr-4 pl-1", children: /* @__PURE__ */ jsxRuntime.jsx(
75368
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-[250px] w-full p-4 pt-4 relative", children: showInitialSkeleton ? /* @__PURE__ */ jsxRuntime.jsx(OverviewChartSkeleton, {}) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 pb-2 pr-4 pl-1", children: /* @__PURE__ */ jsxRuntime.jsx(
75253
75369
  LineChart,
75254
75370
  {
75255
75371
  data: trendData,
@@ -75716,6 +75832,7 @@ var PlantHeadView = () => {
75716
75832
  const [dateRange, setDateRange] = React141__namespace.default.useState(() => getCurrentWeekToDateRange(appTimezone));
75717
75833
  const [usesThisWeekComparison, setUsesThisWeekComparison] = React141__namespace.default.useState(true);
75718
75834
  const [trendMode, setTrendMode] = React141__namespace.default.useState("all");
75835
+ const [selectedLineIds, setSelectedLineIds] = React141__namespace.default.useState([]);
75719
75836
  React141__namespace.default.useEffect(() => {
75720
75837
  trackCorePageView("Operations Overview", {
75721
75838
  dashboard_surface: "operations_overview"
@@ -75734,9 +75851,27 @@ var PlantHeadView = () => {
75734
75851
  () => normalizedLineIds.join(","),
75735
75852
  [normalizedLineIds]
75736
75853
  );
75854
+ const lineOptions = React141__namespace.default.useMemo(
75855
+ () => normalizedLineIds.map((lineId) => ({
75856
+ id: lineId,
75857
+ name: getLineDisplayName(entityConfig, lineId)
75858
+ })),
75859
+ [entityConfig, normalizedLineIds]
75860
+ );
75861
+ React141__namespace.default.useEffect(() => {
75862
+ setSelectedLineIds((previous) => {
75863
+ if (normalizedLineIds.length === 0) return [];
75864
+ const previousSet = new Set(previous);
75865
+ const next = normalizedLineIds.filter((lineId) => previousSet.has(lineId));
75866
+ if (next.length === 0) {
75867
+ return normalizedLineIds;
75868
+ }
75869
+ return next;
75870
+ });
75871
+ }, [lineIdsKey, normalizedLineIds]);
75737
75872
  const scopedLineIds = React141__namespace.default.useMemo(
75738
- () => lineIdsKey ? lineIdsKey.split(",") : [],
75739
- [lineIdsKey]
75873
+ () => selectedLineIds.length > 0 ? selectedLineIds : normalizedLineIds,
75874
+ [normalizedLineIds, selectedLineIds]
75740
75875
  );
75741
75876
  const initializedTimezoneRef = React141__namespace.default.useRef(appTimezone);
75742
75877
  React141__namespace.default.useEffect(() => {
@@ -75764,6 +75899,16 @@ var PlantHeadView = () => {
75764
75899
  const handleTrendModeChange = React141__namespace.default.useCallback((mode) => {
75765
75900
  setTrendMode(mode);
75766
75901
  }, []);
75902
+ const handleSelectedLineIdsChange = React141__namespace.default.useCallback((lineIds) => {
75903
+ if (normalizedLineIds.length === 0) {
75904
+ setSelectedLineIds([]);
75905
+ return;
75906
+ }
75907
+ const uniqueSelected = Array.from(new Set(lineIds));
75908
+ const selectedSet = new Set(uniqueSelected);
75909
+ const next = normalizedLineIds.filter((lineId) => selectedSet.has(lineId));
75910
+ setSelectedLineIds(next.length > 0 ? next : normalizedLineIds);
75911
+ }, [normalizedLineIds]);
75767
75912
  const buildLineMonthlyHistoryUrl = React141__namespace.default.useCallback((lineId) => {
75768
75913
  const rangeStartDate = parseDateKeyToDate(dateRange.startKey);
75769
75914
  const params = new URLSearchParams();
@@ -75830,10 +75975,13 @@ var PlantHeadView = () => {
75830
75975
  {
75831
75976
  dateRange,
75832
75977
  trendMode,
75978
+ lineOptions,
75979
+ selectedLineIds: scopedLineIds,
75833
75980
  appTimezone,
75834
75981
  mobileMenuContext,
75835
75982
  onDateRangeChange: handleDateRangeChange,
75836
- onTrendModeChange: handleTrendModeChange
75983
+ onTrendModeChange: handleTrendModeChange,
75984
+ onSelectedLineIdsChange: handleSelectedLineIdsChange
75837
75985
  }
75838
75986
  ),
75839
75987
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4 sm:p-6 pb-6 max-w-[1800px] mx-auto w-full flex-1 min-h-0 overflow-y-auto flex flex-col gap-5", children: [
package/dist/index.mjs CHANGED
@@ -7409,6 +7409,16 @@ var getClipCycleTimeFrames = (metadata) => {
7409
7409
  }
7410
7410
  return parseFiniteNumber(metadata?.request?.metadata?.cycle_time);
7411
7411
  };
7412
+ var getClipCaptureFps = (clip) => {
7413
+ return parseFiniteNumber(clip?.capture_fps) ?? parseFiniteNumber(clip?.metadata?.playlist?.fps) ?? 20;
7414
+ };
7415
+ var getClipCycleTimeSeconds = (clip) => {
7416
+ const cycleTimeFrames = getClipCycleTimeFrames(clip?.metadata);
7417
+ if (cycleTimeFrames === void 0) {
7418
+ return void 0;
7419
+ }
7420
+ return cycleTimeFrames / getClipCaptureFps(clip);
7421
+ };
7412
7422
  var getSupabaseClient = () => {
7413
7423
  const existing = _getSupabaseInstanceOptional();
7414
7424
  if (existing) {
@@ -7921,10 +7931,10 @@ var S3ClipsSupabaseService = class {
7921
7931
  const transformedClips = (response.clips || []).map((clip) => {
7922
7932
  const clipId = clip.id ?? clip.clip_id;
7923
7933
  const clipType = clip.type ?? clip.clip_type_name;
7924
- const cycleTimeSeconds = parseFiniteNumber(clip.cycle_time_seconds) ?? (() => {
7925
- const cycleTimeFrames = getClipCycleTimeFrames(clip.metadata);
7926
- return cycleTimeFrames !== void 0 ? cycleTimeFrames / (clip.metadata?.playlist?.fps || 20) : void 0;
7927
- })();
7934
+ const cycleTimeSeconds = parseFiniteNumber(clip.cycle_time_seconds) ?? getClipCycleTimeSeconds({
7935
+ capture_fps: clip.capture_fps,
7936
+ metadata: clip.metadata
7937
+ });
7928
7938
  return {
7929
7939
  id: clipId,
7930
7940
  src: clip.src ?? clip.playlist,
@@ -39474,7 +39484,7 @@ var FileManagerFilters = ({
39474
39484
  ] }) })
39475
39485
  ] }, node.id);
39476
39486
  };
39477
- return /* @__PURE__ */ jsxs("div", { className: `relative bg-white rounded-2xl shadow-lg border border-gray-100 h-full hover:shadow-xl transition-all duration-300 ease-out backdrop-blur-sm ${className}`, children: [
39487
+ return /* @__PURE__ */ jsxs("div", { className: `relative flex h-full min-h-[320px] flex-col bg-white rounded-2xl shadow-lg border border-gray-100 hover:shadow-xl transition-all duration-300 ease-out backdrop-blur-sm ${className}`, children: [
39478
39488
  /* @__PURE__ */ jsxs("div", { className: "p-4 border-b border-gray-50 bg-gradient-to-br from-slate-50/80 via-white to-blue-50/30", children: [
39479
39489
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
39480
39490
  /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
@@ -39556,7 +39566,7 @@ var FileManagerFilters = ({
39556
39566
  /* @__PURE__ */ jsxs(
39557
39567
  "div",
39558
39568
  {
39559
- className: "absolute top-14 right-16 z-50 bg-white rounded-xl shadow-xl border border-slate-200 w-[240px] animate-in slide-in-from-top-2 duration-200",
39569
+ className: "absolute top-14 right-2 z-50 bg-white rounded-xl shadow-xl border border-slate-200 w-[min(88vw,240px)] sm:right-16 animate-in slide-in-from-top-2 duration-200",
39560
39570
  onClick: (e) => e.stopPropagation(),
39561
39571
  children: [
39562
39572
  /* @__PURE__ */ jsxs("div", { className: "px-4 py-3 flex items-center justify-between border-b border-slate-200", children: [
@@ -39613,7 +39623,7 @@ var FileManagerFilters = ({
39613
39623
  /* @__PURE__ */ jsxs(
39614
39624
  "div",
39615
39625
  {
39616
- className: "absolute top-14 right-4 z-50 bg-white rounded-xl shadow-xl border border-slate-200 w-[280px] animate-in slide-in-from-top-2 duration-200",
39626
+ className: "absolute top-14 right-2 z-50 bg-white rounded-xl shadow-xl border border-slate-200 w-[min(92vw,280px)] sm:right-4 animate-in slide-in-from-top-2 duration-200",
39617
39627
  onClick: (e) => e.stopPropagation(),
39618
39628
  children: [
39619
39629
  /* @__PURE__ */ jsxs("div", { className: "px-4 py-3 flex items-center justify-between border-b border-slate-200", children: [
@@ -39730,7 +39740,7 @@ var FileManagerFilters = ({
39730
39740
  }
39731
39741
  )
39732
39742
  ] }),
39733
- /* @__PURE__ */ jsxs("div", { className: "px-4 py-3 h-[calc(100%-8rem)] overflow-y-auto scrollbar-thin", children: [
39743
+ /* @__PURE__ */ jsxs("div", { className: "px-4 py-3 flex-1 min-h-0 overflow-y-auto scrollbar-thin", children: [
39734
39744
  /* @__PURE__ */ jsx("div", { className: "space-y-2", children: filterTree.map((node) => renderNode(node)) }),
39735
39745
  filterTree.length === 0 && isTimeFilterActive && (startTime || endTime) && /* @__PURE__ */ jsxs("div", { className: "text-center py-12", children: [
39736
39746
  /* @__PURE__ */ jsx("div", { className: "text-slate-300 mb-4", children: /* @__PURE__ */ jsx(Clock, { className: "h-12 w-12 mx-auto" }) }),
@@ -41841,17 +41851,17 @@ var BottlenecksContent = ({
41841
41851
  }
41842
41852
  }, [workspaceTargetCycleTime]);
41843
41853
  if (!dashboardConfig?.s3Config) {
41844
- return /* @__PURE__ */ jsxs("div", { className: "flex-grow p-4 flex flex-col items-center justify-center h-[calc(100vh-12rem)] text-center", children: [
41854
+ return /* @__PURE__ */ jsxs("div", { className: "flex-grow p-4 flex flex-col items-center justify-center min-h-[calc(100dvh-12rem)] text-center", children: [
41845
41855
  /* @__PURE__ */ jsx(XCircle, { className: "w-12 h-12 text-red-400 mb-3" }),
41846
41856
  /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-red-700 mb-1", children: "S3 Configuration Missing" }),
41847
41857
  /* @__PURE__ */ jsx("p", { className: "text-gray-600 max-w-md", children: "S3 configuration is required to load video clips. Please check your dashboard configuration." })
41848
41858
  ] });
41849
41859
  }
41850
41860
  if (clipTypesLoading && allVideos.length === 0 && Object.keys(mergedCounts).length === 0) {
41851
- return /* @__PURE__ */ jsx("div", { className: "flex-grow p-4 flex items-center justify-center h-[calc(100vh-12rem)]", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "lg", message: "Loading clips..." }) });
41861
+ return /* @__PURE__ */ jsx("div", { className: "flex-grow p-4 flex items-center justify-center min-h-[calc(100dvh-12rem)]", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "lg", message: "Loading clips..." }) });
41852
41862
  }
41853
41863
  if (error && error.type === "fatal" && !hasInitialLoad || clipTypesError) {
41854
- return /* @__PURE__ */ jsxs("div", { className: "flex-grow p-4 flex flex-col items-center justify-center h-[calc(100vh-12rem)] text-center", children: [
41864
+ return /* @__PURE__ */ jsxs("div", { className: "flex-grow p-4 flex flex-col items-center justify-center min-h-[calc(100dvh-12rem)] text-center", children: [
41855
41865
  /* @__PURE__ */ jsx(XCircle, { className: "w-12 h-12 text-red-400 mb-3" }),
41856
41866
  /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-red-700 mb-1", children: "Error Loading Clips" }),
41857
41867
  /* @__PURE__ */ jsx("p", { className: "text-gray-600 max-w-md", children: error?.message || clipTypesError })
@@ -41866,7 +41876,7 @@ var BottlenecksContent = ({
41866
41876
  clipTypesError,
41867
41877
  mergedCounts
41868
41878
  });
41869
- return /* @__PURE__ */ jsxs("div", { className: "flex-grow p-1.5 sm:p-2 lg:p-4 h-[calc(100vh-12rem)] relative", children: [
41879
+ return /* @__PURE__ */ jsxs("div", { className: "flex-grow p-1.5 sm:p-2 lg:p-4 min-h-[calc(100dvh-12rem)] lg:h-[calc(100dvh-12rem)] relative", children: [
41870
41880
  hasNewClips && newClipsNotification && /* @__PURE__ */ jsx(
41871
41881
  NewClipsNotification,
41872
41882
  {
@@ -41875,8 +41885,8 @@ var BottlenecksContent = ({
41875
41885
  onDismiss: clearNotification
41876
41886
  }
41877
41887
  ),
41878
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col lg:flex-row gap-4 h-full", children: [
41879
- /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0 lg:flex-[3]", children: /* @__PURE__ */ jsx("div", { className: "bg-white rounded-lg shadow-sm overflow-hidden h-full", children: filteredVideos.length > 0 && currentVideo && !isFullscreen ? /* @__PURE__ */ jsx("div", { className: "p-4 h-full", children: /* @__PURE__ */ jsx("div", { className: "relative h-full group", children: /* @__PURE__ */ jsxs("div", { className: "relative w-full h-full overflow-hidden rounded-md shadow-inner bg-gray-900", children: [
41888
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 lg:flex-row lg:h-full", children: [
41889
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 w-full lg:flex-[3] lg:h-full", children: /* @__PURE__ */ jsx("div", { className: "bg-white rounded-lg shadow-sm overflow-hidden lg:h-full", children: filteredVideos.length > 0 && currentVideo && !isFullscreen ? /* @__PURE__ */ jsx("div", { className: "p-4 lg:h-full", children: /* @__PURE__ */ jsx("div", { className: "relative group lg:h-full", children: /* @__PURE__ */ jsxs("div", { className: "relative w-full aspect-video lg:aspect-auto lg:h-full overflow-hidden rounded-md shadow-inner bg-gray-900", children: [
41880
41890
  /* @__PURE__ */ jsx(
41881
41891
  "div",
41882
41892
  {
@@ -42026,23 +42036,23 @@ var BottlenecksContent = ({
42026
42036
  )
42027
42037
  ] }) }) }) : (
42028
42038
  /* Priority 5: Show "no clips found" only if we have counts and there are truly no clips for workspace */
42029
- hasInitialLoad && Object.keys(mergedCounts).length > 0 && Object.values(mergedCounts).every((count) => count === 0) ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center h-full", children: /* @__PURE__ */ jsxs("div", { className: "text-center p-8", children: [
42039
+ hasInitialLoad && Object.keys(mergedCounts).length > 0 && Object.values(mergedCounts).every((count) => count === 0) ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center min-h-[220px] sm:min-h-[320px] lg:h-full", children: /* @__PURE__ */ jsxs("div", { className: "text-center p-8", children: [
42030
42040
  /* @__PURE__ */ jsx("svg", { className: "w-16 h-16 text-gray-300 mx-auto mb-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z" }) }),
42031
42041
  /* @__PURE__ */ jsx("h3", { className: "text-xl font-medium text-gray-700 mb-2", children: "No Clips Found" }),
42032
42042
  /* @__PURE__ */ jsx("p", { className: "text-gray-500", children: "There were no video clips found for this workspace today." })
42033
42043
  ] }) }) : (
42034
42044
  /* Priority 6: Show "no matching clips" only if we have data loaded and specifically no clips for this filter */
42035
- hasInitialLoad && (mergedCounts[activeFilter] || 0) === 0 ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center h-full", children: /* @__PURE__ */ jsxs("div", { className: "text-center p-8", children: [
42045
+ hasInitialLoad && (mergedCounts[activeFilter] || 0) === 0 ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center min-h-[220px] sm:min-h-[320px] lg:h-full", children: /* @__PURE__ */ jsxs("div", { className: "text-center p-8", children: [
42036
42046
  /* @__PURE__ */ jsx("svg", { className: "w-16 h-16 text-gray-300 mx-auto mb-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z" }) }),
42037
42047
  /* @__PURE__ */ jsx("h3", { className: "text-xl font-medium text-gray-700 mb-2", children: "No Matching Clips" }),
42038
42048
  /* @__PURE__ */ jsx("p", { className: "text-gray-500", children: "There are no clips matching the selected filter." })
42039
42049
  ] }) }) : (
42040
42050
  /* Priority 7: Default loading state for any other case */
42041
- /* @__PURE__ */ jsx("div", { className: "p-4 h-full", children: /* @__PURE__ */ jsx("div", { className: "relative h-full", children: /* @__PURE__ */ jsx("div", { className: "relative w-full h-full overflow-hidden rounded-md shadow-inner bg-gray-900 flex items-center justify-center", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading..." }) }) }) })
42051
+ /* @__PURE__ */ jsx("div", { className: "p-4 lg:h-full", children: /* @__PURE__ */ jsx("div", { className: "relative lg:h-full", children: /* @__PURE__ */ jsx("div", { className: "relative w-full min-h-[220px] sm:min-h-[320px] lg:min-h-0 lg:h-full overflow-hidden rounded-md shadow-inner bg-gray-900 flex items-center justify-center", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading..." }) }) }) })
42042
42052
  )
42043
42053
  )
42044
42054
  ) }) }),
42045
- /* @__PURE__ */ jsx("div", { className: "flex-shrink-0 lg:flex-[1] lg:min-w-[280px] lg:max-w-[320px]", children: triageMode ? (
42055
+ /* @__PURE__ */ jsx("div", { className: "w-full lg:flex-shrink-0 lg:flex-[1] lg:min-w-[280px] lg:max-w-[320px] lg:h-full", children: triageMode ? (
42046
42056
  /* Triage Mode - Direct tile view for cycle completions and idle time */
42047
42057
  /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-lg shadow-sm h-full overflow-hidden flex flex-col", children: [
42048
42058
  /* @__PURE__ */ jsxs("div", { className: "px-4 py-3 border-b border-gray-100", children: [
@@ -74628,33 +74638,51 @@ var OverviewImprovementsSkeleton = () => /* @__PURE__ */ jsx("div", { className:
74628
74638
  var OperationsOverviewHeader = React141__default.memo(({
74629
74639
  dateRange,
74630
74640
  trendMode,
74641
+ lineOptions,
74642
+ selectedLineIds,
74631
74643
  appTimezone,
74632
74644
  mobileMenuContext,
74633
74645
  onDateRangeChange,
74634
- onTrendModeChange
74646
+ onTrendModeChange,
74647
+ onSelectedLineIdsChange
74635
74648
  }) => {
74636
74649
  bumpRenderCounter();
74637
74650
  const [isFilterOpen, setIsFilterOpen] = React141__default.useState(false);
74651
+ const [isLinesDropdownOpen, setIsLinesDropdownOpen] = React141__default.useState(false);
74638
74652
  const filterRef = React141__default.useRef(null);
74639
74653
  const filterButtonRef = React141__default.useRef(null);
74640
74654
  const mobileFilterButtonRef = React141__default.useRef(null);
74641
- const currentWeekRange = React141__default.useMemo(
74642
- () => getCurrentWeekToDateRange(appTimezone),
74643
- [appTimezone]
74644
- );
74645
- const isCurrentWeekToDateRange = dateRange.startKey === currentWeekRange.startKey && dateRange.endKey === currentWeekRange.endKey;
74655
+ const linesDropdownRef = React141__default.useRef(null);
74646
74656
  const mobileSubtitle = React141__default.useMemo(() => {
74647
- if (isCurrentWeekToDateRange) {
74648
- return `Week of ${format(parseDateKeyToDate(dateRange.startKey), "do MMM")}`;
74657
+ if (dateRange.startKey === dateRange.endKey) {
74658
+ return format(parseDateKeyToDate(dateRange.startKey), "do MMM, yyyy");
74649
74659
  }
74650
74660
  return `${format(parseDateKeyToDate(dateRange.startKey), "do MMM")} - ${format(parseDateKeyToDate(dateRange.endKey), "do MMM, yyyy")}`;
74651
- }, [dateRange.endKey, dateRange.startKey, isCurrentWeekToDateRange]);
74661
+ }, [dateRange.endKey, dateRange.startKey]);
74652
74662
  const desktopSubtitle = React141__default.useMemo(() => {
74653
- if (isCurrentWeekToDateRange) {
74654
- return `Week of ${format(parseDateKeyToDate(dateRange.startKey), "do MMMM, yyyy")}`;
74663
+ if (dateRange.startKey === dateRange.endKey) {
74664
+ return format(parseDateKeyToDate(dateRange.startKey), "do MMMM, yyyy");
74655
74665
  }
74656
74666
  return `${format(parseDateKeyToDate(dateRange.startKey), "do MMMM, yyyy")} - ${format(parseDateKeyToDate(dateRange.endKey), "do MMMM, yyyy")}`;
74657
- }, [dateRange.endKey, dateRange.startKey, isCurrentWeekToDateRange]);
74667
+ }, [dateRange.endKey, dateRange.startKey]);
74668
+ const availableLineIds = React141__default.useMemo(
74669
+ () => lineOptions.map((line) => line.id),
74670
+ [lineOptions]
74671
+ );
74672
+ const selectedLineIdSet = React141__default.useMemo(
74673
+ () => new Set(selectedLineIds),
74674
+ [selectedLineIds]
74675
+ );
74676
+ const isAllLinesSelected = React141__default.useMemo(() => {
74677
+ if (availableLineIds.length === 0) return true;
74678
+ return availableLineIds.every((lineId) => selectedLineIdSet.has(lineId));
74679
+ }, [availableLineIds, selectedLineIdSet]);
74680
+ const activeFilterCount = React141__default.useMemo(() => {
74681
+ let count = 0;
74682
+ if (trendMode !== "all") count += 1;
74683
+ if (!isAllLinesSelected) count += 1;
74684
+ return count;
74685
+ }, [isAllLinesSelected, trendMode]);
74658
74686
  const handleFilterToggle = React141__default.useCallback(() => {
74659
74687
  trackCoreEvent("Operations Overview Filter Toggled", {
74660
74688
  action: !isFilterOpen ? "open" : "close"
@@ -74668,10 +74696,43 @@ var OperationsOverviewHeader = React141__default.memo(({
74668
74696
  });
74669
74697
  onTrendModeChange(nextMode);
74670
74698
  }, [onTrendModeChange]);
74699
+ const handleAllLinesToggle = React141__default.useCallback(() => {
74700
+ trackCoreEvent("Operations Overview Line Filter Changed", {
74701
+ selected_line_ids: availableLineIds,
74702
+ selected_line_count: availableLineIds.length,
74703
+ mode: "all"
74704
+ });
74705
+ onSelectedLineIdsChange(availableLineIds);
74706
+ }, [availableLineIds, onSelectedLineIdsChange]);
74707
+ const handleLineToggle = React141__default.useCallback((lineId) => {
74708
+ const current = new Set(selectedLineIds);
74709
+ if (current.has(lineId)) {
74710
+ if (current.size <= 1) return;
74711
+ current.delete(lineId);
74712
+ } else {
74713
+ current.add(lineId);
74714
+ }
74715
+ const next = availableLineIds.filter((id3) => current.has(id3));
74716
+ trackCoreEvent("Operations Overview Line Filter Changed", {
74717
+ selected_line_ids: next,
74718
+ selected_line_count: next.length,
74719
+ mode: "custom"
74720
+ });
74721
+ onSelectedLineIdsChange(next);
74722
+ }, [availableLineIds, onSelectedLineIdsChange, selectedLineIds]);
74723
+ const handleClearAllFilters = React141__default.useCallback(() => {
74724
+ onTrendModeChange("all");
74725
+ onSelectedLineIdsChange(availableLineIds);
74726
+ setIsFilterOpen(false);
74727
+ }, [availableLineIds, onSelectedLineIdsChange, onTrendModeChange]);
74671
74728
  React141__default.useEffect(() => {
74672
74729
  const handleClickOutside = (event) => {
74673
- if (filterRef.current && !filterRef.current.contains(event.target) && filterButtonRef.current && !filterButtonRef.current.contains(event.target) && mobileFilterButtonRef.current && !mobileFilterButtonRef.current.contains(event.target)) {
74730
+ const target = event.target;
74731
+ if (filterRef.current && !filterRef.current.contains(target) && filterButtonRef.current && !filterButtonRef.current.contains(target) && mobileFilterButtonRef.current && !mobileFilterButtonRef.current.contains(target)) {
74674
74732
  setIsFilterOpen(false);
74733
+ setIsLinesDropdownOpen(false);
74734
+ } else if (linesDropdownRef.current && !linesDropdownRef.current.contains(target)) {
74735
+ setIsLinesDropdownOpen(false);
74675
74736
  }
74676
74737
  };
74677
74738
  document.addEventListener("mousedown", handleClickOutside);
@@ -74684,7 +74745,8 @@ var OperationsOverviewHeader = React141__default.memo(({
74684
74745
  mobileMenuContext ? /* @__PURE__ */ jsx(
74685
74746
  HamburgerButton,
74686
74747
  {
74687
- onClick: mobileMenuContext.onMobileMenuOpen,
74748
+ onClick: mobileMenuContext.onMobileMenuOpen || (() => {
74749
+ }),
74688
74750
  className: "flex-shrink-0 -ml-1"
74689
74751
  }
74690
74752
  ) : /* @__PURE__ */ jsx("div", { className: "w-8 flex-shrink-0" }),
@@ -74709,11 +74771,11 @@ var OperationsOverviewHeader = React141__default.memo(({
74709
74771
  {
74710
74772
  ref: mobileFilterButtonRef,
74711
74773
  onClick: handleFilterToggle,
74712
- className: `p-2 rounded-full transition-colors relative ${isFilterOpen || trendMode !== "all" ? "bg-blue-50" : "active:bg-gray-100"}`,
74774
+ className: `p-2 rounded-full transition-colors relative ${isFilterOpen || activeFilterCount > 0 ? "bg-blue-50" : "active:bg-gray-100"}`,
74713
74775
  "aria-label": "Open filters",
74714
74776
  children: [
74715
- /* @__PURE__ */ jsx(Filter, { className: `w-5 h-5 ${trendMode !== "all" ? "text-blue-600" : "text-gray-700"}` }),
74716
- trendMode !== "all" ? /* @__PURE__ */ jsx("span", { className: "absolute -top-1 -right-1 flex items-center justify-center w-4 h-4 bg-blue-600 text-white text-[10px] rounded-full font-bold", children: "1" }) : null
74777
+ /* @__PURE__ */ jsx(Filter, { className: `w-5 h-5 ${activeFilterCount > 0 ? "text-blue-600" : "text-gray-700"}` }),
74778
+ activeFilterCount > 0 ? /* @__PURE__ */ jsx("span", { className: "absolute -top-1 -right-1 flex items-center justify-center w-4 h-4 bg-blue-600 text-white text-[10px] rounded-full font-bold", children: activeFilterCount }) : null
74717
74779
  ]
74718
74780
  }
74719
74781
  )
@@ -74741,10 +74803,10 @@ var OperationsOverviewHeader = React141__default.memo(({
74741
74803
  {
74742
74804
  ref: filterButtonRef,
74743
74805
  onClick: handleFilterToggle,
74744
- className: `flex items-center gap-2 px-3 py-1.5 rounded-lg border text-sm font-medium transition-all shadow-sm ${isFilterOpen || trendMode !== "all" ? "border-blue-500 bg-blue-50 text-blue-700 ring-1 ring-blue-500" : "border-slate-200 bg-white text-slate-700 hover:bg-slate-50"}`,
74806
+ className: `flex items-center gap-2 px-3 py-1.5 rounded-lg border text-sm font-medium transition-all shadow-sm ${isFilterOpen || activeFilterCount > 0 ? "border-blue-500 bg-blue-50 text-blue-700 ring-1 ring-blue-500" : "border-slate-200 bg-white text-slate-700 hover:bg-slate-50"}`,
74745
74807
  "aria-label": "Open filters",
74746
74808
  children: [
74747
- /* @__PURE__ */ jsx(Filter, { className: `w-[18px] h-[18px] ${trendMode !== "all" ? "text-blue-600" : "text-slate-500"}` }),
74809
+ /* @__PURE__ */ jsx(Filter, { className: `w-[18px] h-[18px] ${activeFilterCount > 0 ? "text-blue-600" : "text-slate-500"}` }),
74748
74810
  "Filters",
74749
74811
  /* @__PURE__ */ jsx(ChevronDown, { className: `w-4 h-4 ml-0.5 transition-transform duration-200 ${isFilterOpen ? "rotate-180" : ""}` })
74750
74812
  ]
@@ -74755,40 +74817,94 @@ var OperationsOverviewHeader = React141__default.memo(({
74755
74817
  isFilterOpen ? /* @__PURE__ */ jsxs("div", { ref: filterRef, className: "absolute right-3 sm:right-4 md:right-5 lg:right-6 top-full mt-2 w-72 bg-white rounded-xl shadow-xl border border-gray-100 p-4 z-50", children: [
74756
74818
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-3", children: [
74757
74819
  /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-gray-900", children: "Filter View" }),
74758
- trendMode !== "all" ? /* @__PURE__ */ jsx(
74820
+ activeFilterCount > 0 ? /* @__PURE__ */ jsx(
74759
74821
  "button",
74760
74822
  {
74761
- onClick: () => {
74762
- onTrendModeChange("all");
74763
- setIsFilterOpen(false);
74764
- },
74823
+ onClick: handleClearAllFilters,
74765
74824
  className: "text-xs text-red-600 hover:text-red-700 font-medium",
74766
74825
  children: "Clear all"
74767
74826
  }
74768
74827
  ) : null
74769
74828
  ] }),
74770
- /* @__PURE__ */ jsx("div", { className: "space-y-3", children: /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
74771
- /* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-gray-500 uppercase tracking-wide ml-1", children: "Shift" }),
74772
- /* @__PURE__ */ jsx("div", { className: "relative", children: /* @__PURE__ */ jsxs(
74773
- "select",
74774
- {
74775
- value: trendMode,
74776
- onChange: handleTrendModeChange,
74777
- className: "w-full appearance-none pl-3 pr-8 py-2 text-sm bg-gray-50 border border-gray-200 hover:border-gray-300 rounded-lg text-gray-900 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all cursor-pointer",
74778
- style: {
74779
- backgroundImage: `url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e")`,
74780
- backgroundPosition: "right 0.75rem center",
74781
- backgroundRepeat: "no-repeat",
74782
- backgroundSize: "1.2em 1.2em"
74783
- },
74784
- children: [
74785
- /* @__PURE__ */ jsx("option", { value: "all", children: "All Shifts" }),
74786
- /* @__PURE__ */ jsx("option", { value: "day", children: "Day Shift" }),
74787
- /* @__PURE__ */ jsx("option", { value: "night", children: "Night Shift" })
74788
- ]
74789
- }
74790
- ) })
74791
- ] }) })
74829
+ /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
74830
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
74831
+ /* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-gray-500 uppercase tracking-wide ml-1", children: "Shift" }),
74832
+ /* @__PURE__ */ jsx("div", { className: "relative", children: /* @__PURE__ */ jsxs(
74833
+ "select",
74834
+ {
74835
+ value: trendMode,
74836
+ onChange: handleTrendModeChange,
74837
+ className: "w-full appearance-none pl-3 pr-8 py-2 text-sm bg-gray-50 border border-gray-200 hover:border-gray-300 rounded-lg text-gray-900 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all cursor-pointer",
74838
+ style: {
74839
+ backgroundImage: `url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e")`,
74840
+ backgroundPosition: "right 0.75rem center",
74841
+ backgroundRepeat: "no-repeat",
74842
+ backgroundSize: "1.2em 1.2em"
74843
+ },
74844
+ children: [
74845
+ /* @__PURE__ */ jsx("option", { value: "all", children: "All Shifts" }),
74846
+ /* @__PURE__ */ jsx("option", { value: "day", children: "Day Shift" }),
74847
+ /* @__PURE__ */ jsx("option", { value: "night", children: "Night Shift" })
74848
+ ]
74849
+ }
74850
+ ) })
74851
+ ] }),
74852
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1", ref: linesDropdownRef, children: [
74853
+ /* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-gray-500 uppercase tracking-wide ml-1", children: "Lines" }),
74854
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
74855
+ /* @__PURE__ */ jsxs(
74856
+ "button",
74857
+ {
74858
+ type: "button",
74859
+ onClick: () => setIsLinesDropdownOpen((prev) => !prev),
74860
+ className: "w-full flex items-center justify-between pl-3 pr-3 py-2 text-sm bg-gray-50 border border-gray-200 hover:border-gray-300 rounded-lg text-gray-900 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all cursor-pointer text-left",
74861
+ children: [
74862
+ /* @__PURE__ */ jsx("span", { className: "truncate pr-2", children: isAllLinesSelected ? "All Lines" : selectedLineIds.length === 1 ? lineOptions.find((l) => l.id === selectedLineIds[0])?.name || "1 Line Selected" : `${selectedLineIds.length} Lines Selected` }),
74863
+ /* @__PURE__ */ jsx(ChevronDown, { className: `w-4 h-4 text-gray-500 flex-shrink-0 transition-transform ${isLinesDropdownOpen ? "rotate-180" : ""}` })
74864
+ ]
74865
+ }
74866
+ ),
74867
+ isLinesDropdownOpen ? /* @__PURE__ */ jsxs("div", { className: "absolute top-full left-0 right-0 mt-1 max-h-48 overflow-y-auto rounded-lg border border-gray-200 bg-white shadow-lg p-1.5 space-y-0.5 z-[60]", children: [
74868
+ /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2.5 rounded-md px-2 py-1.5 text-sm text-gray-900 hover:bg-gray-50 cursor-pointer", children: [
74869
+ /* @__PURE__ */ jsx(
74870
+ "input",
74871
+ {
74872
+ type: "checkbox",
74873
+ className: "h-4 w-4 rounded border-slate-300 text-blue-600 focus:ring-blue-500",
74874
+ checked: isAllLinesSelected,
74875
+ onChange: handleAllLinesToggle
74876
+ }
74877
+ ),
74878
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: "All Lines" })
74879
+ ] }),
74880
+ lineOptions.map((line) => {
74881
+ const isChecked = selectedLineIdSet.has(line.id);
74882
+ const disableUncheck = isChecked && selectedLineIds.length <= 1;
74883
+ return /* @__PURE__ */ jsxs(
74884
+ "label",
74885
+ {
74886
+ className: `flex items-center gap-2.5 rounded-md px-2 py-1.5 text-sm cursor-pointer transition-colors ${disableUncheck ? "text-gray-400 hover:bg-transparent" : "text-gray-800 hover:bg-gray-50"}`,
74887
+ children: [
74888
+ /* @__PURE__ */ jsx(
74889
+ "input",
74890
+ {
74891
+ type: "checkbox",
74892
+ className: "h-4 w-4 rounded border-slate-300 text-blue-600 focus:ring-blue-500 disabled:opacity-40",
74893
+ checked: isChecked,
74894
+ disabled: disableUncheck,
74895
+ onChange: () => handleLineToggle(line.id)
74896
+ }
74897
+ ),
74898
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: line.name })
74899
+ ]
74900
+ },
74901
+ line.id
74902
+ );
74903
+ })
74904
+ ] }) : null
74905
+ ] })
74906
+ ] })
74907
+ ] })
74792
74908
  ] }) : null
74793
74909
  ] }) });
74794
74910
  });
@@ -75146,7 +75262,7 @@ var IdleBreakdownCard = React141__default.memo(({
75146
75262
  }, [idle.scope.idle_time_vlm_enabled_line_count, scopedLineCount, showInitialSkeleton]);
75147
75263
  return /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-slate-100 flex flex-col overflow-hidden text-left", children: [
75148
75264
  /* @__PURE__ */ jsx("div", { className: "px-5 py-4 flex-none flex justify-between items-center border-b border-slate-50/50 relative", children: /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-gray-700", children: "Idle Time Breakdown" }) }),
75149
- /* @__PURE__ */ jsx("div", { className: "flex-1 min-h-0 p-4 pt-2 relative", children: showInitialSkeleton ? /* @__PURE__ */ jsx(OverviewIdleBreakdownSkeleton, {}) : showIdleModuleNotEnabledState ? /* @__PURE__ */ jsx("div", { className: "h-full flex items-center justify-center rounded-xl border border-dashed border-slate-200 bg-slate-50/80 px-6 text-center", children: /* @__PURE__ */ jsxs("div", { children: [
75265
+ /* @__PURE__ */ jsx("div", { className: "flex-1 min-h-[250px] p-4 pt-2 relative", children: showInitialSkeleton ? /* @__PURE__ */ jsx(OverviewIdleBreakdownSkeleton, {}) : showIdleModuleNotEnabledState ? /* @__PURE__ */ jsx("div", { className: "h-full flex items-center justify-center rounded-xl border border-dashed border-slate-200 bg-slate-50/80 px-6 text-center", children: /* @__PURE__ */ jsxs("div", { children: [
75150
75266
  /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-slate-700", children: "Module not enabled" }),
75151
75267
  /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-slate-500", children: "Enable idle-time classification on at least one line to view this breakdown." })
75152
75268
  ] }) }) : /* @__PURE__ */ jsx(
@@ -75220,7 +75336,7 @@ var EfficiencyTrendCard = React141__default.memo(({
75220
75336
  }, []);
75221
75337
  return /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-xl shadow-[0_2px_10px_-3px_rgba(6,81,237,0.1)] border border-slate-100 flex flex-col overflow-hidden text-left", children: [
75222
75338
  /* @__PURE__ */ jsx("div", { className: "px-6 py-5 flex-none flex justify-between items-center border-b border-slate-50/50", children: /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-gray-700", children: "Efficiency Trend" }) }),
75223
- /* @__PURE__ */ jsx("div", { className: "flex-1 min-h-0 w-full p-4 pt-4 relative", children: showInitialSkeleton ? /* @__PURE__ */ jsx(OverviewChartSkeleton, {}) : /* @__PURE__ */ jsx("div", { className: "absolute inset-0 pb-2 pr-4 pl-1", children: /* @__PURE__ */ jsx(
75339
+ /* @__PURE__ */ jsx("div", { className: "flex-1 min-h-[250px] w-full p-4 pt-4 relative", children: showInitialSkeleton ? /* @__PURE__ */ jsx(OverviewChartSkeleton, {}) : /* @__PURE__ */ jsx("div", { className: "absolute inset-0 pb-2 pr-4 pl-1", children: /* @__PURE__ */ jsx(
75224
75340
  LineChart,
75225
75341
  {
75226
75342
  data: trendData,
@@ -75687,6 +75803,7 @@ var PlantHeadView = () => {
75687
75803
  const [dateRange, setDateRange] = React141__default.useState(() => getCurrentWeekToDateRange(appTimezone));
75688
75804
  const [usesThisWeekComparison, setUsesThisWeekComparison] = React141__default.useState(true);
75689
75805
  const [trendMode, setTrendMode] = React141__default.useState("all");
75806
+ const [selectedLineIds, setSelectedLineIds] = React141__default.useState([]);
75690
75807
  React141__default.useEffect(() => {
75691
75808
  trackCorePageView("Operations Overview", {
75692
75809
  dashboard_surface: "operations_overview"
@@ -75705,9 +75822,27 @@ var PlantHeadView = () => {
75705
75822
  () => normalizedLineIds.join(","),
75706
75823
  [normalizedLineIds]
75707
75824
  );
75825
+ const lineOptions = React141__default.useMemo(
75826
+ () => normalizedLineIds.map((lineId) => ({
75827
+ id: lineId,
75828
+ name: getLineDisplayName(entityConfig, lineId)
75829
+ })),
75830
+ [entityConfig, normalizedLineIds]
75831
+ );
75832
+ React141__default.useEffect(() => {
75833
+ setSelectedLineIds((previous) => {
75834
+ if (normalizedLineIds.length === 0) return [];
75835
+ const previousSet = new Set(previous);
75836
+ const next = normalizedLineIds.filter((lineId) => previousSet.has(lineId));
75837
+ if (next.length === 0) {
75838
+ return normalizedLineIds;
75839
+ }
75840
+ return next;
75841
+ });
75842
+ }, [lineIdsKey, normalizedLineIds]);
75708
75843
  const scopedLineIds = React141__default.useMemo(
75709
- () => lineIdsKey ? lineIdsKey.split(",") : [],
75710
- [lineIdsKey]
75844
+ () => selectedLineIds.length > 0 ? selectedLineIds : normalizedLineIds,
75845
+ [normalizedLineIds, selectedLineIds]
75711
75846
  );
75712
75847
  const initializedTimezoneRef = React141__default.useRef(appTimezone);
75713
75848
  React141__default.useEffect(() => {
@@ -75735,6 +75870,16 @@ var PlantHeadView = () => {
75735
75870
  const handleTrendModeChange = React141__default.useCallback((mode) => {
75736
75871
  setTrendMode(mode);
75737
75872
  }, []);
75873
+ const handleSelectedLineIdsChange = React141__default.useCallback((lineIds) => {
75874
+ if (normalizedLineIds.length === 0) {
75875
+ setSelectedLineIds([]);
75876
+ return;
75877
+ }
75878
+ const uniqueSelected = Array.from(new Set(lineIds));
75879
+ const selectedSet = new Set(uniqueSelected);
75880
+ const next = normalizedLineIds.filter((lineId) => selectedSet.has(lineId));
75881
+ setSelectedLineIds(next.length > 0 ? next : normalizedLineIds);
75882
+ }, [normalizedLineIds]);
75738
75883
  const buildLineMonthlyHistoryUrl = React141__default.useCallback((lineId) => {
75739
75884
  const rangeStartDate = parseDateKeyToDate(dateRange.startKey);
75740
75885
  const params = new URLSearchParams();
@@ -75801,10 +75946,13 @@ var PlantHeadView = () => {
75801
75946
  {
75802
75947
  dateRange,
75803
75948
  trendMode,
75949
+ lineOptions,
75950
+ selectedLineIds: scopedLineIds,
75804
75951
  appTimezone,
75805
75952
  mobileMenuContext,
75806
75953
  onDateRangeChange: handleDateRangeChange,
75807
- onTrendModeChange: handleTrendModeChange
75954
+ onTrendModeChange: handleTrendModeChange,
75955
+ onSelectedLineIdsChange: handleSelectedLineIdsChange
75808
75956
  }
75809
75957
  ),
75810
75958
  /* @__PURE__ */ jsxs("div", { className: "p-4 sm:p-6 pb-6 max-w-[1800px] mx-auto w-full flex-1 min-h-0 overflow-y-auto flex flex-col gap-5", children: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optifye/dashboard-core",
3
- "version": "6.11.7",
3
+ "version": "6.11.9",
4
4
  "description": "Reusable UI & logic for Optifye dashboard",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",