@optifye/dashboard-core 6.11.11 → 6.11.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -11,8 +11,8 @@ var events = require('events');
11
11
  var supabaseJs = require('@supabase/supabase-js');
12
12
  var Hls = require('hls.js');
13
13
  var useSWR = require('swr');
14
- var motionUtils = require('motion-utils');
15
14
  var lucideReact = require('lucide-react');
15
+ var motionUtils = require('motion-utils');
16
16
  var recharts = require('recharts');
17
17
  var reactSlot = require('@radix-ui/react-slot');
18
18
  var SelectPrimitive = require('@radix-ui/react-select');
@@ -3446,6 +3446,25 @@ function isValidFactoryViewConfiguration(entityConfig) {
3446
3446
  return lineIds.length > 0;
3447
3447
  }
3448
3448
 
3449
+ // src/lib/constants/videoGridMetricMode.ts
3450
+ var VALID_VIDEO_GRID_METRIC_MODES = /* @__PURE__ */ new Set([
3451
+ "efficiency",
3452
+ "recent_flow",
3453
+ "recent_flow_wip_gated"
3454
+ ]);
3455
+ var normalizeVideoGridMetricMode = (value, assemblyEnabled = false) => {
3456
+ const normalized = typeof value === "string" ? value.trim().toLowerCase() : "";
3457
+ if (VALID_VIDEO_GRID_METRIC_MODES.has(normalized)) {
3458
+ return normalized;
3459
+ }
3460
+ return assemblyEnabled ? "recent_flow_wip_gated" : "efficiency";
3461
+ };
3462
+ var isRecentFlowVideoGridMetricMode = (value, assemblyEnabled = false) => {
3463
+ const normalized = normalizeVideoGridMetricMode(value, assemblyEnabled);
3464
+ return normalized === "recent_flow" || normalized === "recent_flow_wip_gated";
3465
+ };
3466
+ var isWipGatedVideoGridMetricMode = (value, assemblyEnabled = false) => normalizeVideoGridMetricMode(value, assemblyEnabled) === "recent_flow_wip_gated";
3467
+
3449
3468
  // src/lib/services/lineMetricsSelection.ts
3450
3469
  var toTimestamp = (value) => {
3451
3470
  if (typeof value !== "string") return 0;
@@ -3907,7 +3926,7 @@ var dashboardService = {
3907
3926
  company_name: data.company_name || "Company X",
3908
3927
  date: data.date,
3909
3928
  shift_id: data.shift_id,
3910
- action_name: data.action_name || "",
3929
+ action_name: data.action_display_name || data.action_name || "",
3911
3930
  shift_start: data.shift_start || (data.shift_id === (shiftConfig.dayShift?.id ?? 0) ? shiftConfig.dayShift?.startTime : shiftConfig.nightShift?.startTime) || "00:00",
3912
3931
  shift_end: data.shift_end || (data.shift_id === (shiftConfig.dayShift?.id ?? 0) ? shiftConfig.dayShift?.endTime : shiftConfig.nightShift?.endTime) || "00:00",
3913
3932
  shift_type: data.shift_type || (data.shift_id === (shiftConfig.dayShift?.id ?? 0) ? "Day" : "Night"),
@@ -3976,7 +3995,9 @@ var dashboardService = {
3976
3995
  company_id,
3977
3996
  companies!lines_company_id_fkey(company_name:name),
3978
3997
  enable,
3979
- monitoring_mode
3998
+ monitoring_mode,
3999
+ assembly,
4000
+ video_grid_metric_mode
3980
4001
  `).eq("enable", true);
3981
4002
  if (companyId) {
3982
4003
  query = query.eq("company_id", companyId);
@@ -3996,7 +4017,12 @@ var dashboardService = {
3996
4017
  company_name: line.companies?.company_name ?? "N/A",
3997
4018
  enable: line.enable ?? true,
3998
4019
  // Default to true if not specified
3999
- monitoring_mode: line.monitoring_mode ?? "output"
4020
+ monitoring_mode: line.monitoring_mode ?? "output",
4021
+ assembly: line.assembly ?? false,
4022
+ video_grid_metric_mode: normalizeVideoGridMetricMode(
4023
+ line.video_grid_metric_mode,
4024
+ line.assembly ?? false
4025
+ )
4000
4026
  }));
4001
4027
  return transformedLines;
4002
4028
  } catch (err) {
@@ -4033,7 +4059,7 @@ var dashboardService = {
4033
4059
  }
4034
4060
  const lineIdsToQuery = configuredLineIds;
4035
4061
  const [line1Result, metricsResult2] = await Promise.all([
4036
- supabase.from(linesTable).select("id, line_name, factory_id, monitoring_mode, factories!lines_factory_id_fkey(factory_name), company_id, companies!lines_company_id_fkey(company_name:name)").eq("id", defaultLineId).single(),
4062
+ supabase.from(linesTable).select("id, line_name, factory_id, monitoring_mode, assembly, video_grid_metric_mode, factories!lines_factory_id_fkey(factory_name), company_id, companies!lines_company_id_fkey(company_name:name)").eq("id", defaultLineId).single(),
4037
4063
  supabase.from(lineMetricsTable).select("*").in("line_id", lineIdsToQuery).eq("shift_id", queryShiftId).eq("date", queryDate)
4038
4064
  ]);
4039
4065
  if (line1Result.error) throw line1Result.error;
@@ -4112,7 +4138,7 @@ var dashboardService = {
4112
4138
  throw new Error("Company ID must be configured for detailed line requests.");
4113
4139
  }
4114
4140
  const [lineResult, metricsResult] = await Promise.all([
4115
- supabase.from(linesTable).select("id, line_name, factory_id, monitoring_mode, factories!lines_factory_id_fkey(factory_name), company_id, companies!lines_company_id_fkey(company_name:name)").eq("id", lineIdToQuery).single(),
4141
+ supabase.from(linesTable).select("id, line_name, factory_id, monitoring_mode, assembly, video_grid_metric_mode, factories!lines_factory_id_fkey(factory_name), company_id, companies!lines_company_id_fkey(company_name:name)").eq("id", lineIdToQuery).single(),
4116
4142
  supabase.from(lineMetricsTable).select("*").eq("line_id", lineIdToQuery).eq("shift_id", queryShiftId).eq("date", queryDate)
4117
4143
  ]);
4118
4144
  if (lineResult.error) throw lineResult.error;
@@ -4928,25 +4954,25 @@ var workspaceService = {
4928
4954
  if (!configuredFactoryId) {
4929
4955
  throw new Error("Factory ID (entityConfig.factoryId) must be configured to update line thresholds.");
4930
4956
  }
4931
- let outputWorkspaces = workspaces.filter((ws) => ws.action_type === "packaging");
4957
+ let outputWorkspaces = workspaces.filter((ws) => ws.action_type === "output");
4932
4958
  if (outputWorkspaces.length === 0) {
4933
- const packagingKeywords = ["packaging", "package", "packing", "pack"];
4934
- const possiblePackagingWorkspaces = workspaces.filter((ws) => {
4959
+ const outputKeywords = ["output", "packaging", "package", "packing", "pack"];
4960
+ const possibleOutputWorkspaces = workspaces.filter((ws) => {
4935
4961
  if (ws.workspace_id && typeof ws.workspace_id === "string") {
4936
4962
  const wsIdLower = ws.workspace_id.toLowerCase();
4937
- return packagingKeywords.some((keyword) => wsIdLower.includes(keyword));
4963
+ return outputKeywords.some((keyword) => wsIdLower.includes(keyword));
4938
4964
  }
4939
4965
  return false;
4940
4966
  });
4941
- if (possiblePackagingWorkspaces.length > 0) {
4942
- outputWorkspaces = possiblePackagingWorkspaces;
4967
+ if (possibleOutputWorkspaces.length > 0) {
4968
+ outputWorkspaces = possibleOutputWorkspaces;
4943
4969
  } else {
4944
- console.warn("[WorkspaceService] Unable to determine packaging workspaces for threshold calculation. Using all workspaces.");
4970
+ console.warn("[WorkspaceService] Unable to determine output-family workspaces for threshold calculation. Using all workspaces.");
4945
4971
  outputWorkspaces = workspaces;
4946
4972
  }
4947
4973
  }
4948
4974
  if (outputWorkspaces.length === 0 && workspaces.length > 0) {
4949
- console.warn('[WorkspaceService] No workspaces with action_type "packaging" found for line threshold calculation. Thresholds might be zero.');
4975
+ console.warn('[WorkspaceService] No workspaces with action_type "output" found for line threshold calculation. Thresholds might be zero.');
4950
4976
  }
4951
4977
  const totalDayOutput = outputWorkspaces.reduce((sum, ws) => sum + (ws.action_total_day_output || 0), 0);
4952
4978
  const totalPPH = outputWorkspaces.reduce((sum, ws) => sum + (ws.action_pph_threshold || 0), 0);
@@ -8466,7 +8492,11 @@ var LinesService = class {
8466
8492
  createdAt: line.created_at,
8467
8493
  factoryId: line.factory_id,
8468
8494
  monitoringMode: line.monitoring_mode ?? "output",
8469
- assembly: line.assembly ?? false
8495
+ assembly: line.assembly ?? false,
8496
+ videoGridMetricMode: normalizeVideoGridMetricMode(
8497
+ line.video_grid_metric_mode,
8498
+ line.assembly ?? false
8499
+ )
8470
8500
  }));
8471
8501
  } catch (error) {
8472
8502
  console.error("Error fetching lines:", error);
@@ -8510,7 +8540,11 @@ var LinesService = class {
8510
8540
  createdAt: line.created_at,
8511
8541
  factoryId: line.factory_id,
8512
8542
  monitoringMode: line.monitoring_mode ?? "output",
8513
- assembly: line.assembly ?? false
8543
+ assembly: line.assembly ?? false,
8544
+ videoGridMetricMode: normalizeVideoGridMetricMode(
8545
+ line.video_grid_metric_mode,
8546
+ line.assembly ?? false
8547
+ )
8514
8548
  }));
8515
8549
  } catch (error) {
8516
8550
  console.error("Error fetching all lines:", error);
@@ -8562,7 +8596,12 @@ var LinesService = class {
8562
8596
  isActive: data.enable,
8563
8597
  createdAt: data.created_at,
8564
8598
  factoryId: data.factory_id,
8565
- monitoringMode: data.monitoring_mode ?? "output"
8599
+ monitoringMode: data.monitoring_mode ?? "output",
8600
+ assembly: data.assembly ?? false,
8601
+ videoGridMetricMode: normalizeVideoGridMetricMode(
8602
+ data.video_grid_metric_mode,
8603
+ data.assembly ?? false
8604
+ )
8566
8605
  };
8567
8606
  } catch (error) {
8568
8607
  console.error("Error fetching line:", error);
@@ -9703,78 +9742,6 @@ var createStorageService = (supabase) => ({
9703
9742
  }
9704
9743
  });
9705
9744
 
9706
- // src/lib/constants/idleTimeColors.ts
9707
- var IDLE_TIME_REASON_COLORS = {
9708
- "Operator Absent": {
9709
- hex: "#dc2626",
9710
- // red-600 - Critical/Urgent
9711
- text: "text-red-600",
9712
- bg: "bg-red-50",
9713
- border: "border-red-200"
9714
- },
9715
- "No Material": {
9716
- hex: "#f59e0b",
9717
- // amber-500 - Warning/Supply Chain
9718
- text: "text-amber-600",
9719
- bg: "bg-amber-50",
9720
- border: "border-amber-200"
9721
- },
9722
- "Machine Downtime": {
9723
- hex: "#3b82f6",
9724
- // blue-500 - Scheduled/Technical
9725
- text: "text-blue-600",
9726
- bg: "bg-blue-50",
9727
- border: "border-blue-200"
9728
- },
9729
- "Operator Idle": {
9730
- hex: "#8b5cf6",
9731
- // violet-500 - Low Priority/Behavioral
9732
- text: "text-violet-600",
9733
- bg: "bg-violet-50",
9734
- border: "border-violet-200"
9735
- }
9736
- };
9737
- var FALLBACK_HEX_COLORS = [
9738
- "#dc2626",
9739
- // red-600
9740
- "#ea580c",
9741
- // orange-600
9742
- "#ca8a04",
9743
- // yellow-600
9744
- "#16a34a",
9745
- // green-600
9746
- "#0891b2",
9747
- // cyan-600
9748
- "#2563eb",
9749
- // blue-600
9750
- "#7c3aed",
9751
- // violet-600
9752
- "#c026d3"
9753
- // fuchsia-600
9754
- ];
9755
- var DEFAULT_CONFIG3 = {
9756
- hex: "#9ca3af",
9757
- text: "text-gray-500",
9758
- bg: "bg-gray-50",
9759
- border: "border-gray-200"
9760
- };
9761
- function getIdleTimeReasonColor(reason, index = 0) {
9762
- const normalizedReason = reason.includes("_") ? reason.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ") : reason;
9763
- if (IDLE_TIME_REASON_COLORS[normalizedReason]) {
9764
- return IDLE_TIME_REASON_COLORS[normalizedReason];
9765
- }
9766
- const lowerReason = normalizedReason.toLowerCase();
9767
- const foundKey = Object.keys(IDLE_TIME_REASON_COLORS).find((k) => k.toLowerCase() === lowerReason);
9768
- if (foundKey) {
9769
- return IDLE_TIME_REASON_COLORS[foundKey];
9770
- }
9771
- const fallbackHex = FALLBACK_HEX_COLORS[index % FALLBACK_HEX_COLORS.length];
9772
- return {
9773
- ...DEFAULT_CONFIG3,
9774
- hex: fallbackHex
9775
- };
9776
- }
9777
-
9778
9745
  // src/lib/services/idleTimeReasonService.ts
9779
9746
  async function fetchIdleTimeReasons(params) {
9780
9747
  const { workspaceId, lineId, date, shiftId, startDate, endDate, token } = params;
@@ -9832,7 +9799,12 @@ async function fetchIdleTimeReasons(params) {
9832
9799
  }
9833
9800
  function transformToChartData(data) {
9834
9801
  return data.reasons.map((reason) => ({
9835
- name: formatReasonLabel(reason.reason),
9802
+ name: reason.display_name || formatReasonLabel(reason.reason_key || reason.reason),
9803
+ reasonKey: reason.reason_key || reason.reason,
9804
+ displayName: reason.display_name || formatReasonLabel(reason.reason_key || reason.reason),
9805
+ paletteToken: reason.palette_token,
9806
+ iconToken: reason.icon_token,
9807
+ isKnown: reason.is_known,
9836
9808
  value: reason.percentage,
9837
9809
  totalDurationSeconds: reason.total_duration_seconds,
9838
9810
  efficiencyLossPercentage: reason.efficiency_loss_percentage ?? null
@@ -9841,9 +9813,6 @@ function transformToChartData(data) {
9841
9813
  function formatReasonLabel(reason) {
9842
9814
  return reason.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
9843
9815
  }
9844
- function getReasonColor(reason, index = 0) {
9845
- return getIdleTimeReasonColor(reason, index).hex;
9846
- }
9847
9816
 
9848
9817
  // src/lib/services/sessionTracker.ts
9849
9818
  function generateSessionId() {
@@ -11566,6 +11535,52 @@ var workspaceMetricsStore = {
11566
11535
  }
11567
11536
  };
11568
11537
 
11538
+ // src/lib/constants/actions.ts
11539
+ var ACTION_NAMES = {
11540
+ /** Assembly operations */
11541
+ ASSEMBLY: "Assembly",
11542
+ /** Packaging operations */
11543
+ PACKAGING: "Packaging",
11544
+ /** Output operations (user-facing label for the legacy packaging family) */
11545
+ OUTPUT: "Output",
11546
+ /** Inspection operations */
11547
+ INSPECTION: "Inspection",
11548
+ /** Testing operations */
11549
+ TESTING: "Testing",
11550
+ /** Quality control operations */
11551
+ QUALITY_CONTROL: "Quality Control"
11552
+ };
11553
+ var ACTION_FAMILIES = {
11554
+ ASSEMBLY: "assembly",
11555
+ OUTPUT: "output",
11556
+ OTHER: "other"
11557
+ };
11558
+ var normalizeActionFamily = (params) => {
11559
+ const explicitValue = (params.actionFamily ?? params.actionType ?? "").trim().toLowerCase();
11560
+ if (explicitValue === ACTION_FAMILIES.ASSEMBLY) return ACTION_FAMILIES.ASSEMBLY;
11561
+ if (explicitValue === ACTION_FAMILIES.OUTPUT || explicitValue === "packaging") {
11562
+ return ACTION_FAMILIES.OUTPUT;
11563
+ }
11564
+ if (explicitValue === ACTION_FAMILIES.OTHER) return ACTION_FAMILIES.OTHER;
11565
+ const normalizedName = (params.actionName ?? "").trim().toLowerCase();
11566
+ if (!normalizedName) return null;
11567
+ if (normalizedName === "output" || normalizedName.includes("packag")) {
11568
+ return ACTION_FAMILIES.OUTPUT;
11569
+ }
11570
+ if (normalizedName.includes("assembly")) {
11571
+ return ACTION_FAMILIES.ASSEMBLY;
11572
+ }
11573
+ return ACTION_FAMILIES.OTHER;
11574
+ };
11575
+ var getActionDisplayName = (params) => {
11576
+ const explicitDisplayName = (params.displayName ?? "").trim();
11577
+ if (explicitDisplayName) return explicitDisplayName;
11578
+ const family = normalizeActionFamily(params);
11579
+ if (family === ACTION_FAMILIES.OUTPUT) return ACTION_NAMES.OUTPUT;
11580
+ if (family === ACTION_FAMILIES.ASSEMBLY) return ACTION_NAMES.ASSEMBLY;
11581
+ return (params.actionName ?? "").trim();
11582
+ };
11583
+
11569
11584
  // src/lib/utils/workspaceMetricsTransform.ts
11570
11585
  var coerceNumber = (value, fallback = 0) => {
11571
11586
  const num = typeof value === "number" ? value : Number(value);
@@ -11755,10 +11770,17 @@ var toWorkspaceDetailedMetrics = ({
11755
11770
  const targetOutput = coerceNumber(data.target_output ?? data.total_day_output, 0);
11756
11771
  const idealOutput = coerceNumber(data.ideal_output ?? data.ideal_output_until_now, 0);
11757
11772
  const outputDifference = totalActions - idealOutput;
11773
+ const hourlyCycleTimes = Array.isArray(data.hourly_cycle_times) ? data.hourly_cycle_times.map((value) => coerceNumber(value, 0)) : [];
11758
11774
  const totalWorkspacesValue = coerceNumber(
11759
11775
  data.total_workspaces ?? lineMetricsById?.[data.line_id || ""]?.total_workspaces ?? workspaceConfig.totalWorkspaces,
11760
11776
  0
11761
11777
  );
11778
+ const actionFamily = normalizeActionFamily({
11779
+ actionFamily: data.action_family,
11780
+ actionType: data.action_type,
11781
+ actionName: data.action_name
11782
+ });
11783
+ const actionType = actionFamily === "assembly" || actionFamily === "output" ? actionFamily : null;
11762
11784
  return {
11763
11785
  workspace_id: data.workspace_id,
11764
11786
  workspace_name: data.workspace_name,
@@ -11769,8 +11791,20 @@ var toWorkspaceDetailedMetrics = ({
11769
11791
  company_name: data.company_name || "",
11770
11792
  date: data.date,
11771
11793
  shift_id: shiftId,
11772
- action_name: data.action_name || "",
11773
- action_type: data.action_type === "assembly" || data.action_type === "packaging" ? data.action_type : null,
11794
+ action_name: getActionDisplayName({
11795
+ displayName: data.action_display_name,
11796
+ actionFamily: data.action_family,
11797
+ actionType: data.action_type,
11798
+ actionName: data.action_name
11799
+ }),
11800
+ action_type: actionType,
11801
+ action_family: actionFamily,
11802
+ action_display_name: getActionDisplayName({
11803
+ displayName: data.action_display_name,
11804
+ actionFamily: data.action_family,
11805
+ actionType: data.action_type,
11806
+ actionName: data.action_name
11807
+ }),
11774
11808
  shift_start: shiftStart,
11775
11809
  shift_end: shiftEnd,
11776
11810
  shift_type: shiftType,
@@ -11783,6 +11817,7 @@ var toWorkspaceDetailedMetrics = ({
11783
11817
  avg_efficiency: coerceNumber(data.efficiency ?? data.avg_efficiency, 0),
11784
11818
  total_actions: totalActions,
11785
11819
  hourly_action_counts: hourlyActionCounts,
11820
+ hourly_cycle_times: hourlyCycleTimes,
11786
11821
  workspace_rank: coerceNumber(data.workspace_rank, 0),
11787
11822
  total_workspaces: totalWorkspacesValue,
11788
11823
  ideal_output_until_now: idealOutput,
@@ -13321,6 +13356,12 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
13321
13356
  const transformedWorkspaceData = allWorkspaceMetrics.map((item) => {
13322
13357
  const idleTimeValue = typeof item.idle_time === "number" ? item.idle_time : Number(item.idle_time);
13323
13358
  const idleTimeSeconds = Number.isFinite(idleTimeValue) ? idleTimeValue : void 0;
13359
+ const actionFamily = normalizeActionFamily({
13360
+ actionFamily: item.action_family,
13361
+ actionType: item.action_type,
13362
+ actionName: item.action_name
13363
+ });
13364
+ const actionType = actionFamily === "assembly" || actionFamily === "output" ? actionFamily : null;
13324
13365
  return {
13325
13366
  company_id: item.company_id || companyId,
13326
13367
  line_id: item.line_id,
@@ -13340,7 +13381,18 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
13340
13381
  monitoring_mode: item.monitoring_mode ?? void 0,
13341
13382
  idle_time: idleTimeSeconds,
13342
13383
  assembly_enabled: item.assembly_enabled ?? false,
13343
- action_type: item.action_type === "assembly" || item.action_type === "packaging" ? item.action_type : null,
13384
+ video_grid_metric_mode: normalizeVideoGridMetricMode(
13385
+ item.video_grid_metric_mode,
13386
+ item.assembly_enabled ?? false
13387
+ ),
13388
+ action_type: actionType,
13389
+ action_family: actionFamily,
13390
+ action_display_name: getActionDisplayName({
13391
+ displayName: item.action_display_name,
13392
+ actionFamily: item.action_family,
13393
+ actionType: item.action_type,
13394
+ actionName: item.action_name
13395
+ }),
13344
13396
  recent_flow_mode: item.recent_flow_mode ?? void 0,
13345
13397
  recent_flow_percent: item.recent_flow_percent ?? null,
13346
13398
  recent_flow_actual_rate_pph: item.recent_flow_actual_rate_pph ?? null,
@@ -19260,8 +19312,115 @@ function useIdleTimeReasons({
19260
19312
  refetch: fetchData
19261
19313
  };
19262
19314
  }
19315
+ var DEFAULT_PALETTE_TOKEN = "slate";
19316
+ var DEFAULT_ICON_TOKEN = "help-circle";
19317
+ var PALETTE_CONFIG = {
19318
+ red: {
19319
+ hex: "#dc2626",
19320
+ textClass: "text-red-600",
19321
+ bgClass: "bg-red-50",
19322
+ borderClass: "border-red-200"
19323
+ },
19324
+ amber: {
19325
+ hex: "#f59e0b",
19326
+ textClass: "text-amber-600",
19327
+ bgClass: "bg-amber-50",
19328
+ borderClass: "border-amber-200"
19329
+ },
19330
+ blue: {
19331
+ hex: "#3b82f6",
19332
+ textClass: "text-blue-600",
19333
+ bgClass: "bg-blue-50",
19334
+ borderClass: "border-blue-200"
19335
+ },
19336
+ violet: {
19337
+ hex: "#8b5cf6",
19338
+ textClass: "text-violet-600",
19339
+ bgClass: "bg-violet-50",
19340
+ borderClass: "border-violet-200"
19341
+ },
19342
+ emerald: {
19343
+ hex: "#10b981",
19344
+ textClass: "text-emerald-600",
19345
+ bgClass: "bg-emerald-50",
19346
+ borderClass: "border-emerald-200"
19347
+ },
19348
+ cyan: {
19349
+ hex: "#0891b2",
19350
+ textClass: "text-cyan-600",
19351
+ bgClass: "bg-cyan-50",
19352
+ borderClass: "border-cyan-200"
19353
+ },
19354
+ slate: {
19355
+ hex: "#64748b",
19356
+ textClass: "text-slate-600",
19357
+ bgClass: "bg-slate-50",
19358
+ borderClass: "border-slate-200"
19359
+ }
19360
+ };
19361
+ var ICON_CONFIG = {
19362
+ "alert-triangle": lucideReact.AlertTriangle,
19363
+ "refresh-cw": lucideReact.RefreshCw,
19364
+ package: lucideReact.Package,
19365
+ clock: lucideReact.Clock,
19366
+ "user-x": lucideReact.UserX,
19367
+ wrench: lucideReact.Wrench,
19368
+ activity: lucideReact.Activity,
19369
+ "help-circle": lucideReact.HelpCircle
19370
+ };
19371
+ var humanizeIdleReasonLabel = (value) => {
19372
+ const text = String(value || "").trim();
19373
+ if (!text) {
19374
+ return "Unknown";
19375
+ }
19376
+ return text.replace(/_/g, " ").split(/\s+/).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
19377
+ };
19378
+ var normalizePaletteToken = (value) => {
19379
+ const token = String(value || "").trim().toLowerCase();
19380
+ if (token in PALETTE_CONFIG) {
19381
+ return token;
19382
+ }
19383
+ return DEFAULT_PALETTE_TOKEN;
19384
+ };
19385
+ var normalizeIconToken = (value) => {
19386
+ const token = String(value || "").trim().toLowerCase();
19387
+ if (token in ICON_CONFIG) {
19388
+ return token;
19389
+ }
19390
+ return DEFAULT_ICON_TOKEN;
19391
+ };
19392
+ var getIdleReasonPresentation = (metadata) => {
19393
+ const label = typeof metadata?.label === "string" && metadata.label.trim() ? metadata.label.trim() : null;
19394
+ const displayName = typeof metadata?.displayName === "string" && metadata.displayName.trim() ? metadata.displayName.trim() : humanizeIdleReasonLabel(label);
19395
+ const paletteToken = normalizePaletteToken(metadata?.paletteToken);
19396
+ const iconToken = normalizeIconToken(metadata?.iconToken);
19397
+ return {
19398
+ label,
19399
+ displayName,
19400
+ paletteToken,
19401
+ iconToken,
19402
+ isKnown: Boolean(metadata?.isKnown),
19403
+ Icon: ICON_CONFIG[iconToken],
19404
+ ...PALETTE_CONFIG[paletteToken]
19405
+ };
19406
+ };
19407
+ var getIdleReasonHexColor = (metadata) => getIdleReasonPresentation(metadata).hex;
19263
19408
 
19264
19409
  // src/lib/services/clipClassificationService.ts
19410
+ var normalizeClassification = (value) => {
19411
+ const rawLabel = typeof value?.label === "string" ? value.label.trim() : "";
19412
+ const label = rawLabel || void 0;
19413
+ const parsedConfidence = typeof value?.confidence === "number" ? value.confidence : typeof value?.confidence === "string" ? Number(value.confidence) : void 0;
19414
+ return {
19415
+ status: value?.status === "classified" ? "classified" : "processing",
19416
+ label,
19417
+ displayName: typeof value?.display_name === "string" ? value.display_name : label ? humanizeIdleReasonLabel(label) : void 0,
19418
+ paletteToken: typeof value?.palette_token === "string" ? value.palette_token : void 0,
19419
+ iconToken: typeof value?.icon_token === "string" ? value.icon_token : void 0,
19420
+ isKnown: typeof value?.is_known === "boolean" ? value.is_known : void 0,
19421
+ confidence: parsedConfidence !== void 0 && Number.isFinite(parsedConfidence) ? parsedConfidence : void 0
19422
+ };
19423
+ };
19265
19424
  function parseCleanLabel(rawLabel) {
19266
19425
  if (!rawLabel) return null;
19267
19426
  const patterns = [
@@ -19315,7 +19474,13 @@ async function fetchClassifications(clipIds, token) {
19315
19474
  );
19316
19475
  }
19317
19476
  const data = await response.json();
19318
- return data.classifications || {};
19477
+ const rawClassifications = data.classifications || {};
19478
+ return Object.fromEntries(
19479
+ Object.entries(rawClassifications).map(([clipId, classification]) => [
19480
+ clipId,
19481
+ normalizeClassification(classification)
19482
+ ])
19483
+ );
19319
19484
  } catch (error) {
19320
19485
  console.error("Error fetching chunk:", error);
19321
19486
  return Object.fromEntries(
@@ -19356,21 +19521,30 @@ function useClassificationRealtimeUpdates({
19356
19521
  },
19357
19522
  (payload) => {
19358
19523
  console.log("[useClassificationRealtimeUpdates] New classification:", payload);
19359
- try {
19360
- const { clip_id, clip_label, confidence_score } = payload.new;
19361
- const parsedLabel = parseCleanLabel(clip_label);
19362
- if (parsedLabel) {
19363
- onClassificationUpdate(clip_id, {
19364
- status: "classified",
19365
- label: parsedLabel,
19366
- confidence: confidence_score || 0
19367
- });
19368
- } else {
19369
- console.warn("[useClassificationRealtimeUpdates] Failed to parse label:", clip_label);
19524
+ void (async () => {
19525
+ try {
19526
+ const { clip_id, clip_label, confidence_score } = payload.new;
19527
+ const token = await getAccessTokenOrRedirect(supabase, { redirectReason: "session_expired" });
19528
+ const fetched = await fetchClassifications([clip_id], token);
19529
+ const classification = fetched[clip_id];
19530
+ if (classification) {
19531
+ onClassificationUpdate(clip_id, classification);
19532
+ return;
19533
+ }
19534
+ const parsedLabel = parseCleanLabel(clip_label);
19535
+ if (parsedLabel) {
19536
+ onClassificationUpdate(clip_id, {
19537
+ status: "classified",
19538
+ label: parsedLabel,
19539
+ confidence: confidence_score || 0
19540
+ });
19541
+ } else {
19542
+ console.warn("[useClassificationRealtimeUpdates] Failed to parse label:", clip_label);
19543
+ }
19544
+ } catch (error) {
19545
+ console.error("[useClassificationRealtimeUpdates] Error processing update:", error);
19370
19546
  }
19371
- } catch (error) {
19372
- console.error("[useClassificationRealtimeUpdates] Error processing update:", error);
19373
- }
19547
+ })();
19374
19548
  }
19375
19549
  ).subscribe((status) => {
19376
19550
  console.log(`[useClassificationRealtimeUpdates] Subscription status:`, status);
@@ -32402,90 +32576,99 @@ var CycleTimeOverTimeChart = ({
32402
32576
  data,
32403
32577
  idealCycleTime,
32404
32578
  shiftStart,
32405
- className = ""
32579
+ shiftEnd,
32580
+ xAxisMode = "recent",
32581
+ datasetKey,
32582
+ className = "",
32583
+ showIdleTime = false,
32584
+ idleTimeData = []
32406
32585
  }) => {
32407
32586
  const MAX_DATA_POINTS = 40;
32408
32587
  const containerRef = React141__namespace.default.useRef(null);
32409
32588
  const [containerReady, setContainerReady] = React141__namespace.default.useState(false);
32410
- const getHourFromTimeString = (timeStr) => {
32411
- const [hours, minutes] = timeStr.split(":");
32412
- return parseInt(hours);
32589
+ const parseTimeToMinutes3 = (value) => {
32590
+ const [hours, minutes] = value.split(":").map(Number);
32591
+ if (!Number.isFinite(hours) || !Number.isFinite(minutes)) return 0;
32592
+ return hours * 60 + minutes;
32413
32593
  };
32414
- getHourFromTimeString(shiftStart);
32415
- const getDisplayData = (rawData) => {
32416
- return rawData.slice(Math.max(0, rawData.length - MAX_DATA_POINTS));
32594
+ const formatHourLabel = (slotIndex) => {
32595
+ const baseMinutes = parseTimeToMinutes3(shiftStart);
32596
+ const absoluteMinutes = baseMinutes + slotIndex * 60;
32597
+ const hour24 = Math.floor(absoluteMinutes % (24 * 60) / 60);
32598
+ const ampm = hour24 >= 12 ? "PM" : "AM";
32599
+ const hour12 = hour24 % 12 || 12;
32600
+ return `${hour12}${ampm}`;
32601
+ };
32602
+ const formatHourRangeLabel = (slotIndex) => {
32603
+ const startLabel = formatHourLabel(slotIndex);
32604
+ const endLabel = shiftEnd && slotIndex === DURATION - 1 ? formatHourLabel(slotIndex + 1) : formatHourLabel(slotIndex + 1);
32605
+ return `${startLabel} - ${endLabel}`;
32417
32606
  };
32607
+ const getDisplayData = React141__namespace.default.useCallback((rawData) => {
32608
+ if (xAxisMode === "hourly") return rawData;
32609
+ return rawData.slice(Math.max(0, rawData.length - MAX_DATA_POINTS));
32610
+ }, [xAxisMode]);
32418
32611
  const displayData = getDisplayData(data);
32419
32612
  const DURATION = displayData.length;
32420
- const [animatedData, setAnimatedData] = React141__namespace.default.useState(Array(DURATION).fill(0));
32421
- const prevDataRef = React141__namespace.default.useRef(Array(DURATION).fill(0));
32422
- const animationFrameRef = React141__namespace.default.useRef(null);
32423
- const animateToNewData = React141__namespace.default.useCallback((targetData) => {
32424
- const startData = [...prevDataRef.current];
32425
- const startTime = performance.now();
32426
- const duration = 1200;
32427
- const animate = (currentTime) => {
32428
- const elapsed = currentTime - startTime;
32429
- const progress7 = Math.min(elapsed / duration, 1);
32430
- const easeOutQuint = (t) => 1 - Math.pow(1 - t, 5);
32431
- const easedProgress = easeOutQuint(progress7);
32432
- const newData = startData.map((start, index) => {
32433
- const target = targetData[index] || 0;
32434
- const change = target - start;
32435
- const scaleFactor = Math.min(1, 50 / Math.abs(change || 1));
32436
- const adjustedEasing = progress7 * (scaleFactor + (1 - scaleFactor) * easedProgress);
32437
- return start + change * adjustedEasing;
32438
- });
32439
- setAnimatedData(newData);
32440
- prevDataRef.current = newData;
32441
- if (progress7 < 1) {
32442
- animationFrameRef.current = requestAnimationFrame(animate);
32443
- } else {
32444
- setAnimatedData(targetData);
32445
- prevDataRef.current = targetData;
32446
- }
32447
- };
32448
- if (animationFrameRef.current) {
32449
- cancelAnimationFrame(animationFrameRef.current);
32450
- }
32451
- animationFrameRef.current = requestAnimationFrame(animate);
32452
- }, []);
32613
+ const effectiveDatasetKey = datasetKey || `cycle-time:${xAxisMode}`;
32614
+ const [animatedDatasetKey, setAnimatedDatasetKey] = React141__namespace.default.useState(null);
32615
+ const shouldAnimate = animatedDatasetKey !== effectiveDatasetKey;
32616
+ const handleAnimationEnd = React141__namespace.default.useCallback(() => {
32617
+ setAnimatedDatasetKey((currentValue) => currentValue === effectiveDatasetKey ? currentValue : effectiveDatasetKey);
32618
+ }, [effectiveDatasetKey]);
32619
+ const finalData = displayData;
32453
32620
  React141__namespace.default.useEffect(() => {
32454
- if (JSON.stringify(data) !== JSON.stringify(prevDataRef.current)) {
32455
- const processedData = getDisplayData(data);
32456
- animateToNewData(processedData);
32621
+ const containerNode = containerRef.current;
32622
+ if (!containerNode) {
32623
+ setContainerReady(true);
32624
+ return void 0;
32457
32625
  }
32458
- return () => {
32459
- if (animationFrameRef.current) {
32460
- cancelAnimationFrame(animationFrameRef.current);
32461
- }
32462
- };
32463
- }, [data, animateToNewData]);
32464
- React141__namespace.default.useEffect(() => {
32626
+ let frameId = null;
32627
+ let resizeObserver = null;
32465
32628
  const checkContainerDimensions = () => {
32466
- if (containerRef.current) {
32467
- const rect = containerRef.current.getBoundingClientRect();
32468
- if (rect.width > 0 && rect.height > 0) {
32469
- setContainerReady(true);
32470
- }
32629
+ const rect = containerNode.getBoundingClientRect();
32630
+ const isReady = rect.width > 0 && rect.height > 0;
32631
+ if (isReady) {
32632
+ setContainerReady(true);
32471
32633
  }
32634
+ return isReady;
32472
32635
  };
32473
- checkContainerDimensions();
32474
- const resizeObserver = new ResizeObserver(checkContainerDimensions);
32475
- if (containerRef.current) {
32476
- resizeObserver.observe(containerRef.current);
32636
+ if (checkContainerDimensions()) {
32637
+ return void 0;
32477
32638
  }
32478
- const fallbackTimeout = setTimeout(() => {
32639
+ frameId = window.requestAnimationFrame(() => {
32640
+ checkContainerDimensions();
32641
+ });
32642
+ if (typeof ResizeObserver !== "undefined") {
32643
+ resizeObserver = new ResizeObserver(() => {
32644
+ if (checkContainerDimensions() && resizeObserver) {
32645
+ resizeObserver.disconnect();
32646
+ resizeObserver = null;
32647
+ }
32648
+ });
32649
+ resizeObserver.observe(containerNode);
32650
+ } else {
32479
32651
  setContainerReady(true);
32480
- }, 100);
32652
+ }
32481
32653
  return () => {
32482
- resizeObserver.disconnect();
32483
- clearTimeout(fallbackTimeout);
32654
+ if (frameId !== null) {
32655
+ window.cancelAnimationFrame(frameId);
32656
+ }
32657
+ resizeObserver?.disconnect();
32484
32658
  };
32485
32659
  }, []);
32660
+ const labelInterval = React141__namespace.default.useMemo(() => {
32661
+ if (xAxisMode === "hourly") {
32662
+ return Math.max(1, Math.ceil(DURATION / 8));
32663
+ }
32664
+ return 5;
32665
+ }, [xAxisMode, DURATION]);
32486
32666
  const formatTimeLabel = (dataIndex) => {
32487
32667
  if (DURATION === 0) return "";
32488
- if (dataIndex % 5 !== 0 && dataIndex !== DURATION - 1) return "";
32668
+ if (dataIndex % labelInterval !== 0 && dataIndex !== DURATION - 1) return "";
32669
+ if (xAxisMode === "hourly") {
32670
+ return formatHourLabel(dataIndex);
32671
+ }
32489
32672
  const timeAgo = (DURATION - 1 - dataIndex) * 90;
32490
32673
  const minutes = Math.floor(timeAgo / 60);
32491
32674
  const seconds = timeAgo % 60;
@@ -32499,6 +32682,9 @@ var CycleTimeOverTimeChart = ({
32499
32682
  };
32500
32683
  const formatTooltipTime = (dataIndex) => {
32501
32684
  if (DURATION === 0) return "";
32685
+ if (xAxisMode === "hourly") {
32686
+ return formatHourRangeLabel(dataIndex);
32687
+ }
32502
32688
  const timeAgo = (DURATION - 1 - dataIndex) * 90;
32503
32689
  const minutes = Math.floor(timeAgo / 60);
32504
32690
  const seconds = timeAgo % 60;
@@ -32512,39 +32698,40 @@ var CycleTimeOverTimeChart = ({
32512
32698
  return `${minutes} minutes ${seconds} seconds ago`;
32513
32699
  }
32514
32700
  };
32515
- const chartData = Array.from({ length: DURATION }, (_, i) => {
32701
+ const chartData = React141__namespace.default.useMemo(() => Array.from({ length: DURATION }, (_, i) => {
32516
32702
  return {
32517
32703
  timeIndex: i,
32518
32704
  label: formatTimeLabel(i),
32519
32705
  tooltip: formatTooltipTime(i),
32520
- cycleTime: animatedData[i] || 0,
32521
- color: (animatedData[i] || 0) <= idealCycleTime ? "#00AB45" : "#E34329"
32706
+ cycleTime: finalData[i] || 0,
32707
+ idleMinutes: showIdleTime && idleTimeData && idleTimeData[i] || 0,
32708
+ color: (finalData[i] || 0) <= idealCycleTime ? "#00AB45" : "#E34329"
32522
32709
  };
32523
- });
32524
- const renderLegend = () => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center text-sm text-gray-600 absolute -bottom-1 left-0 right-0 bg-white py-1", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 border border-gray-100 rounded-full px-3 py-1", children: [
32525
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-8 flex items-center", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full border-t-2 border-[#E34329] border-dashed" }) }),
32526
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
32527
- "Standard: ",
32528
- idealCycleTime.toFixed(1),
32529
- " seconds"
32530
- ] })
32531
- ] }) });
32710
+ }), [DURATION, finalData, showIdleTime, idleTimeData, idealCycleTime]);
32711
+ const renderLegend = () => {
32712
+ if (!showIdleTime) return null;
32713
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-start text-[10px] font-bold text-gray-500 mb-6 tracking-[0.05em] gap-5", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
32714
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-2.5 h-2.5 rounded-full bg-[#f59e0b]" }),
32715
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Idle Time (min)" })
32716
+ ] }) });
32717
+ };
32532
32718
  return /* @__PURE__ */ jsxRuntime.jsxs(
32533
32719
  "div",
32534
32720
  {
32535
32721
  ref: containerRef,
32536
- className: `w-full h-full min-w-0 relative pb-8 ${className}`,
32722
+ className: `w-full h-full min-w-0 flex flex-col relative pb-2 ${className}`,
32537
32723
  style: { minHeight: "200px", minWidth: 0 },
32538
32724
  children: [
32539
- containerReady ? /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(
32725
+ renderLegend(),
32726
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-0 w-full", children: containerReady ? /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(
32540
32727
  recharts.LineChart,
32541
32728
  {
32542
32729
  data: chartData,
32543
32730
  margin: {
32544
- top: 25,
32731
+ top: 5,
32545
32732
  right: 30,
32546
32733
  bottom: 25,
32547
- left: 35
32734
+ left: 10
32548
32735
  },
32549
32736
  children: [
32550
32737
  /* @__PURE__ */ jsxRuntime.jsx(recharts.CartesianGrid, { strokeDasharray: "3 3", vertical: false }),
@@ -32554,17 +32741,18 @@ var CycleTimeOverTimeChart = ({
32554
32741
  dataKey: "label",
32555
32742
  tick: { fontSize: 11 },
32556
32743
  interval: 0,
32557
- angle: -30,
32558
- textAnchor: "end",
32559
- tickMargin: 15,
32560
- height: 60
32744
+ angle: xAxisMode === "hourly" ? 0 : -30,
32745
+ textAnchor: xAxisMode === "hourly" ? "middle" : "end",
32746
+ tickMargin: xAxisMode === "hourly" ? 8 : 15,
32747
+ height: xAxisMode === "hourly" ? 40 : 60
32561
32748
  }
32562
32749
  ),
32563
32750
  /* @__PURE__ */ jsxRuntime.jsx(
32564
32751
  recharts.YAxis,
32565
32752
  {
32566
32753
  tickMargin: 8,
32567
- width: 35,
32754
+ width: 45,
32755
+ yAxisId: "cycle",
32568
32756
  domain: ["auto", "auto"],
32569
32757
  ticks: [0, idealCycleTime, ...Array.from({ length: 4 }, (_, i) => (i + 1) * Math.ceil(idealCycleTime / 2))].sort((a, b) => a - b),
32570
32758
  tickFormatter: (value) => String(value),
@@ -32588,6 +32776,20 @@ var CycleTimeOverTimeChart = ({
32588
32776
  }
32589
32777
  }
32590
32778
  ),
32779
+ showIdleTime && /* @__PURE__ */ jsxRuntime.jsx(
32780
+ recharts.YAxis,
32781
+ {
32782
+ yAxisId: "idle",
32783
+ orientation: "right",
32784
+ tickMargin: 8,
32785
+ width: 35,
32786
+ domain: [0, 60],
32787
+ tickFormatter: (value) => `${value}m`,
32788
+ tick: { fontSize: 11, fill: "#f59e0b" },
32789
+ axisLine: false,
32790
+ tickLine: false
32791
+ }
32792
+ ),
32591
32793
  /* @__PURE__ */ jsxRuntime.jsx(
32592
32794
  recharts.Tooltip,
32593
32795
  {
@@ -32615,8 +32817,11 @@ var CycleTimeOverTimeChart = ({
32615
32817
  }
32616
32818
  return label;
32617
32819
  },
32618
- formatter: (value) => {
32820
+ formatter: (value, name) => {
32619
32821
  const numValue = typeof value === "number" ? value : Number(value);
32822
+ if (name === "idleMinutes") {
32823
+ return [`${numValue.toFixed(0)} minutes`, "Idle Time"];
32824
+ }
32620
32825
  return [`${numValue.toFixed(1)} seconds`, "Cycle Time"];
32621
32826
  },
32622
32827
  animationDuration: 200
@@ -32626,6 +32831,7 @@ var CycleTimeOverTimeChart = ({
32626
32831
  recharts.ReferenceLine,
32627
32832
  {
32628
32833
  y: idealCycleTime,
32834
+ yAxisId: "cycle",
32629
32835
  stroke: "#E34329",
32630
32836
  strokeDasharray: "3 3",
32631
32837
  strokeWidth: 2,
@@ -32642,6 +32848,7 @@ var CycleTimeOverTimeChart = ({
32642
32848
  recharts.Line,
32643
32849
  {
32644
32850
  type: "monotone",
32851
+ yAxisId: "cycle",
32645
32852
  dataKey: "cycleTime",
32646
32853
  stroke: "#3B82F6",
32647
32854
  strokeWidth: 2,
@@ -32691,15 +32898,35 @@ var CycleTimeOverTimeChart = ({
32691
32898
  }
32692
32899
  );
32693
32900
  },
32901
+ isAnimationActive: shouldAnimate,
32694
32902
  animationBegin: 0,
32695
- animationDuration: 1500,
32903
+ animationDuration: 1200,
32904
+ animationEasing: "ease-out",
32905
+ onAnimationEnd: handleAnimationEnd
32906
+ },
32907
+ `${effectiveDatasetKey}:cycle`
32908
+ ),
32909
+ showIdleTime && /* @__PURE__ */ jsxRuntime.jsx(
32910
+ recharts.Line,
32911
+ {
32912
+ type: "monotone",
32913
+ yAxisId: "idle",
32914
+ dataKey: "idleMinutes",
32915
+ stroke: "#f59e0b",
32916
+ strokeWidth: 2,
32917
+ strokeDasharray: "4 4",
32918
+ dot: { r: 4, fill: "#f59e0b", stroke: "#fff", strokeWidth: 1 },
32919
+ activeDot: { r: 6, fill: "#f59e0b", stroke: "#fff", strokeWidth: 2 },
32920
+ isAnimationActive: shouldAnimate,
32921
+ animationBegin: 0,
32922
+ animationDuration: 1200,
32696
32923
  animationEasing: "ease-out"
32697
- }
32924
+ },
32925
+ `${effectiveDatasetKey}:idle`
32698
32926
  )
32699
32927
  ]
32700
32928
  }
32701
- ) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full flex items-center justify-center bg-gray-50 rounded-lg", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-500 text-sm", children: "Loading chart..." }) }),
32702
- renderLegend()
32929
+ ) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full flex items-center justify-center bg-gray-50 rounded-lg", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-500 text-sm", children: "Loading chart..." }) }) })
32703
32930
  ]
32704
32931
  }
32705
32932
  );
@@ -32891,7 +33118,7 @@ var HourlyOutputChartComponent = ({
32891
33118
  if (!classification.label) {
32892
33119
  return "Reason unavailable";
32893
33120
  }
32894
- return classification.label.replace(/_/g, " ");
33121
+ return classification.displayName || classification.label.replace(/_/g, " ");
32895
33122
  }, [idleClipRanges, idleTimeClipClassifications]);
32896
33123
  const { shiftDuration, shiftEndTime, hasPartialLastHour } = React141__namespace.default.useMemo(() => {
32897
33124
  console.log("[HourlyOutputChart] Calculating shift duration with:", {
@@ -33549,23 +33776,82 @@ var HourlyOutputChart = React141__namespace.default.memo(HourlyOutputChartCompon
33549
33776
  HourlyOutputChart.displayName = "HourlyOutputChart";
33550
33777
 
33551
33778
  // src/components/dashboard/grid/videoGridMetricUtils.ts
33552
- var VIDEO_GRID_LEGEND_LABEL = "7-min Flow";
33779
+ var VIDEO_GRID_LEGEND_LABEL = "Flow";
33553
33780
  var MAP_GRID_LEGEND_LABEL = "Efficiency";
33554
33781
  var MIXED_VIDEO_GRID_LEGEND_LABEL = "Flow / Efficiency";
33555
33782
  var isFiniteNumber2 = (value) => typeof value === "number" && Number.isFinite(value);
33556
- var isAssemblyFlowEnabled = (workspace) => workspace.assembly_enabled === true;
33557
- var hasAssemblyRecentFlow = (workspace) => isAssemblyFlowEnabled(workspace) && isFiniteNumber2(workspace.recent_flow_percent);
33783
+ var isVideoGridRecentFlowEnabled = (workspace) => isRecentFlowVideoGridMetricMode(
33784
+ workspace.video_grid_metric_mode,
33785
+ workspace.assembly_enabled === true
33786
+ );
33787
+ var isVideoGridWipGated = (workspace) => isWipGatedVideoGridMetricMode(
33788
+ workspace.video_grid_metric_mode,
33789
+ workspace.assembly_enabled === true
33790
+ );
33791
+ var hasVideoGridRecentFlow = (workspace) => isVideoGridRecentFlowEnabled(workspace) && isFiniteNumber2(workspace.recent_flow_percent);
33792
+ var isVideoGridRecentFlowUnavailable = (workspace) => isVideoGridRecentFlowEnabled(workspace) && !hasVideoGridRecentFlow(workspace);
33558
33793
  var getVideoGridMetricValue = (workspace) => {
33559
33794
  const recentFlowPercent = workspace.recent_flow_percent;
33560
- if (hasAssemblyRecentFlow(workspace) && isFiniteNumber2(recentFlowPercent)) {
33795
+ if (hasVideoGridRecentFlow(workspace) && isFiniteNumber2(recentFlowPercent)) {
33561
33796
  return recentFlowPercent;
33562
33797
  }
33798
+ if (isVideoGridRecentFlowUnavailable(workspace)) {
33799
+ return null;
33800
+ }
33563
33801
  return workspace.efficiency;
33564
33802
  };
33565
33803
  var hasIncomingWipMapping = (workspace) => Boolean(workspace.incoming_wip_buffer_name);
33804
+ var getVideoGridBaseColorState = (workspace, legend = DEFAULT_EFFICIENCY_LEGEND) => {
33805
+ const metricValue = getVideoGridMetricValue(workspace);
33806
+ if (!isFiniteNumber2(metricValue)) {
33807
+ return "neutral";
33808
+ }
33809
+ return getEfficiencyColor(metricValue, legend);
33810
+ };
33811
+ var isLowWipGreenOverride = (workspace, legend = DEFAULT_EFFICIENCY_LEGEND) => {
33812
+ if (!hasVideoGridRecentFlow(workspace) || !isVideoGridWipGated(workspace)) {
33813
+ return false;
33814
+ }
33815
+ if (getVideoGridBaseColorState(workspace, legend) !== "red") {
33816
+ return false;
33817
+ }
33818
+ if (!hasIncomingWipMapping(workspace)) {
33819
+ return false;
33820
+ }
33821
+ return isFiniteNumber2(workspace.incoming_wip_current) && workspace.incoming_wip_current <= 1;
33822
+ };
33823
+ var toMinuteBucket = (minuteBucket) => Number.isFinite(minuteBucket) ? Math.floor(minuteBucket) : Math.floor(Date.now() / 6e4);
33824
+ var getEffectiveFlowMinuteBucket = (workspace) => {
33825
+ const effectiveAt = workspace.recent_flow_effective_end_at;
33826
+ if (!effectiveAt) {
33827
+ return void 0;
33828
+ }
33829
+ const timestamp = Date.parse(effectiveAt);
33830
+ if (!Number.isFinite(timestamp)) {
33831
+ return void 0;
33832
+ }
33833
+ return Math.floor(timestamp / 6e4);
33834
+ };
33835
+ var hashWorkspaceKey = (workspace) => {
33836
+ const workspaceKey = workspace.workspace_uuid || workspace.workspace_name || "unknown";
33837
+ let hash = 0;
33838
+ for (let index = 0; index < workspaceKey.length; index += 1) {
33839
+ hash = (hash * 31 + workspaceKey.charCodeAt(index)) % 2147483647;
33840
+ }
33841
+ return hash;
33842
+ };
33843
+ var getSyntheticLowWipDisplayValue = (workspace, minuteBucket) => {
33844
+ const bucket = getEffectiveFlowMinuteBucket(workspace) ?? toMinuteBucket(minuteBucket);
33845
+ const offset = (hashWorkspaceKey(workspace) % 11 + bucket % 11 + 11) % 11;
33846
+ return 100 + offset;
33847
+ };
33848
+ var getVideoGridDisplayValue = (workspace, legend = DEFAULT_EFFICIENCY_LEGEND, minuteBucket) => isLowWipGreenOverride(workspace, legend) ? getSyntheticLowWipDisplayValue(workspace, minuteBucket) : getVideoGridMetricValue(workspace);
33566
33849
  var getVideoGridColorState = (workspace, legend = DEFAULT_EFFICIENCY_LEGEND) => {
33567
- const baseColor = getEfficiencyColor(getVideoGridMetricValue(workspace), legend);
33568
- if (!hasAssemblyRecentFlow(workspace)) {
33850
+ const baseColor = getVideoGridBaseColorState(workspace, legend);
33851
+ if (!hasVideoGridRecentFlow(workspace)) {
33852
+ return baseColor;
33853
+ }
33854
+ if (!isVideoGridWipGated(workspace)) {
33569
33855
  return baseColor;
33570
33856
  }
33571
33857
  if (baseColor !== "red") {
@@ -33577,7 +33863,7 @@ var getVideoGridColorState = (workspace, legend = DEFAULT_EFFICIENCY_LEGEND) =>
33577
33863
  if (!isFiniteNumber2(workspace.incoming_wip_current)) {
33578
33864
  return "neutral";
33579
33865
  }
33580
- if (workspace.incoming_wip_current <= 1) {
33866
+ if (isLowWipGreenOverride(workspace, legend)) {
33581
33867
  return "green";
33582
33868
  }
33583
33869
  return baseColor;
@@ -33587,11 +33873,11 @@ var getVideoGridLegendLabel = (workspaces) => {
33587
33873
  if (visibleWorkspaces.length === 0) {
33588
33874
  return MAP_GRID_LEGEND_LABEL;
33589
33875
  }
33590
- const assemblyEnabledCount = visibleWorkspaces.filter(isAssemblyFlowEnabled).length;
33591
- if (assemblyEnabledCount === 0) {
33876
+ const recentFlowEnabledCount = visibleWorkspaces.filter(isVideoGridRecentFlowEnabled).length;
33877
+ if (recentFlowEnabledCount === 0) {
33592
33878
  return MAP_GRID_LEGEND_LABEL;
33593
33879
  }
33594
- if (assemblyEnabledCount === visibleWorkspaces.length) {
33880
+ if (recentFlowEnabledCount === visibleWorkspaces.length) {
33595
33881
  return VIDEO_GRID_LEGEND_LABEL;
33596
33882
  }
33597
33883
  return MIXED_VIDEO_GRID_LEGEND_LABEL;
@@ -33618,6 +33904,7 @@ var VideoCard = React141__namespace.default.memo(({
33618
33904
  useRAF = true,
33619
33905
  className = "",
33620
33906
  compact = false,
33907
+ displayMinuteBucket,
33621
33908
  displayName,
33622
33909
  lastSeenLabel,
33623
33910
  onMouseEnter,
@@ -33638,8 +33925,13 @@ var VideoCard = React141__namespace.default.memo(({
33638
33925
  const lastSeenText = lastSeenLabel || "Unknown";
33639
33926
  const workspaceDisplayName = displayName || getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
33640
33927
  const videoGridMetricValue = getVideoGridMetricValue(workspace);
33928
+ const videoGridDisplayValue = getVideoGridDisplayValue(workspace, effectiveLegend, displayMinuteBucket);
33641
33929
  const videoGridColorState = getVideoGridColorState(workspace, effectiveLegend);
33642
- const badgeTitle = hasAssemblyRecentFlow(workspace) ? `Flow ${Math.round(videoGridMetricValue)}%` : `Efficiency ${Math.round(videoGridMetricValue)}%`;
33930
+ const isRecentFlowCard = isVideoGridRecentFlowEnabled(workspace);
33931
+ const hasDisplayMetric = typeof videoGridDisplayValue === "number" && Number.isFinite(videoGridDisplayValue);
33932
+ const hasBarMetric = typeof videoGridMetricValue === "number" && Number.isFinite(videoGridMetricValue);
33933
+ const badgeTitle = hasVideoGridRecentFlow(workspace) ? `Flow ${Math.round(videoGridDisplayValue ?? 0)}%` : isRecentFlowCard ? "Flow unavailable" : `Efficiency ${Math.round(videoGridDisplayValue ?? 0)}%`;
33934
+ const badgeLabel = hasDisplayMetric ? `${Math.round(videoGridDisplayValue)}%` : "X";
33643
33935
  const efficiencyOverlayClass = videoGridColorState === "green" ? "bg-[#00D654]/25" : videoGridColorState === "yellow" ? "bg-[#FFD700]/30" : videoGridColorState === "red" ? "bg-[#FF2D0A]/30" : "bg-transparent";
33644
33936
  const efficiencyBarClass = videoGridColorState === "green" ? "bg-[#00AB45]" : videoGridColorState === "yellow" ? "bg-[#FFB020]" : videoGridColorState === "red" ? "bg-[#E34329]" : "bg-gray-500/70";
33645
33937
  const efficiencyStatus = videoGridColorState === "green" ? "High" : videoGridColorState === "yellow" ? "Medium" : videoGridColorState === "red" ? "Low" : "Neutral";
@@ -33718,19 +34010,18 @@ var VideoCard = React141__namespace.default.memo(({
33718
34010
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: `absolute ${compact ? "top-1 right-1" : "top-2 right-2"} z-30`, children: /* @__PURE__ */ jsxRuntime.jsx(
33719
34011
  "div",
33720
34012
  {
34013
+ "data-testid": "video-card-metric-badge",
33721
34014
  className: `bg-black/70 backdrop-blur-sm rounded ${compact ? "px-1.5 py-1" : "px-2 py-1"} text-white border border-white/10`,
33722
34015
  title: badgeTitle,
33723
- children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: `${compact ? "text-[10px]" : "text-xs"} font-semibold`, children: [
33724
- Math.round(videoGridMetricValue),
33725
- "%"
33726
- ] })
34016
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${compact ? "text-[10px]" : "text-xs"} font-semibold`, children: badgeLabel })
33727
34017
  }
33728
34018
  ) }),
33729
34019
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: `absolute bottom-0 left-0 right-0 ${compact ? "h-0.5" : "h-1"} bg-black/50 z-30`, children: /* @__PURE__ */ jsxRuntime.jsx(
33730
34020
  "div",
33731
34021
  {
34022
+ "data-testid": "video-card-metric-bar",
33732
34023
  className: `h-full ${efficiencyBarClass} transition-all duration-500`,
33733
- style: { width: `${Math.min(100, videoGridMetricValue)}%` }
34024
+ style: { width: `${hasBarMetric ? Math.min(100, Math.max(0, videoGridMetricValue)) : videoGridColorState === "neutral" ? 100 : 0}%` }
33734
34025
  }
33735
34026
  ) })
33736
34027
  ] }),
@@ -33761,7 +34052,7 @@ var VideoCard = React141__namespace.default.memo(({
33761
34052
  }
33762
34053
  );
33763
34054
  }, (prevProps, nextProps) => {
33764
- if (prevProps.workspace.efficiency !== nextProps.workspace.efficiency || prevProps.workspace.assembly_enabled !== nextProps.workspace.assembly_enabled || prevProps.workspace.recent_flow_percent !== nextProps.workspace.recent_flow_percent || prevProps.workspace.incoming_wip_current !== nextProps.workspace.incoming_wip_current || prevProps.workspace.incoming_wip_buffer_name !== nextProps.workspace.incoming_wip_buffer_name || prevProps.workspace.trend !== nextProps.workspace.trend || prevProps.workspace.performance_score !== nextProps.workspace.performance_score || prevProps.workspace.pph !== nextProps.workspace.pph) {
34055
+ if (prevProps.workspace.efficiency !== nextProps.workspace.efficiency || prevProps.workspace.assembly_enabled !== nextProps.workspace.assembly_enabled || prevProps.workspace.video_grid_metric_mode !== nextProps.workspace.video_grid_metric_mode || prevProps.workspace.recent_flow_mode !== nextProps.workspace.recent_flow_mode || prevProps.workspace.recent_flow_percent !== nextProps.workspace.recent_flow_percent || prevProps.workspace.recent_flow_effective_end_at !== nextProps.workspace.recent_flow_effective_end_at || prevProps.workspace.incoming_wip_current !== nextProps.workspace.incoming_wip_current || prevProps.workspace.incoming_wip_buffer_name !== nextProps.workspace.incoming_wip_buffer_name || prevProps.workspace.trend !== nextProps.workspace.trend || prevProps.workspace.performance_score !== nextProps.workspace.performance_score || prevProps.workspace.pph !== nextProps.workspace.pph) {
33765
34056
  return false;
33766
34057
  }
33767
34058
  if (prevProps.workspace.workspace_uuid !== nextProps.workspace.workspace_uuid || prevProps.workspace.workspace_name !== nextProps.workspace.workspace_name || prevProps.workspace.line_id !== nextProps.workspace.line_id) {
@@ -33779,6 +34070,9 @@ var VideoCard = React141__namespace.default.memo(({
33779
34070
  if (prevProps.compact !== nextProps.compact) {
33780
34071
  return false;
33781
34072
  }
34073
+ if (prevProps.displayMinuteBucket !== nextProps.displayMinuteBucket) {
34074
+ return false;
34075
+ }
33782
34076
  if (prevProps.hlsUrl !== nextProps.hlsUrl || prevProps.shouldPlay !== nextProps.shouldPlay) {
33783
34077
  return false;
33784
34078
  }
@@ -34062,6 +34356,7 @@ var VideoGridView = React141__namespace.default.memo(({
34062
34356
  stream_source: isR2Stream ? "r2" : "media_config"
34063
34357
  });
34064
34358
  }, []);
34359
+ const displayMinuteBucket = Math.floor(Date.now() / 6e4);
34065
34360
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `relative overflow-hidden h-full w-full ${className}`, children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: "h-full w-full p-3 sm:p-2", children: /* @__PURE__ */ jsxRuntime.jsx(
34066
34361
  "div",
34067
34362
  {
@@ -34151,6 +34446,7 @@ var VideoGridView = React141__namespace.default.memo(({
34151
34446
  ),
34152
34447
  lastSeenLabel: card.lastSeenLabel,
34153
34448
  useRAF: effectiveUseRAF,
34449
+ displayMinuteBucket,
34154
34450
  compact: !selectedLine,
34155
34451
  onMouseEnter: onWorkspaceHover ? () => onWorkspaceHover(card.workspaceId) : void 0,
34156
34452
  onMouseLeave: onWorkspaceHoverEnd ? () => onWorkspaceHoverEnd(card.workspaceId) : void 0
@@ -39280,45 +39576,56 @@ var FileManagerFilters = ({
39280
39576
  });
39281
39577
  const [loadingPercentile, setLoadingPercentile] = React141.useState(false);
39282
39578
  const resolvedTargetCycleTime = targetCycleTime && targetCycleTime > 0 ? targetCycleTime : null;
39283
- const getRootCauseConfig = (reason) => {
39284
- const colorConfig = getIdleTimeReasonColor(reason);
39285
- let Icon2 = lucideReact.HelpCircle;
39286
- if (reason.includes("Machine Breakdown")) Icon2 = lucideReact.AlertTriangle;
39287
- else if (reason.includes("Material")) Icon2 = lucideReact.Package;
39288
- else if (reason.includes("Worker") || reason.includes("Absent")) Icon2 = lucideReact.UserX;
39289
- else if (reason.includes("Quality")) Icon2 = lucideReact.XCircle;
39290
- else if (reason.includes("Power")) Icon2 = lucideReact.Zap;
39291
- else if (reason.includes("Maintenance") || reason.includes("Tool")) Icon2 = lucideReact.Wrench;
39292
- else if (reason.includes("Present") || reason.includes("Idle")) Icon2 = lucideReact.Clock;
39579
+ const getIdleTimeClassification = React141.useCallback((clipId) => {
39580
+ const classification = mergedClipClassifications[clipId];
39581
+ return classification || null;
39582
+ }, [mergedClipClassifications]);
39583
+ const getRootCauseConfig = React141.useCallback((classification) => {
39584
+ if (!classification || classification.status === "processing") {
39585
+ return null;
39586
+ }
39587
+ const presentation = getIdleReasonPresentation({
39588
+ label: classification.label,
39589
+ displayName: classification.displayName,
39590
+ paletteToken: classification.paletteToken,
39591
+ iconToken: classification.iconToken,
39592
+ isKnown: classification.isKnown
39593
+ });
39293
39594
  return {
39294
- color: colorConfig.text,
39295
- bgColor: colorConfig.bg,
39296
- borderColor: colorConfig.border,
39297
- Icon: Icon2,
39298
- iconColor: colorConfig.text
39595
+ color: presentation.textClass,
39596
+ bgColor: presentation.bgClass,
39597
+ borderColor: presentation.borderClass,
39598
+ Icon: presentation.Icon,
39599
+ iconColor: presentation.textClass,
39600
+ displayName: presentation.displayName
39299
39601
  };
39300
- };
39301
- const normalizeIdleReasonLabel = React141.useCallback((label) => {
39302
- return label.replace(/_/g, " ").trim();
39303
39602
  }, []);
39304
- const getIdleTimeRootCause = React141.useCallback((clipId) => {
39305
- const classification = mergedClipClassifications[clipId];
39306
- if (!classification) return "processing";
39307
- if (classification.status === "processing") return "processing";
39308
- return classification.label || "processing";
39309
- }, [mergedClipClassifications]);
39310
39603
  const idleReasonOptions = React141.useMemo(() => {
39311
39604
  const idleClips = clipMetadata["idle_time"] || [];
39312
- const uniqueReasons = /* @__PURE__ */ new Set();
39605
+ const uniqueReasons = /* @__PURE__ */ new Map();
39313
39606
  idleClips.forEach((clip) => {
39314
39607
  const clipId = clip.clipId || clip.id;
39315
39608
  if (!clipId) return;
39316
- const reason = getIdleTimeRootCause(clipId);
39317
- if (!reason || reason === "processing") return;
39318
- uniqueReasons.add(normalizeIdleReasonLabel(reason));
39609
+ const classification = getIdleTimeClassification(clipId);
39610
+ if (!classification || classification.status === "processing" || !classification.label) {
39611
+ return;
39612
+ }
39613
+ if (!uniqueReasons.has(classification.label)) {
39614
+ uniqueReasons.set(classification.label, {
39615
+ label: classification.label,
39616
+ displayName: classification.displayName || classification.label.replace(/_/g, " "),
39617
+ paletteToken: classification.paletteToken,
39618
+ iconToken: classification.iconToken,
39619
+ isKnown: classification.isKnown
39620
+ });
39621
+ }
39319
39622
  });
39320
- return Array.from(uniqueReasons).sort((a, b) => a.localeCompare(b));
39321
- }, [clipMetadata, getIdleTimeRootCause, normalizeIdleReasonLabel]);
39623
+ return Array.from(uniqueReasons.values()).sort((a, b) => a.displayName.localeCompare(b.displayName));
39624
+ }, [clipMetadata, getIdleTimeClassification]);
39625
+ const selectedIdleReasonOption = React141.useMemo(
39626
+ () => idleReasonOptions.find((reason) => reason.label === idleLabelFilter) || null,
39627
+ [idleReasonOptions, idleLabelFilter]
39628
+ );
39322
39629
  const getClipBadge = React141.useCallback((node) => {
39323
39630
  if (node.categoryId === "idle_time" || node.categoryId === "low_value") {
39324
39631
  return { text: "Idle", className: "bg-red-100 text-red-700" };
@@ -39390,6 +39697,10 @@ var FileManagerFilters = ({
39390
39697
  seededClassifications[clip.clipId] = {
39391
39698
  status: clip.classification_status,
39392
39699
  label: clip.classification_label || void 0,
39700
+ displayName: clip.classification_display_name || void 0,
39701
+ paletteToken: clip.classification_palette_token || void 0,
39702
+ iconToken: clip.classification_icon_token || void 0,
39703
+ isKnown: clip.classification_is_known ?? void 0,
39393
39704
  confidence: clip.classification_confidence ?? void 0
39394
39705
  };
39395
39706
  }
@@ -39773,7 +40084,7 @@ var FileManagerFilters = ({
39773
40084
  return slot ? slot.label : value;
39774
40085
  }, [timeSlots]);
39775
40086
  const isClipInTimeRange = React141.useCallback((clipTimestamp) => {
39776
- if (!isTimeFilterActive || !startTime || !endTime) {
40087
+ if (!startTime || !endTime) {
39777
40088
  return true;
39778
40089
  }
39779
40090
  try {
@@ -39799,9 +40110,8 @@ var FileManagerFilters = ({
39799
40110
  let filteredClips = categoryClips.filter((clip) => isClipInTimeRange(clip.clip_timestamp));
39800
40111
  if (category.id === "idle_time" && idleLabelFilter) {
39801
40112
  filteredClips = filteredClips.filter((clip) => {
39802
- const rootCause = getIdleTimeRootCause(clip.clipId || clip.id);
39803
- const normalizedRootCause = rootCause.replace(/_/g, " ");
39804
- return normalizedRootCause === idleLabelFilter;
40113
+ const classification = getIdleTimeClassification(clip.clipId || clip.id);
40114
+ return classification?.label === idleLabelFilter;
39805
40115
  });
39806
40116
  }
39807
40117
  const displayCount = isTimeFilterActive || category.id === "idle_time" && idleLabelFilter ? filteredClips.length : categoryCount;
@@ -39831,8 +40141,9 @@ var FileManagerFilters = ({
39831
40141
  clipPosition: index + 1,
39832
40142
  // Store 1-based position
39833
40143
  cycleTimeSeconds: cycleTime,
39834
- duration: clip.duration
40144
+ duration: clip.duration,
39835
40145
  // Store duration for custom badge rendering
40146
+ cycleItemCount: clip.cycle_item_count ?? null
39836
40147
  };
39837
40148
  });
39838
40149
  regularCategoryNodes.push({
@@ -40088,14 +40399,14 @@ var FileManagerFilters = ({
40088
40399
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: `flex-shrink-0 mr-3 ${node.type === "category" || node.type === "percentile-category" ? "p-2 rounded-lg shadow-sm group-hover:scale-110 transition-transform duration-200" : "p-0.5"} ${colorClasses && (node.type === "category" || node.type === "percentile-category") ? `${colorClasses.bg} border border-white/60` : ""}`, children: node.type === "video" && (node.categoryId === "idle_time" || node.categoryId === "low_value") ? (
40089
40400
  // Show root cause icon for idle time clips
40090
40401
  (() => {
40091
- const rootCause = getIdleTimeRootCause(node.clipId || node.id);
40092
- if (rootCause === "processing") {
40402
+ const classification = getIdleTimeClassification(node.clipId || node.id);
40403
+ if (!classification || classification.status === "processing") {
40093
40404
  return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "h-3.5 w-3.5 text-purple-500", strokeWidth: 2.5 });
40094
40405
  }
40095
40406
  if (!idleTimeVlmEnabled && node.categoryId === "idle_time") {
40096
40407
  return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "h-3.5 w-3.5 text-purple-500", strokeWidth: 2.5 });
40097
40408
  }
40098
- const config = getRootCauseConfig(rootCause.replace(/_/g, " "));
40409
+ const config = getRootCauseConfig(classification);
40099
40410
  if (config) {
40100
40411
  const IconComponent = config.Icon;
40101
40412
  return /* @__PURE__ */ jsxRuntime.jsx(IconComponent, { className: `h-3.5 w-3.5 ${config.iconColor}`, strokeWidth: 2.5 });
@@ -40117,10 +40428,10 @@ var FileManagerFilters = ({
40117
40428
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `inline-flex items-center px-2 py-0.5 rounded-md border text-[10px] font-bold ${isLong ? "bg-purple-50 text-purple-700 border-purple-200" : "bg-gray-50 text-gray-600 border-gray-200"}`, children: isLong ? "Long" : "Short" });
40118
40429
  }
40119
40430
  const clipId = node.clipId || node.id;
40120
- const rootCause = getIdleTimeRootCause(clipId);
40121
- const isProcessing = rootCause === "processing";
40122
- const displayLabel = isProcessing ? "Analyzing..." : rootCause.replace(/_/g, " ");
40123
- const config = isProcessing ? null : getRootCauseConfig(displayLabel);
40431
+ const classification = getIdleTimeClassification(clipId);
40432
+ const isProcessing = !classification || classification.status === "processing";
40433
+ const config = isProcessing ? null : getRootCauseConfig(classification);
40434
+ const displayLabel = config?.displayName || "Analyzing...";
40124
40435
  const bgClass = config ? config.bgColor : "bg-slate-50";
40125
40436
  const textClass = config ? config.color : "text-slate-700";
40126
40437
  const borderClass = config ? config.borderColor : "border-slate-200";
@@ -40130,7 +40441,13 @@ var FileManagerFilters = ({
40130
40441
  // Show badge for other clips
40131
40442
  (() => {
40132
40443
  const badge = getClipBadge(node);
40133
- return /* @__PURE__ */ jsxRuntime.jsx("span", { className: `inline-flex items-center px-1.5 py-0.5 rounded-md text-xs font-medium ${badge.className}`, children: badge.text });
40444
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center gap-1.5", children: [
40445
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `inline-flex items-center px-1.5 py-0.5 rounded-md text-xs font-medium ${badge.className}`, children: badge.text }),
40446
+ node.categoryId === "cycle_completion" && node.cycleItemCount && node.cycleItemCount > 1 ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center px-1.5 py-0.5 rounded-md text-xs font-semibold bg-blue-100 text-blue-700 border border-blue-200", children: [
40447
+ "x",
40448
+ node.cycleItemCount
40449
+ ] }) : null
40450
+ ] });
40134
40451
  })()
40135
40452
  ) })
40136
40453
  ] }),
@@ -40218,7 +40535,7 @@ var FileManagerFilters = ({
40218
40535
  idleLabelFilter && activeFilter === "idle_time" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2 text-xs text-purple-600 bg-purple-50/60 px-3 py-1.5 rounded-lg border border-purple-100", children: [
40219
40536
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-medium", children: [
40220
40537
  "Reason: ",
40221
- idleLabelFilter.replace(/_/g, " ")
40538
+ selectedIdleReasonOption?.displayName || idleLabelFilter.replace(/_/g, " ")
40222
40539
  ] }),
40223
40540
  /* @__PURE__ */ jsxRuntime.jsx(
40224
40541
  "button",
@@ -40267,21 +40584,31 @@ var FileManagerFilters = ({
40267
40584
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 animate-spin" }),
40268
40585
  "Loading idle reasons..."
40269
40586
  ] }) : idleReasonOptions.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-4 text-sm text-slate-500", children: "No classified idle reasons found yet." }) : idleReasonOptions.map((reason) => {
40270
- const config = getRootCauseConfig(reason);
40587
+ const config = getRootCauseConfig({
40588
+ status: "classified",
40589
+ label: reason.label,
40590
+ displayName: reason.displayName,
40591
+ paletteToken: reason.paletteToken,
40592
+ iconToken: reason.iconToken,
40593
+ isKnown: reason.isKnown
40594
+ });
40595
+ if (!config) {
40596
+ return null;
40597
+ }
40271
40598
  return /* @__PURE__ */ jsxRuntime.jsxs(
40272
40599
  "button",
40273
40600
  {
40274
40601
  onClick: () => {
40275
- setIdleLabelFilter(reason);
40602
+ setIdleLabelFilter(reason.label);
40276
40603
  setShowIdleLabelFilterModal(false);
40277
40604
  },
40278
- className: `w-full px-3 py-2 text-left text-sm rounded-lg transition-colors flex items-center gap-2 mb-1 ${idleLabelFilter === reason ? "bg-purple-50 text-purple-700 font-medium" : "text-slate-700 hover:bg-slate-50"}`,
40605
+ className: `w-full px-3 py-2 text-left text-sm rounded-lg transition-colors flex items-center gap-2 mb-1 ${idleLabelFilter === reason.label ? "bg-purple-50 text-purple-700 font-medium" : "text-slate-700 hover:bg-slate-50"}`,
40279
40606
  children: [
40280
40607
  /* @__PURE__ */ jsxRuntime.jsx(config.Icon, { className: `h-3.5 w-3.5 ${config.iconColor}` }),
40281
- reason
40608
+ reason.displayName
40282
40609
  ]
40283
40610
  },
40284
- reason
40611
+ reason.label
40285
40612
  );
40286
40613
  }) })
40287
40614
  ]
@@ -40396,33 +40723,14 @@ var FileManagerFilters = ({
40396
40723
  slot.value
40397
40724
  )) })
40398
40725
  ] })
40399
- ] }) }),
40400
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-3 border-t border-slate-200 bg-white rounded-b-xl", children: /* @__PURE__ */ jsxRuntime.jsx(
40401
- "button",
40402
- {
40403
- onClick: () => {
40404
- if (startTime && endTime) {
40405
- setIsTimeFilterActive(true);
40406
- }
40407
- setShowTimeFilterModal(false);
40408
- setStartSearchTerm("");
40409
- setEndSearchTerm("");
40410
- },
40411
- disabled: !startTime || !endTime,
40412
- className: `
40413
- w-full px-4 py-2 text-sm font-semibold rounded-lg transition-all
40414
- ${startTime && endTime ? "bg-blue-600 text-white hover:bg-blue-700 active:scale-[0.98]" : "bg-slate-200 text-slate-400 cursor-not-allowed"}
40415
- `,
40416
- children: "Apply"
40417
- }
40418
- ) })
40726
+ ] }) })
40419
40727
  ]
40420
40728
  }
40421
40729
  )
40422
40730
  ] }),
40423
40731
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-4 py-3 flex-1 min-h-0 overflow-y-auto scrollbar-thin", children: [
40424
40732
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: filterTree.map((node) => renderNode(node)) }),
40425
- filterTree.length === 0 && isTimeFilterActive && (startTime || endTime) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center py-12", children: [
40733
+ filterTree.length === 0 && (startTime || endTime) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center py-12", children: [
40426
40734
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-slate-300 mb-4", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "h-12 w-12 mx-auto" }) }),
40427
40735
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-slate-700 mb-2", children: "No clips found" }),
40428
40736
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-slate-500 mb-4", children: "No clips match the selected time range" }),
@@ -40432,18 +40740,19 @@ var FileManagerFilters = ({
40432
40740
  onClick: () => {
40433
40741
  setStartTime("");
40434
40742
  setEndTime("");
40743
+ setIsTimeFilterActive(false);
40435
40744
  },
40436
40745
  className: "inline-flex items-center px-4 py-2 bg-blue-50 text-blue-600 text-sm font-medium rounded-lg hover:bg-blue-100 transition-colors duration-200",
40437
40746
  children: "Clear time filter"
40438
40747
  }
40439
40748
  )
40440
40749
  ] }),
40441
- filterTree.length === 0 && !isTimeFilterActive && categories.length === 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center py-12", children: [
40750
+ filterTree.length === 0 && !startTime && !endTime && categories.length === 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center py-12", children: [
40442
40751
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-slate-300 mb-4", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.HelpCircle, { className: "h-12 w-12 mx-auto" }) }),
40443
40752
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-slate-700 mb-2", children: "No clip types available" }),
40444
40753
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-slate-500", children: "Loading clip categories..." })
40445
40754
  ] }),
40446
- filterTree.length === 0 && !isTimeFilterActive && categories.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center py-12", children: [
40755
+ filterTree.length === 0 && !startTime && !endTime && categories.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center py-12", children: [
40447
40756
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-slate-300 mb-4", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Play, { className: "h-12 w-12 mx-auto" }) }),
40448
40757
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-slate-700 mb-2", children: "No clips available" }),
40449
40758
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-slate-500", children: "No clips found for the selected time period" })
@@ -40847,6 +41156,34 @@ function useClipsRealtimeUpdates({
40847
41156
  hasNewClips: newClipsNotification !== null && newClipsNotification.count > 0
40848
41157
  };
40849
41158
  }
41159
+ var parseFiniteNumber2 = (value) => {
41160
+ if (typeof value === "number" && Number.isFinite(value)) {
41161
+ return value;
41162
+ }
41163
+ if (typeof value === "string") {
41164
+ const parsed = Number(value);
41165
+ if (Number.isFinite(parsed)) {
41166
+ return parsed;
41167
+ }
41168
+ }
41169
+ return null;
41170
+ };
41171
+ var getMultiCycleItemCount = (clip) => {
41172
+ if (!clip || clip.categoryId !== "cycle_completion") {
41173
+ return null;
41174
+ }
41175
+ const cycleItemCount = parseFiniteNumber2(clip.cycle_item_count);
41176
+ return cycleItemCount !== null && cycleItemCount > 1 ? cycleItemCount : null;
41177
+ };
41178
+ var OVERLAY_ICON_COLOR_BY_PALETTE = {
41179
+ red: "text-red-300",
41180
+ amber: "text-amber-300",
41181
+ blue: "text-blue-300",
41182
+ violet: "text-violet-300",
41183
+ emerald: "text-emerald-300",
41184
+ cyan: "text-cyan-300",
41185
+ slate: "text-slate-200"
41186
+ };
40850
41187
  var BottlenecksContent = ({
40851
41188
  workspaceId,
40852
41189
  workspaceName,
@@ -41587,6 +41924,10 @@ var BottlenecksContent = ({
41587
41924
  classificationUpdates[clipId] = {
41588
41925
  status,
41589
41926
  label: clip.classification_label || void 0,
41927
+ displayName: clip.classification_display_name || void 0,
41928
+ paletteToken: clip.classification_palette_token || void 0,
41929
+ iconToken: clip.classification_icon_token || void 0,
41930
+ isKnown: clip.classification_is_known ?? void 0,
41590
41931
  confidence: clip.classification_confidence ?? void 0
41591
41932
  };
41592
41933
  });
@@ -42371,79 +42712,13 @@ var BottlenecksContent = ({
42371
42712
  return () => window.removeEventListener("keydown", handleEscape);
42372
42713
  }
42373
42714
  }, [isFullscreen, exitFullscreen]);
42374
- const ROOT_CAUSE_CONFIG = {
42375
- "Machine Breakdown": {
42376
- color: "text-white",
42377
- bgColor: "bg-black/60 backdrop-blur-sm",
42378
- borderColor: "border-transparent",
42379
- Icon: lucideReact.AlertTriangle,
42380
- iconColor: "text-red-500",
42381
- // Standard red for high visibility on dark
42382
- dotColor: "bg-red-600"
42383
- },
42384
- "Other": {
42385
- color: "text-white",
42386
- bgColor: "bg-black/60 backdrop-blur-sm",
42387
- borderColor: "border-transparent",
42388
- Icon: lucideReact.HelpCircle,
42389
- iconColor: "text-gray-400",
42390
- dotColor: "bg-gray-500"
42391
- },
42392
- "Power Outage": {
42393
- color: "text-white",
42394
- bgColor: "bg-black/60 backdrop-blur-sm",
42395
- borderColor: "border-transparent",
42396
- Icon: lucideReact.Zap,
42397
- iconColor: "text-red-400",
42398
- // Lighter red
42399
- dotColor: "bg-red-500"
42400
- },
42401
- "Worker Absent": {
42402
- color: "text-white",
42403
- bgColor: "bg-black/60 backdrop-blur-sm",
42404
- borderColor: "border-transparent",
42405
- Icon: lucideReact.UserX,
42406
- iconColor: "text-red-400",
42407
- // Lighter red
42408
- dotColor: "bg-red-500"
42409
- },
42410
- "Material Shortage": {
42411
- color: "text-white",
42412
- bgColor: "bg-black/60 backdrop-blur-sm",
42413
- borderColor: "border-transparent",
42414
- Icon: lucideReact.Package,
42415
- iconColor: "text-red-300",
42416
- // Even lighter red
42417
- dotColor: "bg-red-400"
42418
- },
42419
- "Quality Issue": {
42420
- color: "text-white",
42421
- bgColor: "bg-black/60 backdrop-blur-sm",
42422
- borderColor: "border-transparent",
42423
- Icon: lucideReact.XCircle,
42424
- iconColor: "text-red-300",
42425
- // Even lighter red
42426
- dotColor: "bg-red-400"
42427
- },
42428
- "Scheduled Maintenance": {
42429
- color: "text-white",
42430
- bgColor: "bg-black/60 backdrop-blur-sm",
42431
- borderColor: "border-transparent",
42432
- Icon: lucideReact.Wrench,
42433
- iconColor: "text-red-200",
42434
- // Very light red
42435
- dotColor: "bg-red-300"
42436
- }
42437
- };
42438
- const getIdleTimeRootCause = React141.useCallback((video) => {
42715
+ const getIdleTimeClassification = React141.useCallback((video) => {
42439
42716
  if (video.type !== "idle_time") return null;
42440
42717
  const videoId = video.id || video.clipId;
42441
- if (!videoId) return "processing";
42718
+ if (!videoId) return null;
42442
42719
  const classification = clipClassifications[videoId];
42443
- console.log(`[getIdleTimeRootCause] Looking up ${videoId}: `, classification);
42444
- if (!classification) return "processing";
42445
- if (classification.status === "processing") return "processing";
42446
- return classification.label || "processing";
42720
+ console.log(`[getIdleTimeClassification] Looking up ${videoId}: `, classification);
42721
+ return classification || null;
42447
42722
  }, [clipClassifications]);
42448
42723
  const getIdleTimeConfidence = React141.useCallback((video) => {
42449
42724
  if (video.type !== "idle_time") return null;
@@ -42454,36 +42729,35 @@ var BottlenecksContent = ({
42454
42729
  if (classification.status === "processing") return null;
42455
42730
  return classification.confidence ?? null;
42456
42731
  }, [clipClassifications]);
42457
- const formatDisplayLabel = React141.useCallback((label) => {
42458
- return label.replace(/_/g, " ");
42459
- }, []);
42460
- const getRootCauseConfig = React141.useCallback((rootCause) => {
42461
- if (!rootCause) return null;
42462
- if (rootCause === "processing") {
42732
+ const getRootCauseConfig = React141.useCallback((classification) => {
42733
+ if (!classification || classification.status === "processing") {
42463
42734
  return {
42464
42735
  color: "text-white",
42465
42736
  bgColor: "bg-black/60 backdrop-blur-sm",
42466
- borderColor: "border-gray-500/30",
42737
+ borderColor: "border-white/10",
42467
42738
  Icon: lucideReact.Clock,
42468
- iconColor: "text-purple-500",
42469
- dotColor: "bg-gray-400",
42739
+ iconColor: "text-purple-300",
42740
+ dotColor: "bg-purple-400",
42470
42741
  displayName: "Analyzing..."
42471
42742
  };
42472
42743
  }
42473
- const hardcodedConfig = ROOT_CAUSE_CONFIG[rootCause];
42474
- if (hardcodedConfig) {
42475
- return hardcodedConfig;
42476
- }
42744
+ const presentation = getIdleReasonPresentation({
42745
+ label: classification.label,
42746
+ displayName: classification.displayName,
42747
+ paletteToken: classification.paletteToken,
42748
+ iconToken: classification.iconToken,
42749
+ isKnown: classification.isKnown
42750
+ });
42477
42751
  return {
42478
42752
  color: "text-white",
42479
42753
  bgColor: "bg-black/60 backdrop-blur-sm",
42480
- borderColor: "border-purple-500/30",
42481
- Icon: lucideReact.Tag,
42482
- iconColor: "text-purple-400",
42483
- dotColor: "bg-purple-400",
42484
- displayName: formatDisplayLabel(rootCause)
42754
+ borderColor: "border-white/10",
42755
+ Icon: presentation.Icon,
42756
+ iconColor: OVERLAY_ICON_COLOR_BY_PALETTE[presentation.paletteToken] || OVERLAY_ICON_COLOR_BY_PALETTE.slate,
42757
+ dotColor: presentation.bgClass,
42758
+ displayName: presentation.displayName
42485
42759
  };
42486
- }, [formatDisplayLabel]);
42760
+ }, []);
42487
42761
  const getClipTypeLabel = React141.useCallback((video) => {
42488
42762
  if (!video) return "";
42489
42763
  const currentFilter = activeFilterRef.current;
@@ -42648,15 +42922,15 @@ var BottlenecksContent = ({
42648
42922
  (currentVideo.type === "cycle_completion" || currentVideo.type === "bottleneck" && currentVideo.description.toLowerCase().includes("cycle time")) && currentVideo.cycle_time_seconds || currentVideo.type === "idle_time" || currentVideo.type === "low_value" ? currentVideo.type === "idle_time" ? (
42649
42923
  // Show full colored badge for idle time
42650
42924
  (() => {
42651
- const rootCause = getIdleTimeRootCause(currentVideo);
42925
+ const classification = getIdleTimeClassification(currentVideo);
42652
42926
  const confidence = getIdleTimeConfidence(currentVideo);
42653
- const config = getRootCauseConfig(rootCause);
42927
+ const config = getRootCauseConfig(classification);
42654
42928
  if (!config) return null;
42655
42929
  const IconComponent = config.Icon;
42656
42930
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
42657
- idleTimeVlmEnabled && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-3 left-3 z-10", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `inline-flex items-center gap-1.5 px-2.5 py-1.5 rounded-md border ${config.bgColor} ${config.borderColor} shadow-lg`, children: [
42658
- /* @__PURE__ */ jsxRuntime.jsx(IconComponent, { className: `h-3.5 w-3.5 ${config.iconColor}`, strokeWidth: 2.5 }),
42659
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: `font-medium text-xs ${config.color}`, children: config.displayName || (rootCause ? formatDisplayLabel(rootCause) : "Analyzing...") })
42931
+ idleTimeVlmEnabled && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-3 left-3 z-10", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `inline-flex max-w-[18rem] items-center gap-1.5 rounded-md border px-2.5 py-1.5 shadow-lg ${config.bgColor} ${config.borderColor}`, children: [
42932
+ /* @__PURE__ */ jsxRuntime.jsx(IconComponent, { className: `h-3.5 w-3.5 flex-shrink-0 ${config.iconColor}`, strokeWidth: 2.5 }),
42933
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `truncate font-medium text-xs ${config.color}`, children: config.displayName || "Analyzing..." })
42660
42934
  ] }) }),
42661
42935
  idleTimeVlmEnabled && confidence !== null && (() => {
42662
42936
  const confidencePercent = confidence * 100;
@@ -42691,9 +42965,9 @@ var BottlenecksContent = ({
42691
42965
  currentVideo.type === "idle_time" ? (
42692
42966
  // Show full colored badge for idle time
42693
42967
  (() => {
42694
- const rootCause = getIdleTimeRootCause(currentVideo);
42968
+ const classification = getIdleTimeClassification(currentVideo);
42695
42969
  const confidence = getIdleTimeConfidence(currentVideo);
42696
- const config = getRootCauseConfig(rootCause);
42970
+ const config = getRootCauseConfig(classification);
42697
42971
  if (!config) return null;
42698
42972
  const IconComponent = config.Icon;
42699
42973
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
@@ -42702,9 +42976,9 @@ var BottlenecksContent = ({
42702
42976
  (confidence * 100).toFixed(0),
42703
42977
  "%"
42704
42978
  ] }) }) }),
42705
- idleTimeVlmEnabled && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-3 right-3 z-10", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `inline-flex items-center gap-1.5 px-2.5 py-1.5 rounded-md border ${config.bgColor} ${config.borderColor} shadow-lg`, children: [
42706
- /* @__PURE__ */ jsxRuntime.jsx(IconComponent, { className: `h-3.5 w-3.5 ${config.iconColor}`, strokeWidth: 2.5 }),
42707
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: `font-medium text-xs ${config.color}`, children: rootCause })
42979
+ idleTimeVlmEnabled && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-3 right-3 z-10", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `inline-flex max-w-[18rem] items-center gap-1.5 rounded-md border px-2.5 py-1.5 shadow-lg ${config.bgColor} ${config.borderColor}`, children: [
42980
+ /* @__PURE__ */ jsxRuntime.jsx(IconComponent, { className: `h-3.5 w-3.5 flex-shrink-0 ${config.iconColor}`, strokeWidth: 2.5 }),
42981
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `truncate font-medium text-xs ${config.color}`, children: config.displayName || "Analyzing..." })
42708
42982
  ] }) })
42709
42983
  ] });
42710
42984
  })()
@@ -42744,6 +43018,7 @@ var BottlenecksContent = ({
42744
43018
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500", children: "No clips found" })
42745
43019
  ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: triageClips.map((clip, index) => {
42746
43020
  const isIdleTime = clip.categoryId === "idle_time";
43021
+ const cycleItemCount = getMultiCycleItemCount(clip);
42747
43022
  const timeString = isIdleTime && clip.idle_start_time && clip.idle_end_time ? formatIdleTimeRange(clip.idle_start_time, clip.idle_end_time, timezone) : new Date(clip.clip_timestamp).toLocaleTimeString("en-US", {
42748
43023
  hour12: true,
42749
43024
  hour: "numeric",
@@ -42796,15 +43071,15 @@ var BottlenecksContent = ({
42796
43071
  // Idle time clips - show root cause label below time
42797
43072
  (() => {
42798
43073
  const clipId = clip.id || clip.clipId;
42799
- const rootCause = getIdleTimeRootCause({
43074
+ const classification = getIdleTimeClassification({
42800
43075
  id: clipId,
42801
43076
  type: "idle_time",
42802
43077
  creation_timestamp: clip.clip_timestamp
42803
43078
  });
42804
- console.log(`[BottlenecksContent] Sidebar clip ${clipId}: rootCause=${rootCause}, classification=`, clipClassifications[clipId]);
42805
- const config = getRootCauseConfig(rootCause);
43079
+ console.log(`[BottlenecksContent] Sidebar clip ${clipId}: classification=`, clipClassifications[clipId]);
43080
+ const config = getRootCauseConfig(classification);
42806
43081
  if (!config) {
42807
- console.log(`[BottlenecksContent] No config found for rootCause: ${rootCause}`);
43082
+ console.log(`[BottlenecksContent] No config found for classification on clip: ${clipId}`);
42808
43083
  return null;
42809
43084
  }
42810
43085
  const IconComponent = config.Icon;
@@ -42819,7 +43094,7 @@ var BottlenecksContent = ({
42819
43094
  ] }) })
42820
43095
  ] }),
42821
43096
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pl-6", children: idleTimeVlmEnabled ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex items-center px-2 py-0.5 rounded-md border text-xs font-medium bg-gray-50 text-gray-700 border-gray-200", children: (() => {
42822
- const displayText = config.displayName || (rootCause ? formatDisplayLabel(rootCause) : "Analyzing...");
43097
+ const displayText = config.displayName || "Analyzing...";
42823
43098
  console.log(`[BottlenecksContent] Displaying label: "${displayText}" for clip ${clipId}`);
42824
43099
  return displayText;
42825
43100
  })() }) : (() => {
@@ -42839,7 +43114,13 @@ var BottlenecksContent = ({
42839
43114
  clip.duration ? `${clip.duration.toFixed(1)}s` : "N/A",
42840
43115
  ")"
42841
43116
  ] }) }),
42842
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: `px-2 py-1 text-xs font-semibold rounded ${badgeColor}`, children: badgeText })
43117
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
43118
+ cycleItemCount ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "px-2 py-1 text-xs font-semibold rounded border border-blue-200 bg-blue-100 text-blue-700", children: [
43119
+ "x",
43120
+ cycleItemCount
43121
+ ] }) : null,
43122
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `px-2 py-1 text-xs font-semibold rounded ${badgeColor}`, children: badgeText })
43123
+ ] })
42843
43124
  ] })
42844
43125
  )
42845
43126
  },
@@ -42932,15 +43213,15 @@ var BottlenecksContent = ({
42932
43213
  (currentVideo.type === "cycle_completion" || currentVideo.type === "bottleneck" && currentVideo.description.toLowerCase().includes("cycle time")) && currentVideo.cycle_time_seconds || currentVideo.type === "idle_time" || currentVideo.type === "low_value" ? currentVideo.type === "idle_time" ? (
42933
43214
  // Show full colored badge for idle time
42934
43215
  (() => {
42935
- const rootCause = getIdleTimeRootCause(currentVideo);
43216
+ const classification = getIdleTimeClassification(currentVideo);
42936
43217
  const confidence = getIdleTimeConfidence(currentVideo);
42937
- const config = getRootCauseConfig(rootCause);
43218
+ const config = getRootCauseConfig(classification);
42938
43219
  if (!config) return null;
42939
43220
  const IconComponent = config.Icon;
42940
43221
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
42941
- idleTimeVlmEnabled && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-4 left-4 z-50", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `inline-flex items-center gap-2 px-3 py-2 rounded-md border ${config.bgColor} ${config.borderColor} shadow-lg`, children: [
42942
- /* @__PURE__ */ jsxRuntime.jsx(IconComponent, { className: `h-4 w-4 ${config.iconColor}`, strokeWidth: 2.5 }),
42943
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: `font-medium text-sm ${config.color}`, children: rootCause })
43222
+ idleTimeVlmEnabled && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-4 left-4 z-50", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `inline-flex max-w-[20rem] items-center gap-2 rounded-md border px-3 py-2 shadow-lg ${config.bgColor} ${config.borderColor}`, children: [
43223
+ /* @__PURE__ */ jsxRuntime.jsx(IconComponent, { className: `h-4 w-4 flex-shrink-0 ${config.iconColor}`, strokeWidth: 2.5 }),
43224
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `truncate font-medium text-sm ${config.color}`, children: config.displayName || "Analyzing..." })
42944
43225
  ] }) }),
42945
43226
  idleTimeVlmEnabled && confidence !== null && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-4 right-4 z-50", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "inline-flex items-center gap-1.5 px-3 py-2 rounded-lg bg-black/70 backdrop-blur-sm shadow-lg border border-white/10", children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-medium text-sm text-white", children: [
42946
43227
  "AI Confidence: ",
@@ -42962,9 +43243,9 @@ var BottlenecksContent = ({
42962
43243
  ] }) }) : currentVideo.type === "idle_time" ? (
42963
43244
  // Show full colored badge for idle time
42964
43245
  (() => {
42965
- const rootCause = getIdleTimeRootCause(currentVideo);
43246
+ const classification = getIdleTimeClassification(currentVideo);
42966
43247
  const confidence = getIdleTimeConfidence(currentVideo);
42967
- const config = getRootCauseConfig(rootCause);
43248
+ const config = getRootCauseConfig(classification);
42968
43249
  if (!config) return null;
42969
43250
  const IconComponent = config.Icon;
42970
43251
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
@@ -42973,9 +43254,9 @@ var BottlenecksContent = ({
42973
43254
  (confidence * 100).toFixed(0),
42974
43255
  "%"
42975
43256
  ] }) }) }),
42976
- idleTimeVlmEnabled && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-4 right-20 z-50", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `inline-flex items-center gap-2 px-3 py-2 rounded-md border ${config.bgColor} ${config.borderColor} shadow-lg`, children: [
42977
- /* @__PURE__ */ jsxRuntime.jsx(IconComponent, { className: `h-4 w-4 ${config.iconColor}`, strokeWidth: 2.5 }),
42978
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: `font-medium text-sm ${config.color}`, children: rootCause })
43257
+ idleTimeVlmEnabled && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-4 right-20 z-50", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `inline-flex max-w-[20rem] items-center gap-2 rounded-md border px-3 py-2 shadow-lg ${config.bgColor} ${config.borderColor}`, children: [
43258
+ /* @__PURE__ */ jsxRuntime.jsx(IconComponent, { className: `h-4 w-4 flex-shrink-0 ${config.iconColor}`, strokeWidth: 2.5 }),
43259
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `truncate font-medium text-sm ${config.color}`, children: config.displayName || "Analyzing..." })
42979
43260
  ] }) })
42980
43261
  ] });
42981
43262
  })()
@@ -44924,16 +45205,6 @@ var LineHistoryCalendar = ({
44924
45205
  ] });
44925
45206
  };
44926
45207
  var LineHistoryCalendar_default = LineHistoryCalendar;
44927
- var STATIC_COLORS = {
44928
- "Operator Absent": "#dc2626",
44929
- // red-600 - Critical/Urgent
44930
- "No Material": "#f59e0b",
44931
- // amber-500 - Warning/Supply Chain
44932
- "Machine Downtime": "#3b82f6",
44933
- // blue-500 - Scheduled/Technical
44934
- "Operator Idle": "#8b5cf6"
44935
- // violet-500 - Low Priority/Behavioral
44936
- };
44937
45208
  var PRODUCTIVE_COLOR = "#00AB45";
44938
45209
  var IDLE_COLOR = "#e5e7eb";
44939
45210
  var formatDuration = (seconds) => {
@@ -44956,19 +45227,21 @@ var formatDuration = (seconds) => {
44956
45227
  }
44957
45228
  return parts.join(" ");
44958
45229
  };
44959
- var getColorForEntry = (name, index) => {
44960
- const normalized = name.trim().toLowerCase();
45230
+ var getColorForEntry = (entry) => {
45231
+ const normalized = entry.name.trim().toLowerCase();
44961
45232
  if (normalized === "productive" || normalized === "productive time") {
44962
45233
  return PRODUCTIVE_COLOR;
44963
45234
  }
44964
45235
  if (normalized === "idle" || normalized === "idle time") {
44965
45236
  return IDLE_COLOR;
44966
45237
  }
44967
- if (STATIC_COLORS[name]) {
44968
- return STATIC_COLORS[name];
44969
- }
44970
- const snakeCaseName = name.replace(/ /g, "_");
44971
- return getReasonColor(snakeCaseName, index);
45238
+ return getIdleReasonHexColor({
45239
+ label: entry.reasonKey || entry.name,
45240
+ displayName: entry.displayName || entry.name,
45241
+ paletteToken: entry.paletteToken,
45242
+ iconToken: entry.iconToken,
45243
+ isKnown: entry.isKnown
45244
+ });
44972
45245
  };
44973
45246
  var CustomTooltip = ({ active, payload, hideTotalDuration }) => {
44974
45247
  if (active && payload && payload.length) {
@@ -44982,8 +45255,8 @@ var CustomTooltip = ({ active, payload, hideTotalDuration }) => {
44982
45255
  const hasContributors = contributors.length > 0;
44983
45256
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white p-3 border border-gray-100 shadow-lg rounded-lg text-xs z-50 min-w-[280px]", children: [
44984
45257
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 pb-2 mb-2 border-b border-slate-100", children: [
44985
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-2.5 h-2.5 rounded-full flex-shrink-0", style: { backgroundColor: payload[0].payload.fill || getColorForEntry(payload[0].name, 0) } }),
44986
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-slate-800 text-[13px]", children: payload[0].name.replace(/_/g, " ") })
45258
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-2.5 h-2.5 rounded-full flex-shrink-0", style: { backgroundColor: payload[0].payload.fill || getColorForEntry(payload[0].payload) } }),
45259
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-slate-800 text-[13px]", children: datum?.displayName || payload[0].name.replace(/_/g, " ") })
44987
45260
  ] }),
44988
45261
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5 pb-1", children: [
44989
45262
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center gap-4", children: [
@@ -45044,7 +45317,8 @@ var IdleTimeReasonChartComponent = ({
45044
45317
  isLoading = false,
45045
45318
  error = null,
45046
45319
  hideTotalDuration = false,
45047
- updateAnimation = "replay"
45320
+ updateAnimation = "replay",
45321
+ variant = "pie"
45048
45322
  }) => {
45049
45323
  const [activeData, setActiveData] = React141__namespace.default.useState([]);
45050
45324
  React141__namespace.default.useEffect(() => {
@@ -45089,6 +45363,73 @@ var IdleTimeReasonChartComponent = ({
45089
45363
  if (!data || data.length === 0) {
45090
45364
  return /* @__PURE__ */ jsxRuntime.jsx(EmptyState, { message: "No Idle Time reasons available" });
45091
45365
  }
45366
+ if (variant === "bar") {
45367
+ return /* @__PURE__ */ jsxRuntime.jsx(
45368
+ "div",
45369
+ {
45370
+ className: "w-full h-full flex items-center overflow-visible focus:outline-none",
45371
+ tabIndex: -1,
45372
+ onFocus: (e) => e.currentTarget.blur(),
45373
+ children: /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(
45374
+ recharts.BarChart,
45375
+ {
45376
+ data: activeData,
45377
+ layout: "vertical",
45378
+ margin: { top: 5, right: 30, left: 10, bottom: 5 },
45379
+ barCategoryGap: 8,
45380
+ barGap: 2,
45381
+ children: [
45382
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.CartesianGrid, { strokeDasharray: "3 3", horizontal: false, vertical: true }),
45383
+ /* @__PURE__ */ jsxRuntime.jsx(
45384
+ recharts.XAxis,
45385
+ {
45386
+ type: "number",
45387
+ hide: false,
45388
+ domain: [0, 100],
45389
+ ticks: [0, 20, 40, 60, 80, 100],
45390
+ tickFormatter: (val) => `${val}%`,
45391
+ tick: { fontSize: 10, fill: "#6b7280" },
45392
+ axisLine: { stroke: "#e5e7eb" },
45393
+ tickLine: false
45394
+ }
45395
+ ),
45396
+ /* @__PURE__ */ jsxRuntime.jsx(
45397
+ recharts.YAxis,
45398
+ {
45399
+ type: "category",
45400
+ dataKey: "name",
45401
+ tick: { fontSize: 11, fill: "#4b5563" },
45402
+ tickFormatter: (val) => typeof val === "string" ? val.replace(/_/g, " ") : val,
45403
+ width: 90,
45404
+ axisLine: false,
45405
+ tickLine: false
45406
+ }
45407
+ ),
45408
+ /* @__PURE__ */ jsxRuntime.jsx(
45409
+ recharts.Tooltip,
45410
+ {
45411
+ cursor: { fill: "rgba(0,0,0,0.04)" },
45412
+ content: /* @__PURE__ */ jsxRuntime.jsx(CustomTooltip, { hideTotalDuration })
45413
+ }
45414
+ ),
45415
+ /* @__PURE__ */ jsxRuntime.jsx(
45416
+ recharts.Bar,
45417
+ {
45418
+ dataKey: "value",
45419
+ radius: [0, 4, 4, 0],
45420
+ animationDuration: 1500,
45421
+ animationEasing: "ease-out",
45422
+ isAnimationActive: true,
45423
+ barSize: 20,
45424
+ children: activeData.map((entry, index) => /* @__PURE__ */ jsxRuntime.jsx(recharts.Cell, { fill: getColorForEntry(entry) }, `cell-${index}`))
45425
+ }
45426
+ )
45427
+ ]
45428
+ }
45429
+ ) })
45430
+ }
45431
+ );
45432
+ }
45092
45433
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
45093
45434
  /* @__PURE__ */ jsxRuntime.jsx("style", { children: `
45094
45435
  .recharts-wrapper:focus,
@@ -45150,7 +45491,7 @@ var IdleTimeReasonChartComponent = ({
45150
45491
  children: activeData.map((entry, index) => /* @__PURE__ */ jsxRuntime.jsx(
45151
45492
  recharts.Cell,
45152
45493
  {
45153
- fill: getColorForEntry(entry.name, index),
45494
+ fill: getColorForEntry(entry),
45154
45495
  strokeWidth: 0
45155
45496
  },
45156
45497
  `cell-${index}`
@@ -45167,10 +45508,10 @@ var IdleTimeReasonChartComponent = ({
45167
45508
  "span",
45168
45509
  {
45169
45510
  className: "inline-block w-2.5 h-2.5 rounded-full mr-1.5 mt-0.5 flex-shrink-0",
45170
- style: { backgroundColor: getColorForEntry(entry.name, index) }
45511
+ style: { backgroundColor: getColorForEntry(entry) }
45171
45512
  }
45172
45513
  ),
45173
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-600 leading-tight text-[11px] sm:text-xs break-words", children: entry.name.replace(/_/g, " ") })
45514
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-600 leading-tight text-[11px] sm:text-xs break-words", children: entry.displayName || entry.name.replace(/_/g, " ") })
45174
45515
  ] }, `item-${index}`)) }) })
45175
45516
  ]
45176
45517
  }
@@ -45295,7 +45636,6 @@ var LineMonthlyHistory = ({
45295
45636
  const { isIdleTimeVlmEnabled } = useIdleTimeVlmConfig();
45296
45637
  const idleTimeVlmEnabled = isIdleTimeVlmEnabled(lineId);
45297
45638
  const isUptimeMode = monitoringMode === "uptime";
45298
- const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;
45299
45639
  const chartKey = React141.useMemo(() => `${lineId}-${month}-${year}-${selectedShiftId}-${rangeStart}-${rangeEnd}`, [lineId, month, year, selectedShiftId, rangeStart, rangeEnd]);
45300
45640
  const monthBounds = React141.useMemo(() => getMonthKeyBounds(year, month), [year, month]);
45301
45641
  const normalizedRange = React141.useMemo(() => {
@@ -45343,20 +45683,20 @@ var LineMonthlyHistory = ({
45343
45683
  { efficiency: 0, underperforming: 0, totalWorkspaces: 0, count: 0 }
45344
45684
  );
45345
45685
  const avgEfficiency = averages.count > 0 ? averages.efficiency / averages.count : 0;
45346
- const { underperformingDays, totalDays } = (analysisMonthlyData || []).reduce(
45686
+ const outputAverages = (analysisMonthlyData || []).reduce(
45347
45687
  (acc, day) => {
45348
45688
  const shiftData = getShiftData2(day, selectedShiftId);
45349
45689
  if (!shiftData || !hasRealData(shiftData)) {
45350
45690
  return acc;
45351
45691
  }
45352
- const status = getEfficiencyColor(shiftData.avg_efficiency || 0, effectiveLegend);
45353
45692
  return {
45354
- totalDays: acc.totalDays + 1,
45355
- underperformingDays: acc.underperformingDays + (status === "red" ? 1 : 0)
45693
+ output: acc.output + (shiftData.output || 0),
45694
+ count: acc.count + 1
45356
45695
  };
45357
45696
  },
45358
- { underperformingDays: 0, totalDays: 0 }
45697
+ { output: 0, count: 0 }
45359
45698
  );
45699
+ const avgOutput = outputAverages.count > 0 ? outputAverages.output / outputAverages.count : 0;
45360
45700
  const uptimeSummary = React141.useMemo(() => {
45361
45701
  if (!isUptimeMode) return null;
45362
45702
  const validDays = (analysisMonthlyData || []).map((day) => getShiftData2(day, selectedShiftId)).filter((shiftData) => shiftData && hasRealData(shiftData)).map((shiftData) => ({ shiftData, totals: getUptimeTotals(shiftData) }));
@@ -45389,6 +45729,10 @@ var LineMonthlyHistory = ({
45389
45729
  const efficiencyImproved = efficiencyDelta >= 0;
45390
45730
  const EfficiencyTrendIcon = efficiencyImproved ? lucideReact.ArrowUp : lucideReact.ArrowDown;
45391
45731
  const efficiencyTrendText = `${Math.abs(efficiencyDelta).toFixed(1)}%`;
45732
+ const outputDelta = trendSummary?.avg_daily_output?.delta_pp ?? 0;
45733
+ const outputImproved = outputDelta >= 0;
45734
+ const OutputTrendIcon = outputImproved ? lucideReact.ArrowUp : lucideReact.ArrowDown;
45735
+ const outputTrendText = `${Math.abs(outputDelta).toFixed(1)}%`;
45392
45736
  const utilizationDelta = efficiencyDelta;
45393
45737
  const utilizationImproved = utilizationDelta >= 0;
45394
45738
  const UtilizationTrendIcon = utilizationImproved ? lucideReact.ArrowUp : lucideReact.ArrowDown;
@@ -45673,12 +46017,17 @@ var LineMonthlyHistory = ({
45673
46017
  ] })
45674
46018
  ] }),
45675
46019
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-lg shadow-sm border border-gray-100 p-4 flex flex-col justify-between", children: [
45676
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-bold text-gray-700 mb-2", children: "No. of underperforming days" }),
45677
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2 flex-nowrap", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xl font-bold text-gray-900", children: [
45678
- underperformingDays,
45679
- "/",
45680
- totalDays
45681
- ] }) })
46020
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-bold text-gray-700 mb-2", children: "Avg. Output" }),
46021
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 flex-nowrap", children: [
46022
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xl font-bold text-gray-900", children: Math.round(avgOutput).toLocaleString() }),
46023
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-1 ${outputImproved ? "bg-emerald-50 text-emerald-600" : "bg-red-50 text-red-600"} px-1.5 py-0.5 rounded-full text-[10px] font-medium whitespace-nowrap flex-shrink-0`, children: [
46024
+ /* @__PURE__ */ jsxRuntime.jsx(OutputTrendIcon, { className: "w-3 h-3" }),
46025
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
46026
+ outputTrendText,
46027
+ " vs last month"
46028
+ ] })
46029
+ ] })
46030
+ ] })
45682
46031
  ] })
45683
46032
  ] }),
45684
46033
  isUptimeMode ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `grid grid-cols-1 ${idleTimeVlmEnabled ? "sm:grid-cols-2" : "sm:grid-cols-1"} gap-2 sm:gap-3 lg:gap-4`, children: [
@@ -46197,11 +46546,6 @@ var LineMonthlyPdfGenerator = ({
46197
46546
  doc.text("Working Days:", 25, kpiStartY + kpiSpacing * 2);
46198
46547
  doc.setFont("helvetica", "bold");
46199
46548
  doc.text(`${outputMetrics.totalDays} days`, 120, kpiStartY + kpiSpacing * 2);
46200
- createKPIBox(kpiStartY + kpiSpacing * 3);
46201
- doc.setFont("helvetica", "normal");
46202
- doc.text("Underperforming Days:", 25, kpiStartY + kpiSpacing * 3);
46203
- doc.setFont("helvetica", "bold");
46204
- doc.text(`${outputMetrics.underperformingDays} of ${outputMetrics.totalDays}`, 120, kpiStartY + kpiSpacing * 3);
46205
46549
  }
46206
46550
  } else {
46207
46551
  doc.setFontSize(12);
@@ -47779,7 +48123,8 @@ var WorkspaceMonthlyHistory = ({
47779
48123
  availableShifts,
47780
48124
  monthlyDataLoading = false,
47781
48125
  className = "",
47782
- trendSummary
48126
+ trendSummary,
48127
+ isAssemblyWorkspace = false
47783
48128
  }) => {
47784
48129
  const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;
47785
48130
  const isUptimeMode = monitoringMode === "uptime";
@@ -48018,12 +48363,8 @@ var WorkspaceMonthlyHistory = ({
48018
48363
  }, [analysisMonthlyData, selectedShiftId, isUptimeMode, shiftWorkSeconds]);
48019
48364
  const efficiencyDelta = trendSummary?.avg_efficiency?.delta_pp ?? 0;
48020
48365
  const efficiencyImproved = efficiencyDelta >= 0;
48021
- const outputDeltaRaw = trendSummary?.avg_daily_output?.delta_pp ?? 0;
48022
48366
  const cycleDeltaRaw = trendSummary?.avg_cycle_time?.delta_seconds ?? 0;
48023
- const outputPrev = trendSummary?.avg_daily_output?.previous ?? 0;
48024
48367
  const cyclePrev = trendSummary?.avg_cycle_time?.previous ?? 0;
48025
- const outputDelta = outputPrev ? outputDeltaRaw / outputPrev * 100 : 0;
48026
- const outputImproved = outputDelta >= 0;
48027
48368
  const cycleDelta = cyclePrev ? cycleDeltaRaw / cyclePrev * 100 : 0;
48028
48369
  const cycleWorsened = cycleDelta > 0;
48029
48370
  const utilizationDelta = efficiencyDelta;
@@ -48103,7 +48444,7 @@ var WorkspaceMonthlyHistory = ({
48103
48444
  shift.id
48104
48445
  )) }) }),
48105
48446
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-2 gap-6", children: [
48106
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-100 p-4 sm:p-6", children: [
48447
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-100 p-4 sm:p-6 flex flex-col", children: [
48107
48448
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-7 gap-2", children: WEEKDAYS3.map((day, idx) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-medium text-gray-500 dark:text-gray-400 text-center", children: day }, day)) }),
48108
48449
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-7 gap-2 mt-6", children: calendarData.calendar.map((day, index) => {
48109
48450
  const dayNumber = index >= calendarData.startOffset ? index - calendarData.startOffset + 1 : null;
@@ -48191,8 +48532,32 @@ var WorkspaceMonthlyHistory = ({
48191
48532
  ) }, index);
48192
48533
  }) })
48193
48534
  ] }),
48194
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
48195
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-4", children: [
48535
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4 h-full", children: [
48536
+ isAssemblyWorkspace && !isUptimeMode ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-4 md:grid-cols-2", children: [
48537
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-lg shadow-sm border border-gray-100 p-4 flex flex-col justify-between", children: [
48538
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-semibold text-gray-600 mb-1", children: "Avg Idle Time" }),
48539
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 flex-nowrap", children: [
48540
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-2xl font-bold text-gray-900", children: formatIdleTime(metrics2?.avgIdleTime ?? 0) }),
48541
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-1 ${idleImproved ? "bg-emerald-50 text-emerald-600" : "bg-red-50 text-red-600"} px-2 py-0.5 rounded-full text-[10px] font-medium whitespace-nowrap flex-shrink-0`, children: [
48542
+ idleImproved ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDown, { className: "w-3 h-3" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "w-3 h-3" }),
48543
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: idleTrendText })
48544
+ ] })
48545
+ ] })
48546
+ ] }),
48547
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-lg shadow-sm border border-gray-100 p-4 flex flex-col justify-between", children: [
48548
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-semibold text-gray-600 mb-1", children: "Avg Cycle Time" }),
48549
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 flex-nowrap", children: [
48550
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-2xl font-bold text-gray-900", children: [
48551
+ metrics2?.avgCycleTime ?? 0,
48552
+ "s"
48553
+ ] }),
48554
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-1 ${cycleWorsened ? "bg-red-50 text-red-600" : "bg-emerald-50 text-emerald-600"} px-2 py-0.5 rounded-full text-[10px] font-medium whitespace-nowrap flex-shrink-0`, children: [
48555
+ cycleWorsened ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "w-3 h-3" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDown, { className: "w-3 h-3" }),
48556
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: `${Math.abs(cycleDelta).toFixed(1)}% vs last month` })
48557
+ ] })
48558
+ ] })
48559
+ ] })
48560
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `grid grid-cols-1 gap-4 ${isUptimeMode ? "md:grid-cols-3" : "md:grid-cols-2"}`, children: [
48196
48561
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-lg shadow-sm border border-gray-100 p-4 flex flex-col justify-between", children: [
48197
48562
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-semibold text-gray-600 mb-1", children: isUptimeMode ? "Avg Utilization" : "Avg Efficiency" }),
48198
48563
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 flex-nowrap", children: [
@@ -48206,16 +48571,13 @@ var WorkspaceMonthlyHistory = ({
48206
48571
  ] })
48207
48572
  ] })
48208
48573
  ] }),
48209
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-lg shadow-sm border border-gray-100 p-4 flex flex-col justify-between", children: [
48210
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-semibold text-gray-600 mb-1", children: isUptimeMode ? "Avg Idle Time" : "Avg Daily Output" }),
48574
+ isUptimeMode && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-lg shadow-sm border border-gray-100 p-4 flex flex-col justify-between", children: [
48575
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-semibold text-gray-600 mb-1", children: "Avg Idle Time" }),
48211
48576
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 flex-nowrap", children: [
48212
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-2xl font-bold text-gray-900", children: isUptimeMode ? formatIdleTime(metrics2?.avgIdleTime ?? 0) : metrics2?.avgDailyOutput?.toLocaleString?.() ?? 0 }),
48213
- isUptimeMode ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-1 ${idleImproved ? "bg-emerald-50 text-emerald-600" : "bg-red-50 text-red-600"} px-2 py-0.5 rounded-full text-[10px] font-medium whitespace-nowrap flex-shrink-0`, children: [
48577
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-2xl font-bold text-gray-900", children: formatIdleTime(metrics2?.avgIdleTime ?? 0) }),
48578
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-1 ${idleImproved ? "bg-emerald-50 text-emerald-600" : "bg-red-50 text-red-600"} px-2 py-0.5 rounded-full text-[10px] font-medium whitespace-nowrap flex-shrink-0`, children: [
48214
48579
  idleImproved ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDown, { className: "w-3 h-3" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "w-3 h-3" }),
48215
48580
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: idleTrendText })
48216
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-1 ${outputImproved ? "bg-emerald-50 text-emerald-600" : "bg-red-50 text-red-600"} px-2 py-0.5 rounded-full text-[10px] font-medium whitespace-nowrap flex-shrink-0`, children: [
48217
- outputImproved ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "w-3 h-3" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDown, { className: "w-3 h-3" }),
48218
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: `${Math.abs(outputDelta).toFixed(1)}% vs last month` })
48219
48581
  ] })
48220
48582
  ] })
48221
48583
  ] }),
@@ -48237,9 +48599,9 @@ var WorkspaceMonthlyHistory = ({
48237
48599
  ] })
48238
48600
  ] }),
48239
48601
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `grid grid-cols-1 ${idleTimeVlmEnabled ? "sm:grid-cols-2" : "sm:grid-cols-1"} gap-4`, children: [
48240
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-lg shadow-sm border border-gray-100 p-4", children: [
48602
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-lg shadow-sm border border-gray-100 p-4 flex flex-col", children: [
48241
48603
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-bold text-gray-700 mb-3 text-left", children: isUptimeMode ? "Utilization" : "Time Utilization" }),
48242
- pieChartData.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full h-[160px] flex items-center overflow-hidden", children: [
48604
+ pieChartData.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full flex items-center overflow-hidden h-[160px]", children: [
48243
48605
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 h-full min-w-0 relative flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full aspect-square max-h-full", style: { maxWidth: "min(100%, 280px)", containerType: "inline-size" }, children: [
48244
48606
  /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsx(recharts.PieChart, { children: /* @__PURE__ */ jsxRuntime.jsx(
48245
48607
  recharts.Pie,
@@ -48312,7 +48674,7 @@ var WorkspaceMonthlyHistory = ({
48312
48674
  ] }) })
48313
48675
  ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center h-[160px]", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-400", children: "No data available" }) })
48314
48676
  ] }),
48315
- idleTimeVlmEnabled && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-lg shadow-sm border border-gray-100 p-4 h-full", children: [
48677
+ idleTimeVlmEnabled && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-lg shadow-sm border border-gray-100 p-4 h-full flex flex-col", children: [
48316
48678
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-bold text-gray-700 mb-3 text-left", children: "Idle Time Breakdown" }),
48317
48679
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[160px]", children: /* @__PURE__ */ jsxRuntime.jsx(
48318
48680
  IdleTimeReasonChart,
@@ -48324,7 +48686,7 @@ var WorkspaceMonthlyHistory = ({
48324
48686
  ) })
48325
48687
  ] })
48326
48688
  ] }),
48327
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-lg shadow-sm border border-gray-100 p-4 flex-1", children: [
48689
+ (!isAssemblyWorkspace || isUptimeMode) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-lg shadow-sm border border-gray-100 p-4 flex-1", children: [
48328
48690
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-bold text-gray-700 mb-3 text-left", children: isUptimeMode ? "Daily Utilization" : "Daily Output" }),
48329
48691
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: { height: "220px" }, children: /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(
48330
48692
  recharts.BarChart,
@@ -48522,7 +48884,7 @@ var WorkspaceWhatsAppShareButton = ({
48522
48884
  }
48523
48885
  );
48524
48886
  };
48525
- var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiencyLegend }) => {
48887
+ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiencyLegend, hourlyCycleTimes }) => {
48526
48888
  const [isGenerating, setIsGenerating] = React141.useState(false);
48527
48889
  const entityConfig = useEntityConfig();
48528
48890
  const effectiveLegend = efficiencyLegend || DEFAULT_EFFICIENCY_LEGEND;
@@ -48530,6 +48892,7 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
48530
48892
  setIsGenerating(true);
48531
48893
  try {
48532
48894
  const isUptimeMode = workspace.monitoring_mode === "uptime";
48895
+ const isAssemblyCycleMode = !isUptimeMode && workspace.line_assembly_enabled === true && workspace.action_type === "assembly";
48533
48896
  const shiftMinutes = getShiftDurationMinutes(workspace.shift_start, workspace.shift_end);
48534
48897
  const shiftSeconds = shiftMinutes ? shiftMinutes * 60 : 0;
48535
48898
  const idleSeconds = Math.max(workspace.idle_time || 0, 0);
@@ -48590,9 +48953,38 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
48590
48953
  minute: "2-digit",
48591
48954
  hour12: true
48592
48955
  });
48956
+ const shiftEndTime = (/* @__PURE__ */ new Date(`2000-01-01 ${workspace.shift_end}`)).toLocaleTimeString("en-IN", {
48957
+ hour: "2-digit",
48958
+ minute: "2-digit",
48959
+ hour12: true
48960
+ });
48961
+ const parseTimeToMinutes3 = (timeValue) => {
48962
+ const [hourPart, minutePart] = timeValue.split(":").map(Number);
48963
+ const hour = Number.isFinite(hourPart) ? hourPart : 0;
48964
+ const minute = Number.isFinite(minutePart) ? minutePart : 0;
48965
+ return hour * 60 + minute;
48966
+ };
48967
+ const toShiftUtcMs = (dateKey, timeValue) => {
48968
+ const [yearPart, monthPart, dayPart] = dateKey.split("-").map(Number);
48969
+ const year = Number.isFinite(yearPart) ? yearPart : 1970;
48970
+ const month = Number.isFinite(monthPart) ? monthPart : 1;
48971
+ const day = Number.isFinite(dayPart) ? dayPart : 1;
48972
+ const [hourPart, minutePart] = timeValue.split(":").map(Number);
48973
+ const hour = Number.isFinite(hourPart) ? hourPart : 0;
48974
+ const minute = Number.isFinite(minutePart) ? minutePart : 0;
48975
+ const IST_OFFSET_MINUTES = 330;
48976
+ return Date.UTC(year, month - 1, day, hour, minute) - IST_OFFSET_MINUTES * 60 * 1e3;
48977
+ };
48978
+ const shiftStartMinutes = parseTimeToMinutes3(workspace.shift_start);
48979
+ const shiftEndMinutes = parseTimeToMinutes3(workspace.shift_end);
48980
+ const wrapsMidnight = shiftEndMinutes <= shiftStartMinutes;
48981
+ const shiftStartUtcMs = toShiftUtcMs(workspace.date, workspace.shift_start);
48982
+ const shiftEndUtcMs = toShiftUtcMs(workspace.date, workspace.shift_end) + (wrapsMidnight ? 24 * 60 * 60 * 1e3 : 0);
48983
+ const isShiftInProgress = Date.now() >= shiftStartUtcMs && Date.now() < shiftEndUtcMs;
48984
+ const reportPeriodEndTime = isShiftInProgress ? currentTime : shiftEndTime;
48593
48985
  doc.setFontSize(12);
48594
48986
  doc.setTextColor(80, 80, 80);
48595
- doc.text(`Report Period: ${shiftStartTime} - ${currentTime}`, 20, 79);
48987
+ doc.text(`Report Period: ${shiftStartTime} - ${reportPeriodEndTime}`, 20, 79);
48596
48988
  doc.setTextColor(0, 0, 0);
48597
48989
  doc.setDrawColor(180, 180, 180);
48598
48990
  doc.setLineWidth(0.8);
@@ -48606,10 +48998,8 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
48606
48998
  };
48607
48999
  const perfOverviewStartY = 93;
48608
49000
  const hasIdleTimeReason = idleTimeReasons && idleTimeReasons.length > 0;
48609
- let perfOverviewHeight = hasIdleTimeReason ? 80 : 70;
48610
- if (isUptimeMode) {
48611
- perfOverviewHeight = hasIdleTimeReason ? 70 : 60;
48612
- }
49001
+ const perfOverviewRows = isUptimeMode ? 3 : isAssemblyCycleMode ? 2 : 4;
49002
+ const perfOverviewHeight = 30 + perfOverviewRows * 10 + (hasIdleTimeReason ? 10 : 0);
48613
49003
  doc.setFillColor(245, 245, 245);
48614
49004
  doc.roundedRect(15, perfOverviewStartY, 180, perfOverviewHeight, 3, 3, "F");
48615
49005
  doc.setFontSize(18);
@@ -48637,6 +49027,19 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
48637
49027
  doc.text("Stoppages:", 25, kpiStartY + kpiSpacing * 2);
48638
49028
  doc.setFont("helvetica", "bold");
48639
49029
  doc.text(`${workspace.total_actions}`, 120, kpiStartY + kpiSpacing * 2);
49030
+ } else if (isAssemblyCycleMode) {
49031
+ createKPIBox(kpiStartY);
49032
+ doc.setFontSize(11);
49033
+ doc.setFont("helvetica", "normal");
49034
+ doc.text("Average Cycle Time:", 25, kpiStartY);
49035
+ doc.setFont("helvetica", "bold");
49036
+ doc.text(`${(workspace.avg_cycle_time || 0).toFixed(1)}s`, 120, kpiStartY);
49037
+ createKPIBox(kpiStartY + kpiSpacing);
49038
+ doc.setFont("helvetica", "normal");
49039
+ doc.text("Total Idle Time:", 25, kpiStartY + kpiSpacing);
49040
+ doc.setFont("helvetica", "bold");
49041
+ const idleTimeFormatted = formatIdleTime(workspace.idle_time);
49042
+ doc.text(idleTimeFormatted, 120, kpiStartY + kpiSpacing);
48640
49043
  } else {
48641
49044
  createKPIBox(kpiStartY);
48642
49045
  doc.setFontSize(11);
@@ -48673,17 +49076,11 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
48673
49076
  const reasonText = `${reasonName} (${topReason.value.toFixed(1)}%)`;
48674
49077
  doc.text(reasonText, 120, reasonY);
48675
49078
  }
48676
- let separatorBeforeHourlyY = hasIdleTimeReason ? 183 : 173;
48677
- if (isUptimeMode) {
48678
- separatorBeforeHourlyY -= 10;
48679
- }
49079
+ const separatorBeforeHourlyY = perfOverviewStartY + perfOverviewHeight + 10;
48680
49080
  doc.setDrawColor(180, 180, 180);
48681
49081
  doc.setLineWidth(0.8);
48682
49082
  doc.line(20, separatorBeforeHourlyY, 190, separatorBeforeHourlyY);
48683
- let hourlyPerfStartY = hasIdleTimeReason ? 188 : 178;
48684
- if (isUptimeMode) {
48685
- hourlyPerfStartY -= 10;
48686
- }
49083
+ const hourlyPerfStartY = separatorBeforeHourlyY + 5;
48687
49084
  const uptimeSeries = isUptimeMode ? buildUptimeSeries({
48688
49085
  idleTimeHourly: workspace.idle_time_hourly,
48689
49086
  shiftStart: workspace.shift_start,
@@ -48701,8 +49098,9 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
48701
49098
  const uptimePercent = total > 0 ? Math.round(activeMinutes / total * 100) : 0;
48702
49099
  return { activeMinutes, idleMinutes, uptimePercent };
48703
49100
  }) : [];
48704
- const hourlyData = isUptimeMode ? hourlyUptime : workspace.hourly_action_counts || [];
49101
+ const hourlyData = isUptimeMode ? hourlyUptime : isAssemblyCycleMode ? hourlyCycleTimes && hourlyCycleTimes.length > 0 ? hourlyCycleTimes : workspace.hourly_action_counts || [] : workspace.hourly_action_counts || [];
48705
49102
  const hourlyTarget = workspace.pph_threshold;
49103
+ const cycleTarget = workspace.ideal_cycle_time || 0;
48706
49104
  const pageHeight = doc.internal.pageSize.height;
48707
49105
  const maxContentY = pageHeight - 15;
48708
49106
  const baseTableStartY = hourlyPerfStartY + 31;
@@ -48727,12 +49125,16 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
48727
49125
  doc.setFont("helvetica", "bold");
48728
49126
  doc.setTextColor(40, 40, 40);
48729
49127
  const hourlyTitleY = hourlyPerfStartY + 10;
48730
- doc.text(isUptimeMode ? "Hourly Utilization" : "Hourly Performance", 20, hourlyTitleY);
49128
+ doc.text(
49129
+ isUptimeMode ? "Hourly Utilization" : isAssemblyCycleMode ? "Hourly Cycle Time" : "Hourly Performance",
49130
+ 20,
49131
+ hourlyTitleY
49132
+ );
48731
49133
  doc.setTextColor(0, 0, 0);
48732
49134
  const headerY = titleFontSize === 16 ? hourlyPerfStartY + 18 : hourlyPerfStartY + 20;
48733
49135
  const gridTopY = headerY - 5;
48734
49136
  const headerBottomY = gridTopY + 8;
48735
- const colBoundaries = isUptimeMode ? [20, 105, 190] : [20, 70, 100, 130, 155, 190];
49137
+ const colBoundaries = isUptimeMode ? [20, 105, 190] : isAssemblyCycleMode ? [20, 85, 125, 165, 190] : [20, 70, 100, 130, 155, 190];
48736
49138
  const totalRows = hourlyData.length;
48737
49139
  const gridBottomY = headerBottomY + totalRows * rowHeight;
48738
49140
  const tableHeight = gridBottomY - gridTopY;
@@ -48753,6 +49155,10 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
48753
49155
  doc.text("Time Range", 25, headerTextY);
48754
49156
  if (isUptimeMode) {
48755
49157
  doc.text("Utilization", 147, headerTextY);
49158
+ } else if (isAssemblyCycleMode) {
49159
+ doc.text("Standard", 90, headerTextY);
49160
+ doc.text("Average", 130, headerTextY);
49161
+ doc.text("Status", 170, headerTextY);
48756
49162
  } else {
48757
49163
  doc.text("Output", 75, headerTextY);
48758
49164
  doc.text("Target", 105, headerTextY);
@@ -48801,6 +49207,25 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
48801
49207
  if (isUptimeMode) {
48802
49208
  const utilizationStr = dataCollected ? `${uptimePercent}%` : "TBD";
48803
49209
  doc.text(utilizationStr, 147, yPos);
49210
+ } else if (isAssemblyCycleMode) {
49211
+ const actualCycleTime = Number(outputValue) || 0;
49212
+ const standardCycleStr = `${cycleTarget.toFixed(1)}s`;
49213
+ const actualCycleStr = dataCollected ? `${actualCycleTime.toFixed(1)}s` : "TBD";
49214
+ doc.text(standardCycleStr, 90, yPos);
49215
+ doc.text(actualCycleStr, 130, yPos);
49216
+ if (!dataCollected) {
49217
+ doc.setTextColor(100, 100, 100);
49218
+ doc.text("-", 170, yPos);
49219
+ } else if (actualCycleTime > 0 && actualCycleTime <= cycleTarget) {
49220
+ doc.setTextColor(0, 171, 69);
49221
+ doc.setFont("ZapfDingbats", "normal");
49222
+ doc.text("4", 170, yPos);
49223
+ doc.setFont("helvetica", "normal");
49224
+ } else {
49225
+ doc.setTextColor(227, 67, 41);
49226
+ doc.text("\xD7", 170, yPos);
49227
+ }
49228
+ doc.setTextColor(0, 0, 0);
48804
49229
  } else {
48805
49230
  doc.text(outputStr, 75, yPos);
48806
49231
  doc.text(targetStr, 105, yPos);
@@ -48872,7 +49297,8 @@ var WorkspaceMonthlyPdfGenerator = ({
48872
49297
  shiftConfig,
48873
49298
  efficiencyLegend,
48874
49299
  className,
48875
- compact = false
49300
+ compact = false,
49301
+ isAssemblyWorkspace = false
48876
49302
  }) => {
48877
49303
  const [isGenerating, setIsGenerating] = React141.useState(false);
48878
49304
  const effectiveLegend = efficiencyLegend || DEFAULT_EFFICIENCY_LEGEND;
@@ -48995,6 +49421,7 @@ var WorkspaceMonthlyPdfGenerator = ({
48995
49421
  avgOutput: filteredShifts.reduce((sum, shift) => sum + shift.output, 0) / filteredShifts.length,
48996
49422
  avgCycleTime: filteredShifts.reduce((sum, shift) => sum + shift.cycleTime, 0) / filteredShifts.length,
48997
49423
  avgPph: filteredShifts.reduce((sum, shift) => sum + shift.pph, 0) / filteredShifts.length,
49424
+ avgIdleTime: filteredShifts.reduce((sum, shift) => sum + shift.idleTime, 0) / filteredShifts.length,
48998
49425
  totalDays: filteredShifts.length,
48999
49426
  underperformingDays: filteredShifts.filter((shift) => shift.efficiency < effectiveLegend.green_min).length
49000
49427
  } : null;
@@ -49006,7 +49433,8 @@ var WorkspaceMonthlyPdfGenerator = ({
49006
49433
  doc.roundedRect(22, y - 7, 165, 12, 2, 2, "S");
49007
49434
  };
49008
49435
  doc.setFillColor(245, 245, 245);
49009
- doc.roundedRect(15, 95, 180, 70, 3, 3, "F");
49436
+ const isAssemblyWorkspaceAndNotUptime = !isUptimeMode && isAssemblyWorkspace;
49437
+ doc.roundedRect(15, 95, 180, isAssemblyWorkspaceAndNotUptime ? 40 : 70, 3, 3, "F");
49010
49438
  doc.setFontSize(18);
49011
49439
  doc.setFont("helvetica", "bold");
49012
49440
  doc.setTextColor(40, 40, 40);
@@ -49045,32 +49473,41 @@ var WorkspaceMonthlyPdfGenerator = ({
49045
49473
  doc.text(`${uptimeMetrics.underperformingDays} of ${uptimeMetrics.totalDays}`, 120, kpiStartY + kpiSpacing * 4);
49046
49474
  } else {
49047
49475
  const outputMetrics = monthlyMetrics;
49048
- createKPIBox(kpiStartY);
49049
- doc.setFontSize(11);
49050
- doc.setFont("helvetica", "normal");
49051
- doc.text("Average Efficiency:", 25, kpiStartY);
49052
- doc.setFont("helvetica", "bold");
49053
- doc.text(`${outputMetrics.avgEfficiency.toFixed(1)}% (Target: ${Math.round(effectiveLegend.green_min)}%)`, 120, kpiStartY);
49054
- createKPIBox(kpiStartY + kpiSpacing);
49055
- doc.setFont("helvetica", "normal");
49056
- doc.text("Average Output:", 25, kpiStartY + kpiSpacing);
49057
- doc.setFont("helvetica", "bold");
49058
- doc.text(`${Math.round(outputMetrics.avgOutput)} pieces`, 120, kpiStartY + kpiSpacing);
49059
- createKPIBox(kpiStartY + kpiSpacing * 2);
49060
- doc.setFont("helvetica", "normal");
49061
- doc.text("Average PPH:", 25, kpiStartY + kpiSpacing * 2);
49062
- doc.setFont("helvetica", "bold");
49063
- doc.text(`${outputMetrics.avgPph.toFixed(1)} per hour`, 120, kpiStartY + kpiSpacing * 2);
49064
- createKPIBox(kpiStartY + kpiSpacing * 3);
49065
- doc.setFont("helvetica", "normal");
49066
- doc.text("Working Days:", 25, kpiStartY + kpiSpacing * 3);
49067
- doc.setFont("helvetica", "bold");
49068
- doc.text(`${outputMetrics.totalDays} days`, 120, kpiStartY + kpiSpacing * 3);
49069
- createKPIBox(kpiStartY + kpiSpacing * 4);
49070
- doc.setFont("helvetica", "normal");
49071
- doc.text("Underperforming Days:", 25, kpiStartY + kpiSpacing * 4);
49072
- doc.setFont("helvetica", "bold");
49073
- doc.text(`${outputMetrics.underperformingDays} of ${outputMetrics.totalDays}`, 120, kpiStartY + kpiSpacing * 4);
49476
+ if (!isAssemblyWorkspace) {
49477
+ createKPIBox(kpiStartY);
49478
+ doc.setFontSize(11);
49479
+ doc.setFont("helvetica", "normal");
49480
+ doc.text("Average Efficiency:", 25, kpiStartY);
49481
+ doc.setFont("helvetica", "bold");
49482
+ doc.text(`${outputMetrics.avgEfficiency.toFixed(1)}% (Target: ${Math.round(effectiveLegend.green_min)}%)`, 120, kpiStartY);
49483
+ createKPIBox(kpiStartY + kpiSpacing);
49484
+ doc.setFont("helvetica", "normal");
49485
+ doc.text("Average PPH:", 25, kpiStartY + kpiSpacing);
49486
+ doc.setFont("helvetica", "bold");
49487
+ doc.text(`${outputMetrics.avgPph.toFixed(1)} per hour`, 120, kpiStartY + kpiSpacing);
49488
+ createKPIBox(kpiStartY + kpiSpacing * 2);
49489
+ doc.setFont("helvetica", "normal");
49490
+ doc.text("Working Days:", 25, kpiStartY + kpiSpacing * 2);
49491
+ doc.setFont("helvetica", "bold");
49492
+ doc.text(`${outputMetrics.totalDays} days`, 120, kpiStartY + kpiSpacing * 2);
49493
+ createKPIBox(kpiStartY + kpiSpacing * 3);
49494
+ doc.setFont("helvetica", "normal");
49495
+ doc.text("Underperforming Days:", 25, kpiStartY + kpiSpacing * 3);
49496
+ doc.setFont("helvetica", "bold");
49497
+ doc.text(`${outputMetrics.underperformingDays} of ${outputMetrics.totalDays}`, 120, kpiStartY + kpiSpacing * 3);
49498
+ } else {
49499
+ createKPIBox(kpiStartY);
49500
+ doc.setFontSize(11);
49501
+ doc.setFont("helvetica", "normal");
49502
+ doc.text("Average Cycle Time:", 25, kpiStartY);
49503
+ doc.setFont("helvetica", "bold");
49504
+ doc.text(`${Math.round(outputMetrics.avgCycleTime)}s`, 120, kpiStartY);
49505
+ createKPIBox(kpiStartY + kpiSpacing);
49506
+ doc.setFont("helvetica", "normal");
49507
+ doc.text("Average Idle Time:", 25, kpiStartY + kpiSpacing);
49508
+ doc.setFont("helvetica", "bold");
49509
+ doc.text(formatIdleTime(outputMetrics.avgIdleTime ?? 0), 120, kpiStartY + kpiSpacing);
49510
+ }
49074
49511
  }
49075
49512
  } else {
49076
49513
  doc.setFontSize(12);
@@ -49081,29 +49518,33 @@ var WorkspaceMonthlyPdfGenerator = ({
49081
49518
  }
49082
49519
  doc.setDrawColor(180, 180, 180);
49083
49520
  doc.setLineWidth(0.8);
49084
- doc.line(20, 180, 190, 180);
49521
+ const separatorY = isAssemblyWorkspaceAndNotUptime ? 150 : 180;
49522
+ doc.line(20, separatorY, 190, separatorY);
49085
49523
  doc.setFillColor(245, 245, 245);
49086
- doc.roundedRect(15, 185, 180, 85, 3, 3, "F");
49524
+ const dailySectionY = isAssemblyWorkspaceAndNotUptime ? 155 : 185;
49525
+ doc.roundedRect(15, dailySectionY, 180, 85, 3, 3, "F");
49087
49526
  doc.setFontSize(18);
49088
49527
  doc.setFont("helvetica", "bold");
49089
49528
  doc.setTextColor(40, 40, 40);
49090
- doc.text(isUptimeMode ? "Daily Utilization Summary" : "Daily Performance Summary", 20, 195);
49529
+ doc.text(isUptimeMode ? "Daily Utilization Summary" : "Daily Performance Summary", 20, dailySectionY + 10);
49091
49530
  doc.setTextColor(0, 0, 0);
49092
49531
  if (validDays.length > 0) {
49093
49532
  doc.setFontSize(10);
49094
49533
  doc.setFont("helvetica", "bold");
49095
49534
  doc.setFillColor(240, 240, 240);
49096
- doc.roundedRect(20, 200, 170, 7, 1, 1, "F");
49097
- doc.text("Date", 25, 205);
49098
- doc.text(isUptimeMode ? "Productive" : "Actual", 60, 205);
49099
- doc.text(isUptimeMode ? "Idle" : "Standard", 95, 205);
49100
- doc.text(isUptimeMode ? "Utilization" : "Efficiency", 135, 205);
49101
- doc.text("Status", 170, 205);
49535
+ const tableHeaderY = dailySectionY + 15;
49536
+ doc.roundedRect(20, tableHeaderY, 170, 7, 1, 1, "F");
49537
+ const textY = tableHeaderY + 5;
49538
+ doc.text("Date", 25, textY);
49539
+ doc.text(isUptimeMode ? "Productive" : isAssemblyWorkspace ? "Cycle Time" : "Actual", 60, textY);
49540
+ doc.text(isUptimeMode ? "Idle" : isAssemblyWorkspace ? "Target CT" : "Standard", 95, textY);
49541
+ doc.text(isUptimeMode ? "Utilization" : "Efficiency", 135, textY);
49542
+ doc.text("Status", 170, textY);
49102
49543
  doc.setLineWidth(0.2);
49103
49544
  doc.setDrawColor(220, 220, 220);
49104
- doc.line(20, 208, 190, 208);
49545
+ doc.line(20, textY + 3, 190, textY + 3);
49105
49546
  doc.setFont("helvetica", "normal");
49106
- let yPos = 215;
49547
+ let yPos = textY + 10;
49107
49548
  const recentDays = validDays.slice(-10).reverse();
49108
49549
  recentDays.forEach((dayData, index) => {
49109
49550
  if (yPos > 260) return;
@@ -49137,8 +49578,13 @@ var WorkspaceMonthlyPdfGenerator = ({
49137
49578
  }
49138
49579
  doc.setTextColor(0, 0, 0);
49139
49580
  } else {
49140
- doc.text(`${shift.output}`, 60, yPos);
49141
- doc.text(`${shift.targetOutput}`, 95, yPos);
49581
+ if (isAssemblyWorkspace) {
49582
+ doc.text(`${shift.cycleTime.toFixed(1)}`, 60, yPos);
49583
+ doc.text(`${shift.pphThreshold > 0 ? (3600 / shift.pphThreshold).toFixed(1) : "-"}`, 95, yPos);
49584
+ } else {
49585
+ doc.text(`${shift.output}`, 60, yPos);
49586
+ doc.text(`${shift.targetOutput}`, 95, yPos);
49587
+ }
49142
49588
  doc.text(`${shift.efficiency.toFixed(1)}%`, 135, yPos);
49143
49589
  if (shift.efficiency >= effectiveLegend.green_min) {
49144
49590
  doc.setTextColor(0, 171, 69);
@@ -49153,12 +49599,12 @@ var WorkspaceMonthlyPdfGenerator = ({
49153
49599
  });
49154
49600
  doc.setLineWidth(0.2);
49155
49601
  doc.setDrawColor(220, 220, 220);
49156
- doc.roundedRect(20, 200, 170, yPos - 200 - 3, 1, 1, "S");
49602
+ doc.roundedRect(20, tableHeaderY, 170, yPos - tableHeaderY - 3, 1, 1, "S");
49157
49603
  } else {
49158
49604
  doc.setFontSize(12);
49159
49605
  doc.setFont("helvetica", "normal");
49160
49606
  doc.setTextColor(100, 100, 100);
49161
- doc.text("No daily data available for this month", 25, 215);
49607
+ doc.text("No daily data available for this month", 25, dailySectionY + 30);
49162
49608
  doc.setTextColor(0, 0, 0);
49163
49609
  }
49164
49610
  doc.setFontSize(9);
@@ -49191,46 +49637,64 @@ var WorkspaceCycleTimeMetricCards = ({
49191
49637
  workspace,
49192
49638
  className,
49193
49639
  legend,
49194
- layout: layout2 = "grid"
49640
+ layout: layout2 = "grid",
49641
+ isAssemblyWorkspace = false,
49642
+ idleTimeData
49195
49643
  }) => {
49196
49644
  const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;
49197
49645
  const efficiencyValue = workspace.avg_efficiency || 0;
49198
49646
  const efficiencyTarget = effectiveLegend.green_min;
49199
49647
  const efficiencyColor = getEfficiencyHexColor(efficiencyValue, effectiveLegend);
49200
- const hideEfficiencyCard = shouldHideWorkspaceEfficiencyCard(workspace);
49201
- const containerClassName = layout2 === "stack" ? `space-y-4 ${className || ""}` : `grid grid-cols-1 sm:grid-cols-2 ${hideEfficiencyCard ? "lg:grid-cols-2" : "lg:grid-cols-3"} gap-3 min-h-0 ${className || ""}`;
49648
+ const hideEfficiencyCard = isAssemblyWorkspace || shouldHideWorkspaceEfficiencyCard(workspace);
49649
+ const totalCards = 2 + (hideEfficiencyCard ? 0 : 1) + (idleTimeData ? 1 : 0);
49650
+ let gridColsClass = "lg:grid-cols-3";
49651
+ if (totalCards === 4) gridColsClass = "lg:grid-cols-4";
49652
+ else if (totalCards === 2) gridColsClass = "lg:grid-cols-2";
49653
+ const containerClassName = layout2 === "stack" ? `space-y-4 ${className || ""}` : `grid grid-cols-1 gap-4 sm:gap-3 sm:grid-cols-2 ${gridColsClass} w-full h-full min-h-0 ${className || ""}`;
49202
49654
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: containerClassName, children: [
49203
- !hideEfficiencyCard && /* @__PURE__ */ jsxRuntime.jsxs(Card2, { children: [
49204
- /* @__PURE__ */ jsxRuntime.jsx(CardHeader2, { className: "pb-2 flex-none", children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle2, { className: "text-lg text-center", children: "Efficiency" }) }),
49205
- /* @__PURE__ */ jsxRuntime.jsx(CardContent2, { className: "flex-1 flex items-center justify-center py-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
49206
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-5xl font-bold", style: { color: efficiencyColor }, children: [
49655
+ !hideEfficiencyCard && /* @__PURE__ */ jsxRuntime.jsxs(Card2, { className: "flex flex-col bg-white shadow-sm border border-gray-200 h-full min-h-[150px] sm:min-h-0 rounded-xl", children: [
49656
+ /* @__PURE__ */ jsxRuntime.jsx(CardHeader2, { className: "pb-1 pt-5 flex-none", children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle2, { className: "text-[15px] font-bold text-center text-gray-900 tracking-wide", children: "Efficiency" }) }),
49657
+ /* @__PURE__ */ jsxRuntime.jsxs(CardContent2, { className: "flex-1 flex flex-col items-center justify-center pb-6", children: [
49658
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-5xl font-bold tracking-tight", style: { color: efficiencyColor }, children: [
49207
49659
  efficiencyValue.toFixed(1),
49208
49660
  "%"
49209
49661
  ] }),
49210
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-gray-500 mt-2", children: [
49662
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-gray-500 mt-1 font-medium tracking-wide", children: [
49211
49663
  "Target: ",
49212
49664
  Math.round(efficiencyTarget),
49213
49665
  "%"
49214
49666
  ] })
49215
- ] }) })
49667
+ ] })
49216
49668
  ] }),
49217
- /* @__PURE__ */ jsxRuntime.jsxs(Card2, { children: [
49218
- /* @__PURE__ */ jsxRuntime.jsx(CardHeader2, { className: "pb-2 flex-none", children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle2, { className: "text-lg text-center", children: "Cycle Time (s)" }) }),
49219
- /* @__PURE__ */ jsxRuntime.jsx(CardContent2, { className: "flex-1 flex items-center justify-center py-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
49220
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: `text-5xl font-bold ${workspace.avg_cycle_time > (workspace.ideal_cycle_time || 0) ? "text-red-500" : "text-green-500"}`, children: workspace.avg_cycle_time.toFixed(1) }),
49221
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-gray-500 mt-2", children: [
49669
+ /* @__PURE__ */ jsxRuntime.jsxs(Card2, { className: "flex flex-col bg-white shadow-sm border border-gray-200 h-full min-h-[150px] sm:min-h-0 rounded-xl", children: [
49670
+ /* @__PURE__ */ jsxRuntime.jsx(CardHeader2, { className: "pb-1 pt-5 flex-none", children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle2, { className: "text-[15px] font-bold text-center text-gray-900 tracking-wide", children: "Cycle Time (s)" }) }),
49671
+ /* @__PURE__ */ jsxRuntime.jsxs(CardContent2, { className: "flex-1 flex flex-col items-center justify-center pb-6", children: [
49672
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: `text-5xl font-bold tracking-tight ${workspace.avg_cycle_time > (workspace.ideal_cycle_time || 0) ? "text-red-500" : "text-[#34C759]"}`, children: workspace.avg_cycle_time.toFixed(1) }),
49673
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-gray-500 mt-1 font-medium tracking-wide", children: [
49222
49674
  "Standard: ",
49223
49675
  workspace.ideal_cycle_time?.toFixed(1) || 0,
49224
49676
  "s"
49225
49677
  ] })
49226
- ] }) })
49678
+ ] })
49227
49679
  ] }),
49228
- /* @__PURE__ */ jsxRuntime.jsxs(Card2, { children: [
49229
- /* @__PURE__ */ jsxRuntime.jsx(CardHeader2, { className: "pb-2 flex-none", children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle2, { className: "text-lg text-center", children: "Idle Time" }) }),
49230
- /* @__PURE__ */ jsxRuntime.jsx(CardContent2, { className: "flex-1 flex items-center justify-center py-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
49231
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: `text-4xl font-bold ${!workspace.idle_time || workspace.idle_time <= 0 ? "text-green-500" : workspace.idle_time <= 300 ? "text-yellow-500" : "text-red-500"}`, children: formatIdleTime(workspace.idle_time) }),
49232
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500 mt-2", children: "Total idle time" })
49233
- ] }) })
49680
+ /* @__PURE__ */ jsxRuntime.jsxs(Card2, { className: "flex flex-col bg-white shadow-sm border border-gray-200 h-full min-h-[150px] sm:min-h-0 rounded-xl", children: [
49681
+ /* @__PURE__ */ jsxRuntime.jsx(CardHeader2, { className: "pb-1 pt-5 flex-none", children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle2, { className: "text-[15px] font-bold text-center text-gray-900 tracking-wide", children: "Idle Time" }) }),
49682
+ /* @__PURE__ */ jsxRuntime.jsxs(CardContent2, { className: "flex-1 flex flex-col items-center justify-center pb-6", children: [
49683
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: `text-5xl font-bold tracking-tight ${!workspace.idle_time || workspace.idle_time <= 0 ? "text-[#34C759]" : workspace.idle_time <= 300 ? "text-yellow-500" : "text-red-500"}`, children: formatIdleTime(workspace.idle_time) }),
49684
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-500 mt-1 font-medium tracking-wide", children: "Total idle time" })
49685
+ ] })
49686
+ ] }),
49687
+ idleTimeData && /* @__PURE__ */ jsxRuntime.jsxs(Card2, { className: "flex flex-col bg-white shadow-sm border border-gray-200 h-full min-h-[150px] sm:min-h-0 rounded-xl", children: [
49688
+ /* @__PURE__ */ jsxRuntime.jsx(CardHeader2, { className: "pb-1 pt-5 flex-none", children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle2, { className: "text-[15px] font-bold text-center text-gray-900 tracking-wide", children: "Idle Time Breakdown" }) }),
49689
+ /* @__PURE__ */ jsxRuntime.jsx(CardContent2, { className: "flex-1 p-2 flex items-center justify-center h-full sm:h-auto min-h-[120px] sm:min-h-[100px]", children: /* @__PURE__ */ jsxRuntime.jsx(
49690
+ IdleTimeReasonChart,
49691
+ {
49692
+ data: idleTimeData.chartData,
49693
+ isLoading: idleTimeData.isLoading,
49694
+ error: idleTimeData.error,
49695
+ variant: "bar"
49696
+ }
49697
+ ) })
49234
49698
  ] })
49235
49699
  ] });
49236
49700
  };
@@ -49368,13 +49832,13 @@ var getWorkspaceStyles = (position, isPlaceholder = false) => {
49368
49832
  ${isPlaceholder ? "cursor-default" : ""}`;
49369
49833
  };
49370
49834
  var formatPercentRange = (min, max) => {
49371
- const format8 = (value) => Number.isInteger(value) ? `${value}` : value.toFixed(1);
49835
+ const format9 = (value) => Number.isInteger(value) ? `${value}` : value.toFixed(1);
49372
49836
  if (min >= 100 || max >= 100) {
49373
- return `${format8(min)}+%`;
49837
+ return `${format9(min)}+%`;
49374
49838
  }
49375
- return `${format8(min)}-${format8(max)}%`;
49839
+ return `${format9(min)}-${format9(max)}%`;
49376
49840
  };
49377
- var Legend6 = ({
49841
+ var Legend5 = ({
49378
49842
  useBottleneckLabel = false,
49379
49843
  legend,
49380
49844
  metricLabel = "Efficiency"
@@ -49560,7 +50024,7 @@ var WorkspaceGrid = React141__namespace.default.memo(({
49560
50024
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `relative w-full h-full overflow-hidden ${className}`, children: [
49561
50025
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute top-0 left-2 sm:left-4 right-2 sm:right-8 z-20", children: [
49562
50026
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-row items-center justify-between py-1 sm:py-1.5 gap-2", children: [
49563
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden sm:block", children: /* @__PURE__ */ jsxRuntime.jsx(Legend6, { legend, useBottleneckLabel: hasFlowBuffers, metricLabel: legendMetricLabel }) }),
50027
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden sm:block", children: /* @__PURE__ */ jsxRuntime.jsx(Legend5, { legend, useBottleneckLabel: hasFlowBuffers, metricLabel: legendMetricLabel }) }),
49564
50028
  mapViewEnabled && /* @__PURE__ */ jsxRuntime.jsx(
49565
50029
  "button",
49566
50030
  {
@@ -49577,7 +50041,7 @@ var WorkspaceGrid = React141__namespace.default.memo(({
49577
50041
  }
49578
50042
  )
49579
50043
  ] }),
49580
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sm:hidden mt-1 mr-32", children: /* @__PURE__ */ jsxRuntime.jsx(Legend6, { legend, useBottleneckLabel: hasFlowBuffers, metricLabel: legendMetricLabel }) })
50044
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sm:hidden mt-1 mr-32", children: /* @__PURE__ */ jsxRuntime.jsx(Legend5, { legend, useBottleneckLabel: hasFlowBuffers, metricLabel: legendMetricLabel }) })
49581
50045
  ] }),
49582
50046
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-14 sm:top-16 left-0 right-0 bottom-0", children: /* @__PURE__ */ jsxRuntime.jsx(AnimatePresence, { mode: "wait", children: viewMode === "video" ? /* @__PURE__ */ jsxRuntime.jsx(
49583
50047
  motion.div,
@@ -50088,7 +50552,7 @@ var KPISection = React141.memo(({
50088
50552
  value: showSkeleton ? "" : kpis.efficiency.value,
50089
50553
  change: effChange,
50090
50554
  trend: effTrend,
50091
- trendLabel: "today",
50555
+ trendLabel: "vs yesterday",
50092
50556
  trendMode: "pill",
50093
50557
  suffix: "%",
50094
50558
  showZeroChange: true,
@@ -52324,7 +52788,7 @@ var SideNavBar = React141.memo(({
52324
52788
  setIsAlertsOpen(willOpen);
52325
52789
  setIsSettingsOpen(false);
52326
52790
  if (willOpen) {
52327
- trackCoreEvent("Alerts Page Clicked", { source: "side_nav" });
52791
+ trackCoreEvent("Alerts page clicked", { source: "side_nav" });
52328
52792
  void refreshAlertsSummary();
52329
52793
  }
52330
52794
  },
@@ -52479,7 +52943,7 @@ var SideNavBar = React141.memo(({
52479
52943
  {
52480
52944
  onClick: () => {
52481
52945
  setIsAlertsOpen(true);
52482
- trackCoreEvent("Alerts Page Clicked", { source: "side_nav_mobile" });
52946
+ trackCoreEvent("Alerts page clicked", { source: "side_nav_mobile" });
52483
52947
  void refreshAlertsSummary();
52484
52948
  onMobileMenuClose?.();
52485
52949
  },
@@ -59689,7 +60153,8 @@ var MonthlyRangeFilter = ({
59689
60153
  onMonthNavigate,
59690
60154
  className,
59691
60155
  variant = "default",
59692
- showLabel = true
60156
+ showLabel = true,
60157
+ singleDateOnly = false
59693
60158
  }) => {
59694
60159
  const todayKey = React141.useMemo(
59695
60160
  () => dateFnsTz.formatInTimeZone(/* @__PURE__ */ new Date(), timezone || "UTC", "yyyy-MM-dd"),
@@ -59777,6 +60242,12 @@ var MonthlyRangeFilter = ({
59777
60242
  }
59778
60243
  setActivePreset("Custom");
59779
60244
  setPendingChangeMeta({ source: "custom" });
60245
+ if (singleDateOnly) {
60246
+ setRangeStart(day);
60247
+ setRangeEnd(day);
60248
+ setSelecting(false);
60249
+ return;
60250
+ }
59780
60251
  if (!selecting || !rangeStart) {
59781
60252
  setRangeStart(day);
59782
60253
  setRangeEnd(null);
@@ -59811,6 +60282,13 @@ var MonthlyRangeFilter = ({
59811
60282
  };
59812
60283
  const handleApply = () => {
59813
60284
  if (rangeStart) {
60285
+ if (singleDateOnly) {
60286
+ const selected = dateFns.startOfDay(rangeStart) > dateFns.startOfDay(today) ? dateFns.startOfDay(today) : dateFns.startOfDay(rangeStart);
60287
+ const dateKey = dateFns.format(selected, "yyyy-MM-dd");
60288
+ onChange({ startKey: dateKey, endKey: dateKey }, pendingChangeMeta || { source: "custom" });
60289
+ setIsOpen(false);
60290
+ return;
60291
+ }
59814
60292
  const boundedStart = dateFns.startOfDay(rangeStart) > dateFns.startOfDay(today) ? dateFns.startOfDay(today) : dateFns.startOfDay(rangeStart);
59815
60293
  const candidateEnd = rangeEnd || rangeStart;
59816
60294
  const boundedEnd = dateFns.startOfDay(candidateEnd) > dateFns.startOfDay(today) ? dateFns.startOfDay(today) : dateFns.startOfDay(candidateEnd);
@@ -59905,7 +60383,7 @@ var MonthlyRangeFilter = ({
59905
60383
  "overflow-hidden bg-white animate-in fade-in zoom-in-95 duration-200 flex",
59906
60384
  isInline ? "relative mt-2 w-full rounded-xl border border-gray-200" : "absolute right-0 z-50 mt-2 w-[520px] rounded-xl border border-gray-100 shadow-2xl"
59907
60385
  ), children: [
59908
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-40 bg-[#F8FAFF] border-r border-gray-100 p-4 flex flex-col justify-between", children: [
60386
+ !singleDateOnly && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-40 bg-gray-50 border-r border-gray-100 p-4 flex flex-col justify-between", children: [
59909
60387
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
59910
60388
  presets.map((preset) => /* @__PURE__ */ jsxRuntime.jsx(
59911
60389
  "button",
@@ -59914,7 +60392,7 @@ var MonthlyRangeFilter = ({
59914
60392
  onClick: () => handlePresetClick(preset),
59915
60393
  className: clsx(
59916
60394
  "w-full text-left px-3 py-2 text-sm rounded-lg transition-all duration-200",
59917
- activePreset === preset.label ? "bg-[#EEF2FF] text-[#4F46E5] font-semibold" : "text-gray-600 hover:bg-gray-100/80"
60395
+ activePreset === preset.label ? "bg-blue-50 text-blue-600 font-semibold" : "text-gray-600 hover:bg-gray-100/80"
59918
60396
  ),
59919
60397
  children: preset.label
59920
60398
  },
@@ -59927,7 +60405,7 @@ var MonthlyRangeFilter = ({
59927
60405
  onClick: () => setActivePreset("Custom"),
59928
60406
  className: clsx(
59929
60407
  "w-full text-left px-3 py-2 text-sm rounded-lg transition-all duration-200",
59930
- activePreset === "Custom" ? "bg-[#EEF2FF] text-[#4F46E5] font-semibold" : "text-gray-600 hover:bg-gray-100/80"
60408
+ activePreset === "Custom" ? "bg-blue-50 text-blue-600 font-semibold" : "text-gray-600 hover:bg-gray-100/80"
59931
60409
  ),
59932
60410
  children: "Custom"
59933
60411
  }
@@ -59949,7 +60427,7 @@ var MonthlyRangeFilter = ({
59949
60427
  type: "button",
59950
60428
  onClick: handleApply,
59951
60429
  disabled: !rangeStart,
59952
- className: "w-full py-2.5 bg-[#4F46E5] text-white text-sm font-semibold rounded-lg hover:bg-[#4338CA] transition-all disabled:opacity-50 disabled:cursor-not-allowed",
60430
+ className: "w-full py-2.5 bg-blue-600 text-white text-sm font-semibold rounded-lg hover:bg-blue-700 transition-all disabled:opacity-50 disabled:cursor-not-allowed",
59953
60431
  children: "Apply"
59954
60432
  }
59955
60433
  )
@@ -59999,9 +60477,9 @@ var MonthlyRangeFilter = ({
59999
60477
  const dayNum = day.getDate();
60000
60478
  const isFutureDay = dateFns.startOfDay(day) > dateFns.startOfDay(today);
60001
60479
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
60002
- inRange && !isStart && !isEnd && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 -inset-x-0.5 bg-[#EEF2FF] z-0" }),
60003
- inRange && isStart && !isSingleDaySelection && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 right-0 left-1/2 bg-[#EEF2FF] z-0" }),
60004
- inRange && isEnd && !isSingleDaySelection && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 left-0 right-1/2 bg-[#EEF2FF] z-0" }),
60480
+ inRange && !isStart && !isEnd && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 -inset-x-0.5 bg-blue-50 z-0" }),
60481
+ inRange && isStart && !isSingleDaySelection && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 right-0 left-1/2 bg-blue-50 z-0" }),
60482
+ inRange && isEnd && !isSingleDaySelection && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 left-0 right-1/2 bg-blue-50 z-0" }),
60005
60483
  /* @__PURE__ */ jsxRuntime.jsx(
60006
60484
  "button",
60007
60485
  {
@@ -60010,19 +60488,47 @@ var MonthlyRangeFilter = ({
60010
60488
  disabled: isFutureDay,
60011
60489
  className: clsx(
60012
60490
  "h-10 w-full flex items-center justify-center text-sm font-semibold transition-all duration-150 relative z-10",
60013
- isFutureDay && "text-gray-200 cursor-not-allowed",
60491
+ // Future day NOT in range
60492
+ isFutureDay && !inRange && "text-gray-300 cursor-not-allowed",
60014
60493
  // Not in range
60015
- !isFutureDay && !inRange && "text-gray-700 hover:bg-gray-50 rounded-lg",
60494
+ !isFutureDay && !inRange && "text-gray-700 hover:bg-gray-100 rounded-lg",
60016
60495
  // Middle of range
60017
- inRange && !isStart && !isEnd && "text-[#4F46E5]",
60496
+ inRange && !isStart && !isEnd && clsx(
60497
+ "text-blue-600",
60498
+ isFutureDay && "cursor-not-allowed opacity-60"
60499
+ ),
60018
60500
  // Start/End of range or Single selection
60019
- (isStart || isEnd) && "bg-[#4F46E5] text-white rounded-lg shadow-sm"
60501
+ (isStart || isEnd) && clsx(
60502
+ "bg-blue-600 text-white rounded-lg shadow-sm",
60503
+ isFutureDay && "cursor-not-allowed opacity-80"
60504
+ )
60020
60505
  ),
60021
60506
  children: dayNum
60022
60507
  }
60023
60508
  )
60024
60509
  ] }, day.toISOString());
60025
- }) })
60510
+ }) }),
60511
+ singleDateOnly && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 flex items-center justify-end gap-2 border-t border-gray-100 pt-3", children: [
60512
+ /* @__PURE__ */ jsxRuntime.jsx(
60513
+ "button",
60514
+ {
60515
+ type: "button",
60516
+ onClick: handleReset,
60517
+ className: "px-3 py-1.5 text-sm font-medium text-gray-500 hover:text-gray-700 transition-colors",
60518
+ children: "Reset"
60519
+ }
60520
+ ),
60521
+ /* @__PURE__ */ jsxRuntime.jsx(
60522
+ "button",
60523
+ {
60524
+ type: "button",
60525
+ onClick: handleApply,
60526
+ disabled: !rangeStart,
60527
+ className: "px-4 py-1.5 bg-blue-600 text-white text-sm font-semibold rounded-lg hover:bg-blue-700 transition-all disabled:opacity-50 disabled:cursor-not-allowed",
60528
+ children: "Apply"
60529
+ }
60530
+ )
60531
+ ] })
60026
60532
  ] })
60027
60533
  ] })
60028
60534
  ] });
@@ -61799,7 +62305,36 @@ var getMonthDateInfo = (timezone) => {
61799
62305
  const monthEndDate = dateFnsTz.fromZonedTime(`${monthEndKey}T23:59:59`, timezone);
61800
62306
  return { startDate, endDate, monthEndDate };
61801
62307
  };
61802
- var LeaderboardCountdown = ({ targetDate, format: format8, finishedLabel = "Finished", placeholder = "--", onFinished }) => {
62308
+ var createKpisOverviewUrl = ({
62309
+ tab,
62310
+ date,
62311
+ shift
62312
+ }) => {
62313
+ const params = new URLSearchParams();
62314
+ if (tab) {
62315
+ params.set("tab", tab);
62316
+ }
62317
+ if (date) {
62318
+ params.set("date", date);
62319
+ }
62320
+ if (typeof shift === "number" && Number.isFinite(shift)) {
62321
+ params.set("shift", shift.toString());
62322
+ }
62323
+ const queryString = params.toString();
62324
+ return queryString ? `/kpis?${queryString}` : "/kpis";
62325
+ };
62326
+ var getZonedDateAtMidday = (dateKey, timezone) => dateFnsTz.fromZonedTime(`${dateKey}T12:00:00`, timezone);
62327
+ var formatDateKey = (dateKey, timezone, options) => {
62328
+ try {
62329
+ return new Intl.DateTimeFormat("en-US", {
62330
+ ...options,
62331
+ timeZone: timezone
62332
+ }).format(getZonedDateAtMidday(dateKey, timezone));
62333
+ } catch {
62334
+ return dateKey;
62335
+ }
62336
+ };
62337
+ var LeaderboardCountdown = ({ targetDate, format: format9, finishedLabel = "Finished", placeholder = "--", onFinished }) => {
61803
62338
  const [time2, setTime] = React141.useState("");
61804
62339
  const hasFinishedRef = React141.useRef(false);
61805
62340
  React141.useEffect(() => {
@@ -61821,7 +62356,7 @@ var LeaderboardCountdown = ({ targetDate, format: format8, finishedLabel = "Fini
61821
62356
  }
61822
62357
  return;
61823
62358
  }
61824
- if (format8 === "days") {
62359
+ if (format9 === "days") {
61825
62360
  const days = Math.floor(diff / (1e3 * 60 * 60 * 24));
61826
62361
  const hours = Math.floor(diff % (1e3 * 60 * 60 * 24) / (1e3 * 60 * 60));
61827
62362
  setTime(`${days} days ${hours} hours`);
@@ -61836,7 +62371,7 @@ var LeaderboardCountdown = ({ targetDate, format: format8, finishedLabel = "Fini
61836
62371
  tick();
61837
62372
  const interval = setInterval(tick, 1e3);
61838
62373
  return () => clearInterval(interval);
61839
- }, [targetDate, format8, finishedLabel, placeholder, onFinished]);
62374
+ }, [targetDate, format9, finishedLabel, placeholder, onFinished]);
61840
62375
  return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: time2 });
61841
62376
  };
61842
62377
  var LinesLeaderboard = ({
@@ -61854,7 +62389,9 @@ var LinesLeaderboard = ({
61854
62389
  shiftEndDate,
61855
62390
  monthEndDate,
61856
62391
  viewType,
61857
- setViewType
62392
+ setViewType,
62393
+ timezone: _timezone,
62394
+ isHistoricalDaily
61858
62395
  }) => {
61859
62396
  const formatEfficiency = (value) => typeof value === "number" && Number.isFinite(value) ? `${value.toFixed(1)}%` : "--";
61860
62397
  const assignedLineIdSet = React141__namespace.default.useMemo(
@@ -61947,10 +62484,10 @@ var LinesLeaderboard = ({
61947
62484
  }
61948
62485
  }, [timeRange, leaderboardData, isLoadingToday, isLoadingMonthly]);
61949
62486
  const topThree = leaderboardData.slice(0, 3);
61950
- leaderboardData.slice(3);
61951
62487
  const countdownTarget = timeRange === "monthly" ? monthEndDate : shiftEndDate;
61952
62488
  const countdownFormat = timeRange === "monthly" ? "days" : "clock";
61953
62489
  const countdownFinishedLabel = timeRange === "monthly" ? "Finished" : "Shift Ended";
62490
+ const showCountdown = timeRange === "monthly" || !isHistoricalDaily;
61954
62491
  const handleCountdownFinished = React141__namespace.default.useCallback(() => {
61955
62492
  trackCoreEvent("Leaderboard Countdown Finished", {
61956
62493
  countdown_type: timeRange === "monthly" ? "month_end" : "shift_end",
@@ -62024,7 +62561,7 @@ var LinesLeaderboard = ({
62024
62561
  }
62025
62562
  )
62026
62563
  ] }),
62027
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "md:absolute md:right-0 md:top-1/2 md:-translate-y-1/2 flex items-center gap-3", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2.5 px-4 py-2 bg-white rounded-full shadow-sm border border-gray-100", children: [
62564
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "md:absolute md:right-0 md:top-1/2 md:-translate-y-1/2 flex flex-wrap items-center justify-center md:justify-end gap-2", children: showCountdown && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2.5 px-4 py-2 bg-white rounded-full shadow-sm border border-gray-100", children: [
62028
62565
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "w-4 h-4 text-orange-500" }),
62029
62566
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline gap-2", children: [
62030
62567
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-semibold text-gray-400 uppercase tracking-wider", children: "Ends in" }),
@@ -62145,7 +62682,7 @@ var LinesLeaderboard = ({
62145
62682
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-gray-900", children: item.supervisorName })
62146
62683
  ] }) }),
62147
62684
  /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-3 whitespace-nowrap text-sm text-gray-500", children: item.line.line_name }),
62148
- /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-3 whitespace-nowrap text-right", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col items-end", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-bold text-gray-900", children: formatEfficiency(item.efficiency) }) }) })
62685
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-3 whitespace-nowrap text-right", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col items-end", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold text-gray-900", children: formatEfficiency(item.efficiency) }) }) })
62149
62686
  ]
62150
62687
  },
62151
62688
  item.id
@@ -62319,6 +62856,9 @@ var KPIsOverviewView = ({
62319
62856
  const [activeTab, setActiveTab] = React141.useState("today");
62320
62857
  const [timeRange, setTimeRange] = React141.useState("today");
62321
62858
  const [viewType, setViewType] = React141.useState("operator");
62859
+ const [selectedLeaderboardDate, setSelectedLeaderboardDate] = React141.useState("");
62860
+ const [selectedLeaderboardShiftId, setSelectedLeaderboardShiftId] = React141.useState(0);
62861
+ const [hasHydratedLeaderboardRouteState, setHasHydratedLeaderboardRouteState] = React141.useState(false);
62322
62862
  const [loading, setLoading] = React141.useState(true);
62323
62863
  const [error, setError] = React141.useState(null);
62324
62864
  const [topPerformer, setTopPerformer] = React141.useState({
@@ -62340,13 +62880,6 @@ var KPIsOverviewView = ({
62340
62880
  const [monthlyError, setMonthlyError] = React141.useState(null);
62341
62881
  const dailyRequestKeyRef = React141.useRef(null);
62342
62882
  const monthlyRequestKeyRef = React141.useRef(null);
62343
- React141.useEffect(() => {
62344
- if (!router$1.isReady) return;
62345
- const tab = router$1.query.tab;
62346
- if (tab === "leaderboard") {
62347
- setActiveTab("leaderboard");
62348
- }
62349
- }, [router$1.isReady, router$1.query.tab]);
62350
62883
  const supabase = useSupabase();
62351
62884
  const { user } = useAuth();
62352
62885
  const dashboardConfig = useDashboardConfig();
@@ -62432,7 +62965,77 @@ var KPIsOverviewView = ({
62432
62965
  () => getShiftEndDate(currentShiftDetails, configuredTimezone),
62433
62966
  [currentShiftDetails, configuredTimezone]
62434
62967
  );
62435
- const shiftName = (currentShiftDetails.shiftName || "Day").replace(/ Shift$/i, "");
62968
+ React141__namespace.default.useMemo(() => {
62969
+ if (shiftConfig?.shifts && shiftConfig.shifts.length > 0) {
62970
+ return shiftConfig.shifts.map((shift) => ({
62971
+ id: shift.shiftId,
62972
+ label: shift.shiftName || `Shift ${shift.shiftId}`,
62973
+ startTime: shift.startTime,
62974
+ endTime: shift.endTime
62975
+ }));
62976
+ }
62977
+ return [
62978
+ { id: 0, label: "Day Shift", startTime: "06:00", endTime: "18:00" },
62979
+ { id: 1, label: "Night Shift", startTime: "18:00", endTime: "06:00" }
62980
+ ];
62981
+ }, [shiftConfig]);
62982
+ const effectiveLeaderboardDate = selectedLeaderboardDate || currentShiftDate;
62983
+ const effectiveLeaderboardShiftId = Number.isFinite(selectedLeaderboardShiftId) ? selectedLeaderboardShiftId : currentShiftId;
62984
+ const isHistoricalLeaderboardDaily = activeTab === "leaderboard" && timeRange === "today" && (effectiveLeaderboardDate !== currentShiftDate || effectiveLeaderboardShiftId !== currentShiftId);
62985
+ React141.useEffect(() => {
62986
+ if (!router$1.isReady) return;
62987
+ const tabQuery = router$1.query.tab;
62988
+ const dateQuery = router$1.query.date;
62989
+ const shiftQuery = router$1.query.shift;
62990
+ const parsedShiftQuery = typeof shiftQuery === "string" ? Number.parseInt(shiftQuery, 10) : Number.NaN;
62991
+ const hasHistoricalQuery = tabQuery === "leaderboard" && typeof dateQuery === "string" && Number.isFinite(parsedShiftQuery);
62992
+ setActiveTab(tabQuery === "leaderboard" ? "leaderboard" : "today");
62993
+ if (hasHistoricalQuery) {
62994
+ setSelectedLeaderboardDate(dateQuery);
62995
+ setSelectedLeaderboardShiftId(parsedShiftQuery);
62996
+ setTimeRange("today");
62997
+ } else {
62998
+ setSelectedLeaderboardDate(currentShiftDate);
62999
+ setSelectedLeaderboardShiftId(currentShiftId);
63000
+ }
63001
+ setHasHydratedLeaderboardRouteState(true);
63002
+ }, [
63003
+ router$1.isReady,
63004
+ router$1.query.tab,
63005
+ router$1.query.date,
63006
+ router$1.query.shift,
63007
+ currentShiftDate,
63008
+ currentShiftId
63009
+ ]);
63010
+ React141.useEffect(() => {
63011
+ if (!router$1.isReady || !hasHydratedLeaderboardRouteState) return;
63012
+ const expectedTab = activeTab === "leaderboard" ? "leaderboard" : void 0;
63013
+ const expectedDate = activeTab === "leaderboard" && timeRange === "today" && isHistoricalLeaderboardDaily ? effectiveLeaderboardDate : void 0;
63014
+ const expectedShift = expectedDate !== void 0 ? effectiveLeaderboardShiftId.toString() : void 0;
63015
+ const currentTab = typeof router$1.query.tab === "string" ? router$1.query.tab : void 0;
63016
+ const currentDateQuery = typeof router$1.query.date === "string" ? router$1.query.date : void 0;
63017
+ const currentShiftQuery = typeof router$1.query.shift === "string" ? router$1.query.shift : void 0;
63018
+ if (currentTab === expectedTab && currentDateQuery === expectedDate && currentShiftQuery === expectedShift) {
63019
+ return;
63020
+ }
63021
+ void router$1.replace(
63022
+ createKpisOverviewUrl({
63023
+ tab: expectedTab === "leaderboard" ? "leaderboard" : void 0,
63024
+ date: expectedDate,
63025
+ shift: expectedShift !== void 0 ? Number.parseInt(expectedShift, 10) : void 0
63026
+ }),
63027
+ void 0,
63028
+ { shallow: true }
63029
+ );
63030
+ }, [
63031
+ router$1,
63032
+ activeTab,
63033
+ timeRange,
63034
+ effectiveLeaderboardDate,
63035
+ effectiveLeaderboardShiftId,
63036
+ hasHydratedLeaderboardRouteState,
63037
+ isHistoricalLeaderboardDaily
63038
+ ]);
62436
63039
  const factoryViewId = entityConfig.factoryViewId || "factory";
62437
63040
  const {
62438
63041
  lineMetrics,
@@ -62607,10 +63210,10 @@ var KPIsOverviewView = ({
62607
63210
  }, [supabase, resolvedCompanyId, leaderboardLinesForView, monthStartDate, monthEndDateKey, viewType]);
62608
63211
  const fetchDailyLeaderboard = React141.useCallback(async () => {
62609
63212
  if (!supabase || !resolvedCompanyId || leaderboardLinesForView.length === 0) return;
62610
- if (!currentShiftDate) return;
63213
+ if (!effectiveLeaderboardDate) return;
62611
63214
  const targetLineIds = leaderboardLinesForView.map((line) => line.id);
62612
63215
  const lineIdsKey = targetLineIds.slice().sort().join(",");
62613
- const requestKey = `${resolvedCompanyId}|${currentShiftDate}|${currentShiftId}|${lineIdsKey}`;
63216
+ const requestKey = `${resolvedCompanyId}|${effectiveLeaderboardDate}|${effectiveLeaderboardShiftId}|${lineIdsKey}`;
62614
63217
  if (dailyRequestKeyRef.current === requestKey) return;
62615
63218
  dailyRequestKeyRef.current = requestKey;
62616
63219
  setDailyLoading(true);
@@ -62618,8 +63221,8 @@ var KPIsOverviewView = ({
62618
63221
  try {
62619
63222
  const entries = await lineLeaderboardService.getDailyLineLeaderboard(supabase, {
62620
63223
  companyId: resolvedCompanyId,
62621
- date: currentShiftDate,
62622
- shiftId: currentShiftId,
63224
+ date: effectiveLeaderboardDate,
63225
+ shiftId: effectiveLeaderboardShiftId,
62623
63226
  lineIds: targetLineIds,
62624
63227
  lineMode: viewType === "machine" ? "uptime" : "output"
62625
63228
  });
@@ -62637,7 +63240,14 @@ var KPIsOverviewView = ({
62637
63240
  } finally {
62638
63241
  setDailyLoading(false);
62639
63242
  }
62640
- }, [supabase, resolvedCompanyId, leaderboardLinesForView, currentShiftDate, currentShiftId, viewType]);
63243
+ }, [
63244
+ supabase,
63245
+ resolvedCompanyId,
63246
+ leaderboardLinesForView,
63247
+ effectiveLeaderboardDate,
63248
+ effectiveLeaderboardShiftId,
63249
+ viewType
63250
+ ]);
62641
63251
  React141.useEffect(() => {
62642
63252
  if (activeTab !== "leaderboard") return;
62643
63253
  fetchMonthlyLeaderboard();
@@ -62726,6 +63336,12 @@ var KPIsOverviewView = ({
62726
63336
  trackProps.status = isEfficiencyOnTrack(kpis.efficiency?.value) ? "On Track" : "Behind";
62727
63337
  }
62728
63338
  trackCoreEvent("Line Card Clicked", trackProps);
63339
+ if (activeTab === "leaderboard" && timeRange === "today" && isHistoricalLeaderboardDaily) {
63340
+ navigation.navigate(
63341
+ `/kpis/${line.id}?date=${encodeURIComponent(effectiveLeaderboardDate)}&shift=${effectiveLeaderboardShiftId}`
63342
+ );
63343
+ return;
63344
+ }
62729
63345
  navigation.navigate(`/kpis/${line.id}`);
62730
63346
  };
62731
63347
  const handleBackClick = React141.useCallback(() => {
@@ -62751,21 +63367,24 @@ var KPIsOverviewView = ({
62751
63367
  });
62752
63368
  setActiveTab(newTab);
62753
63369
  }, [activeTab, leaderboardLines.length, lines.length]);
62754
- const formatLocalDate2 = (date) => {
62755
- const options = {
63370
+ const formatLocalDate2 = React141.useCallback((dateKey) => {
63371
+ return formatDateKey(dateKey, configuredTimezone, {
62756
63372
  year: "numeric",
62757
63373
  month: "long",
62758
63374
  day: "numeric"
62759
- };
62760
- return date.toLocaleDateString("en-US", options);
62761
- };
63375
+ });
63376
+ }, [configuredTimezone]);
62762
63377
  const getMonthRange = () => {
62763
- const now4 = /* @__PURE__ */ new Date();
62764
- const startOfMonth2 = new Date(now4.getFullYear(), now4.getMonth(), 1);
62765
- const endOfMonth2 = new Date(now4.getFullYear(), now4.getMonth() + 1, 0);
62766
- const startLabel = startOfMonth2.toLocaleDateString("en-US", { month: "short", day: "numeric" });
62767
- const endLabel = endOfMonth2.toLocaleDateString("en-US", { month: "short", day: "numeric" });
62768
- return `${startLabel} - ${endLabel}, ${now4.getFullYear()}`;
63378
+ const zonedNow = dateFnsTz.toZonedTime(/* @__PURE__ */ new Date(), configuredTimezone);
63379
+ const startOfMonthKey = buildDateKey(zonedNow.getFullYear(), zonedNow.getMonth(), 1);
63380
+ const endOfMonthKey = buildDateKey(
63381
+ zonedNow.getFullYear(),
63382
+ zonedNow.getMonth(),
63383
+ new Date(zonedNow.getFullYear(), zonedNow.getMonth() + 1, 0).getDate()
63384
+ );
63385
+ const startLabel = formatDateKey(startOfMonthKey, configuredTimezone, { month: "short", day: "numeric" });
63386
+ const endLabel = formatDateKey(endOfMonthKey, configuredTimezone, { month: "short", day: "numeric" });
63387
+ return `${startLabel} - ${endLabel}, ${zonedNow.getFullYear()}`;
62769
63388
  };
62770
63389
  const isMonthlyMode = activeTab === "leaderboard" && timeRange === "monthly";
62771
63390
  const isLeaderboardLoading = timeRange === "today" ? dailyLoading : monthlyLoading;
@@ -62774,8 +63393,13 @@ var KPIsOverviewView = ({
62774
63393
  const showTopPerformerImage = Boolean(topPerformer.imageUrl) && !topPerformerImageError;
62775
63394
  typeof topPerformer.efficiency === "number" && Number.isFinite(topPerformer.efficiency);
62776
63395
  topPerformerLoading ? "--" : typeof topPerformer.efficiency === "number" && Number.isFinite(topPerformer.efficiency) ? `${topPerformer.efficiency.toFixed(1)}%` : "--";
63396
+ const showHistoricalLeaderboardHeader = activeTab === "leaderboard" && timeRange === "today" && isHistoricalLeaderboardDaily;
63397
+ const headerDateKey = activeTab === "leaderboard" && timeRange === "today" ? effectiveLeaderboardDate : monthEndDateKey;
63398
+ const headerShiftId = showHistoricalLeaderboardHeader ? effectiveLeaderboardShiftId : currentShiftDetails.shiftId;
63399
+ const headerShiftName = getShiftNameById(headerShiftId, configuredTimezone, shiftConfig).replace(/ Shift$/i, "");
63400
+ const headerDateLabel = isMonthlyMode ? getMonthRange() : formatLocalDate2(headerDateKey);
62777
63401
  const getShiftIcon = (shiftId) => {
62778
- const shiftNameLower = shiftName.toLowerCase();
63402
+ const shiftNameLower = getShiftNameById(shiftId, configuredTimezone, shiftConfig).toLowerCase();
62779
63403
  if (shiftNameLower.includes("day") || shiftNameLower.includes("morning") || shiftId === 0) {
62780
63404
  return /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" }) });
62781
63405
  }
@@ -62878,18 +63502,26 @@ var KPIsOverviewView = ({
62878
63502
  ),
62879
63503
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
62880
63504
  /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-lg font-semibold text-gray-900", children: activeTab === "leaderboard" ? "Leaderboard" : "Overview" }),
62881
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-2 w-2 rounded-full bg-emerald-500 animate-pulse ring-2 ring-emerald-500/20" })
63505
+ /* @__PURE__ */ jsxRuntime.jsx(
63506
+ "div",
63507
+ {
63508
+ className: `h-2 w-2 rounded-full ring-2 ${showHistoricalLeaderboardHeader ? "bg-amber-500 ring-amber-500/20" : "bg-emerald-500 animate-pulse ring-emerald-500/20"}`
63509
+ }
63510
+ )
62882
63511
  ] }) }),
62883
63512
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-12" })
62884
63513
  ] }),
62885
63514
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 flex flex-wrap items-center justify-center gap-2", children: [
62886
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: `inline-flex items-center bg-gray-100 rounded-full ${isMonthlyMode ? "px-4 py-1.5 ring-1 ring-gray-200 shadow-sm" : "px-2.5 py-1"}`, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: `font-medium text-gray-700 ${isMonthlyMode ? "text-sm" : "text-xs"}`, children: isMonthlyMode ? getMonthRange() : formatLocalDate2(/* @__PURE__ */ new Date()) }) }),
63515
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: `inline-flex items-center bg-gray-100 rounded-full ${isMonthlyMode ? "px-4 py-1.5 ring-1 ring-gray-200 shadow-sm" : "px-2.5 py-1"}`, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: `font-medium text-gray-700 ${isMonthlyMode ? "text-sm" : "text-xs"}`, children: headerDateLabel }) }),
62887
63516
  !isMonthlyMode && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
62888
63517
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center gap-1 px-2.5 py-1 bg-gray-100 rounded-full", children: [
62889
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-700 scale-90", children: getShiftIcon(currentShiftDetails.shiftId) }),
62890
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-gray-700", children: shiftName })
63518
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-700 scale-90", children: getShiftIcon(headerShiftId) }),
63519
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-gray-700", children: headerShiftName })
62891
63520
  ] }),
62892
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "inline-flex items-center px-2.5 py-1 bg-green-100 rounded-full", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-green-700", children: /* @__PURE__ */ jsxRuntime.jsx(ISTTimer_default, {}) }) })
63521
+ showHistoricalLeaderboardHeader ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center gap-1 px-2.5 py-1 bg-amber-50 text-amber-700 rounded-full", children: [
63522
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "w-3 h-3" }),
63523
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium", children: "Historical" })
63524
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "inline-flex items-center px-2.5 py-1 bg-green-100 rounded-full", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-green-700", children: /* @__PURE__ */ jsxRuntime.jsx(ISTTimer_default, {}) }) })
62893
63525
  ] })
62894
63526
  ] }),
62895
63527
  activeTab !== "leaderboard" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-4 bg-white shadow-md hover:shadow-lg transition-all duration-300 ease-out hover:scale-[1.01] relative rounded-2xl border border-amber-100 pl-2 pr-4 py-1.5 flex items-center justify-between group", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-4 min-w-0", children: [
@@ -62990,7 +63622,12 @@ var KPIsOverviewView = ({
62990
63622
  ) }),
62991
63623
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
62992
63624
  /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl md:text-3xl lg:text-4xl font-semibold text-gray-900 tracking-tight", children: activeTab === "leaderboard" ? "Leaderboard" : "Overview" }),
62993
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-2.5 w-2.5 rounded-full bg-emerald-500 animate-pulse ring-4 ring-emerald-500/10 flex-shrink-0" })
63625
+ /* @__PURE__ */ jsxRuntime.jsx(
63626
+ "div",
63627
+ {
63628
+ className: `h-2.5 w-2.5 rounded-full ring-4 flex-shrink-0 ${showHistoricalLeaderboardHeader ? "bg-amber-500 ring-amber-500/10" : "bg-emerald-500 animate-pulse ring-emerald-500/10"}`
63629
+ }
63630
+ )
62994
63631
  ] }),
62995
63632
  !topPerformerLoading && activeTab !== "leaderboard" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-0 top-1/2 -translate-y-1/2 z-10", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-2xl border border-amber-200 shadow-md pl-1.5 pr-4 py-1.5 flex items-center gap-4 transition-all hover:shadow-lg hover:border-amber-300 group", children: [
62996
63633
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
@@ -63060,7 +63697,7 @@ var KPIsOverviewView = ({
63060
63697
  ] }) })
63061
63698
  ] }),
63062
63699
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-blue-50/50 px-4 py-2 rounded-xl border border-blue-100/50 backdrop-blur-sm", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center gap-6", children: [
63063
- !isMonthlyMode && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
63700
+ !isMonthlyMode && !showHistoricalLeaderboardHeader && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
63064
63701
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-blue-700", children: [
63065
63702
  /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-4 h-4 opacity-70", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" }) }),
63066
63703
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-base font-semibold tabular-nums", children: /* @__PURE__ */ jsxRuntime.jsx(ISTTimer_default, {}) })
@@ -63069,16 +63706,23 @@ var KPIsOverviewView = ({
63069
63706
  ] }),
63070
63707
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-blue-600", children: [
63071
63708
  /* @__PURE__ */ jsxRuntime.jsx("svg", { className: `w-4 h-4 opacity-70 ${isMonthlyMode ? "scale-110" : ""}`, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" }) }),
63072
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${isMonthlyMode ? "text-base font-medium" : "text-sm font-medium"}`, children: isMonthlyMode ? getMonthRange() : formatLocalDate2(/* @__PURE__ */ new Date()) })
63709
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${isMonthlyMode ? "text-base font-medium" : "text-sm font-medium"}`, children: headerDateLabel })
63073
63710
  ] }),
63074
63711
  !isMonthlyMode && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
63075
63712
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-200" }),
63076
63713
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-blue-600", children: [
63077
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "opacity-70", children: getShiftIcon(currentShiftDetails.shiftId) }),
63714
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "opacity-70", children: getShiftIcon(headerShiftId) }),
63078
63715
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm font-semibold uppercase tracking-wider", children: [
63079
- shiftName,
63716
+ headerShiftName,
63080
63717
  " Shift"
63081
63718
  ] })
63719
+ ] }),
63720
+ showHistoricalLeaderboardHeader && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
63721
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-200" }),
63722
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 px-2.5 py-1 bg-amber-50 text-amber-700 rounded-full border border-amber-200", children: [
63723
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "w-3.5 h-3.5" }),
63724
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-semibold uppercase tracking-wider", children: "Historical" })
63725
+ ] })
63082
63726
  ] })
63083
63727
  ] })
63084
63728
  ] }) }),
@@ -63101,19 +63745,52 @@ var KPIsOverviewView = ({
63101
63745
  }
63102
63746
  )
63103
63747
  ] }),
63104
- (activeTab === "leaderboard" || activeTab === "today") && showViewTypeDropdown && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative", children: /* @__PURE__ */ jsxRuntime.jsxs(
63105
- "select",
63106
- {
63107
- value: viewType,
63108
- onChange: (e) => setViewType(e.target.value),
63109
- className: "appearance-none pl-3 pr-8 py-1.5 text-sm font-medium bg-white border border-gray-200 hover:border-gray-300 rounded-lg text-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500 transition-all cursor-pointer shadow-sm",
63110
- style: { 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")`, backgroundPosition: `right 0.5rem center`, backgroundRepeat: `no-repeat`, backgroundSize: `1.2em 1.2em` },
63111
- children: [
63112
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "operator", children: "Workforce" }),
63113
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "machine", children: "Machine" })
63114
- ]
63115
- }
63116
- ) })
63748
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
63749
+ activeTab === "leaderboard" && timeRange === "today" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
63750
+ /* @__PURE__ */ jsxRuntime.jsx(
63751
+ MonthlyRangeFilter_default,
63752
+ {
63753
+ month: parseDateKeyToDate(effectiveLeaderboardDate).getMonth(),
63754
+ year: parseDateKeyToDate(effectiveLeaderboardDate).getFullYear(),
63755
+ timezone: configuredTimezone,
63756
+ value: {
63757
+ startKey: effectiveLeaderboardDate,
63758
+ endKey: effectiveLeaderboardDate
63759
+ },
63760
+ onChange: (range) => {
63761
+ setSelectedLeaderboardDate(range.startKey);
63762
+ },
63763
+ showLabel: false,
63764
+ singleDateOnly: true
63765
+ }
63766
+ ),
63767
+ isHistoricalLeaderboardDaily && /* @__PURE__ */ jsxRuntime.jsx(
63768
+ "button",
63769
+ {
63770
+ type: "button",
63771
+ onClick: () => {
63772
+ setSelectedLeaderboardDate(currentShiftDate);
63773
+ setSelectedLeaderboardShiftId(currentShiftId);
63774
+ },
63775
+ className: "text-xs font-medium text-blue-600 hover:text-blue-700 whitespace-nowrap",
63776
+ children: "Return to Live"
63777
+ }
63778
+ )
63779
+ ] }),
63780
+ (activeTab === "leaderboard" || activeTab === "today") && showViewTypeDropdown && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative", children: /* @__PURE__ */ jsxRuntime.jsxs(
63781
+ "select",
63782
+ {
63783
+ value: viewType,
63784
+ onChange: (e) => setViewType(e.target.value),
63785
+ className: "appearance-none pl-3 pr-8 py-1.5 text-sm font-medium bg-white border border-gray-200 hover:border-gray-300 rounded-lg text-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500 transition-all cursor-pointer shadow-sm",
63786
+ style: { 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")`, backgroundPosition: `right 0.5rem center`, backgroundRepeat: `no-repeat`, backgroundSize: `1.2em 1.2em` },
63787
+ children: [
63788
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "operator", children: "Workforce" }),
63789
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "machine", children: "Machine" })
63790
+ ]
63791
+ }
63792
+ ) })
63793
+ ] })
63117
63794
  ] })
63118
63795
  ] })
63119
63796
  ] }) }),
@@ -63158,7 +63835,9 @@ var KPIsOverviewView = ({
63158
63835
  shiftEndDate,
63159
63836
  monthEndDate,
63160
63837
  viewType,
63161
- setViewType
63838
+ setViewType,
63839
+ timezone: configuredTimezone,
63840
+ isHistoricalDaily: isHistoricalLeaderboardDaily
63162
63841
  }
63163
63842
  ) })
63164
63843
  ) })
@@ -64700,51 +65379,41 @@ var ClipsCostView = () => {
64700
65379
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-[120px]" })
64701
65380
  ] }) })
64702
65381
  ] }) }),
64703
- /* @__PURE__ */ jsxRuntime.jsx("main", { className: "flex-1 p-4 sm:p-6 lg:p-8 max-w-7xl mx-auto w-full", children: error ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4 bg-red-50 border border-red-200 rounded-lg flex items-start gap-3 text-red-700 animate-in fade-in slide-in-from-top-2 max-w-2xl mx-auto", children: [
65382
+ /* @__PURE__ */ jsxRuntime.jsx("main", { className: "flex-1 p-4 sm:p-6 lg:p-8 max-w-5xl mx-auto w-full", children: error ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4 bg-red-50 border border-red-200 rounded-lg flex items-start gap-3 text-red-700 animate-in fade-in slide-in-from-top-2 max-w-2xl mx-auto", children: [
64704
65383
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { className: "h-5 w-5 flex-shrink-0 mt-0.5" }),
64705
65384
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
64706
65385
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "font-medium", children: "Error loading usage data" }),
64707
65386
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm mt-1 text-red-600", children: error })
64708
65387
  ] })
64709
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-xl mt-8 animate-in fade-in slide-in-from-bottom-4 duration-500", children: [
64710
- /* @__PURE__ */ jsxRuntime.jsx(Card2, { className: "overflow-hidden shadow-sm border-gray-200 bg-white", children: /* @__PURE__ */ jsxRuntime.jsx(CardContent2, { className: "p-8", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center text-center", children: [
64711
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-3 bg-blue-50 rounded-full mb-4", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Film, { className: "h-8 w-8 text-blue-600" }) }),
64712
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-sm font-medium text-gray-500 uppercase tracking-wider mb-2", children: "Clips Analyzed" }),
64713
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-500 mb-3", children: formatMonthLabel(data?.monthStart) }),
64714
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-5xl font-bold text-gray-900 mb-2 tabular-nums tracking-tight", children: formatNumber(data?.monthlyClassifications || 0) })
64715
- ] }) }) }),
64716
- /* @__PURE__ */ jsxRuntime.jsx(Card2, { className: "mt-4 overflow-hidden shadow-sm border-gray-200 bg-white", children: /* @__PURE__ */ jsxRuntime.jsxs(CardContent2, { className: "p-6", children: [
64717
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-semibold text-gray-900 mb-3 uppercase tracking-wider", children: "Previous Months" }),
64718
- (data?.historicalMonthlyClassifications?.length || 0) === 0 ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500", children: "No historical month usage yet." }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: data?.historicalMonthlyClassifications.map((item) => /* @__PURE__ */ jsxRuntime.jsxs(
65388
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-8 grid grid-cols-1 lg:grid-cols-3 gap-6 animate-in fade-in slide-in-from-bottom-4 duration-500", children: [
65389
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lg:col-span-2", children: /* @__PURE__ */ jsxRuntime.jsx(Card2, { className: "overflow-hidden shadow-sm border-gray-200 bg-white h-full flex flex-col justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(CardContent2, { className: "p-10 lg:p-14", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center text-center", children: [
65390
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 bg-blue-50/50 rounded-2xl mb-6 ring-1 ring-blue-100/50", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Film, { className: "h-10 w-10 text-blue-600", strokeWidth: 1.5 }) }),
65391
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-base font-semibold text-gray-600 uppercase tracking-widest mb-3", children: "Current Month Usage" }),
65392
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-gray-400 mb-6", children: formatMonthLabel(data?.monthStart) }),
65393
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-baseline gap-2 mb-2", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-7xl font-bold text-gray-900 tabular-nums tracking-tighter", children: formatNumber(data?.monthlyClassifications || 0) }) }),
65394
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500 mt-4", children: "Clips analyzed and processed by our AI pipeline" })
65395
+ ] }) }) }) }),
65396
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lg:col-span-1", children: /* @__PURE__ */ jsxRuntime.jsx(Card2, { className: "overflow-hidden shadow-sm border-gray-200 bg-white h-full", children: /* @__PURE__ */ jsxRuntime.jsxs(CardContent2, { className: "p-6 lg:p-8 flex flex-col h-full", children: [
65397
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-semibold text-gray-900 mb-6 uppercase tracking-widest", children: "Previous Months" }),
65398
+ (data?.historicalMonthlyClassifications?.length || 0) === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 flex flex-col items-center justify-center text-center py-8", children: [
65399
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-3 bg-gray-50 rounded-full mb-3", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Film, { className: "h-6 w-6 text-gray-400", strokeWidth: 1.5 }) }),
65400
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500", children: "No historical month usage yet." })
65401
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-3 flex-1 overflow-y-auto pr-2", children: data?.historicalMonthlyClassifications.map((item) => /* @__PURE__ */ jsxRuntime.jsxs(
64719
65402
  "div",
64720
65403
  {
64721
- className: "flex items-center justify-between rounded-md border border-gray-100 bg-gray-50 px-3 py-2",
65404
+ className: "group flex items-center justify-between rounded-xl border border-gray-100 bg-gray-50/50 hover:bg-white hover:border-gray-200 hover:shadow-sm px-4 py-3.5 transition-all duration-200",
64722
65405
  children: [
64723
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-gray-700", children: formatMonthLabel(item.monthStart) }),
64724
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold text-gray-900 tabular-nums", children: formatNumber(item.classifications) })
65406
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-gray-600 group-hover:text-gray-900 transition-colors", children: formatMonthLabel(item.monthStart) }),
65407
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-base font-semibold text-gray-900 tabular-nums", children: formatNumber(item.classifications) })
64725
65408
  ]
64726
65409
  },
64727
65410
  item.monthStart
64728
65411
  )) })
64729
- ] }) })
65412
+ ] }) }) })
64730
65413
  ] }) })
64731
65414
  ] });
64732
65415
  };
64733
65416
  var ClipsCostView_default = ClipsCostView;
64734
-
64735
- // src/lib/constants/actions.ts
64736
- var ACTION_NAMES = {
64737
- /** Assembly operations */
64738
- ASSEMBLY: "Assembly",
64739
- /** Packaging operations */
64740
- PACKAGING: "Packaging",
64741
- /** Inspection operations */
64742
- INSPECTION: "Inspection",
64743
- /** Testing operations */
64744
- TESTING: "Testing",
64745
- /** Quality control operations */
64746
- QUALITY_CONTROL: "Quality Control"
64747
- };
64748
65417
  var calculateShiftHours = (startTime, endTime, breaks = []) => {
64749
65418
  if (!startTime || !endTime) return 8;
64750
65419
  const [startHour, startMinute] = startTime.split(":").map(Number);
@@ -65326,9 +65995,9 @@ var ShiftsView = ({
65326
65995
  const actionIds = Array.from(
65327
65996
  new Set(currentThresholds.map((threshold) => threshold.action_id).filter(Boolean))
65328
65997
  );
65329
- const actionNameById = /* @__PURE__ */ new Map();
65998
+ const actionMetadataById = /* @__PURE__ */ new Map();
65330
65999
  if (actionIds.length > 0) {
65331
- const { data: actionRows, error: actionsError } = await supabase.from("actions").select("id, action_name").in("id", actionIds);
66000
+ const { data: actionRows, error: actionsError } = await supabase.from("actions").select("id, action_name, action_family").in("id", actionIds);
65332
66001
  if (actionsError) {
65333
66002
  console.warn(
65334
66003
  `[ShiftsView] Failed to resolve action names for line ${lineId}, shift ${shift.shiftId}: ${actionsError.message}`
@@ -65336,7 +66005,13 @@ var ShiftsView = ({
65336
66005
  } else {
65337
66006
  (actionRows || []).forEach((actionRow) => {
65338
66007
  if (actionRow.id && actionRow.action_name) {
65339
- actionNameById.set(actionRow.id, actionRow.action_name);
66008
+ actionMetadataById.set(actionRow.id, {
66009
+ action_name: actionRow.action_name,
66010
+ action_family: normalizeActionFamily({
66011
+ actionFamily: actionRow.action_family,
66012
+ actionName: actionRow.action_name
66013
+ })
66014
+ });
65340
66015
  }
65341
66016
  });
65342
66017
  }
@@ -65357,7 +66032,7 @@ var ShiftsView = ({
65357
66032
  nextDayOutput = dayOutputToKeep;
65358
66033
  nextPPH = newShiftHours > 0 ? Math.round(dayOutputToKeep / newShiftHours) : 0;
65359
66034
  }
65360
- const resolvedActionName = (typeof threshold.action_name === "string" && threshold.action_name.trim().length > 0 ? threshold.action_name : actionNameById.get(threshold.action_id)) || ACTION_NAMES.ASSEMBLY;
66035
+ const resolvedActionName = (typeof threshold.action_name === "string" && threshold.action_name.trim().length > 0 ? threshold.action_name : actionMetadataById.get(threshold.action_id)?.action_name) || ACTION_NAMES.ASSEMBLY;
65361
66036
  return {
65362
66037
  line_id: threshold.line_id || lineId,
65363
66038
  shift_id: shift.shiftId,
@@ -65378,18 +66053,20 @@ var ShiftsView = ({
65378
66053
  `Failed to update action thresholds for line ${lineId}, shift ${shift.shiftId}: ${thresholdsUpsertError.message}`
65379
66054
  );
65380
66055
  }
65381
- const packagingActionIds = new Set(
65382
- Array.from(actionNameById.entries()).filter(([, actionName]) => actionName.toLowerCase() === ACTION_NAMES.PACKAGING.toLowerCase()).map(([actionId]) => actionId)
66056
+ const outputActionIds = new Set(
66057
+ Array.from(actionMetadataById.entries()).filter(([, action]) => action.action_family === ACTION_FAMILIES.OUTPUT).map(([actionId]) => actionId)
65383
66058
  );
65384
- const packagingThresholds = recalculatedThresholds.filter((threshold) => {
65385
- if (packagingActionIds.has(threshold.action_id)) return true;
65386
- return typeof threshold.action_name === "string" && threshold.action_name.toLowerCase() === ACTION_NAMES.PACKAGING.toLowerCase();
66059
+ const outputThresholds = recalculatedThresholds.filter((threshold) => {
66060
+ if (outputActionIds.has(threshold.action_id)) return true;
66061
+ return normalizeActionFamily({
66062
+ actionName: threshold.action_name
66063
+ }) === ACTION_FAMILIES.OUTPUT;
65387
66064
  });
65388
- const thresholdDayOutput = packagingThresholds.reduce(
66065
+ const thresholdDayOutput = outputThresholds.reduce(
65389
66066
  (sum, threshold) => sum + (Number(threshold.total_day_output) || 0),
65390
66067
  0
65391
66068
  );
65392
- const thresholdPPH = packagingThresholds.reduce(
66069
+ const thresholdPPH = outputThresholds.reduce(
65393
66070
  (sum, threshold) => sum + (Number(threshold.pph_threshold) || 0),
65394
66071
  0
65395
66072
  );
@@ -66600,7 +67277,7 @@ var TargetsViewUI = ({
66600
67277
  "aria-label": `Action type for ${formattedName}`,
66601
67278
  children: [
66602
67279
  /* @__PURE__ */ jsxRuntime.jsx("option", { value: "assembly", className: "py-2", children: "Assembly" }),
66603
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "packaging", className: "py-2", children: "Packaging" })
67280
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "output", className: "py-2", children: ACTION_NAMES.OUTPUT })
66604
67281
  ]
66605
67282
  }
66606
67283
  ) }),
@@ -66671,7 +67348,7 @@ var TargetsViewUI = ({
66671
67348
  "aria-label": `Action type for ${formattedName}`,
66672
67349
  children: [
66673
67350
  /* @__PURE__ */ jsxRuntime.jsx("option", { value: "assembly", children: "Assembly" }),
66674
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "packaging", children: "Packaging" })
67351
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "output", children: ACTION_NAMES.OUTPUT })
66675
67352
  ]
66676
67353
  }
66677
67354
  )
@@ -66876,14 +67553,20 @@ var TargetsView = ({
66876
67553
  throw new Error("Failed to fetch bulk targets data");
66877
67554
  }
66878
67555
  const { data } = bulkResponse;
66879
- const assemblyAction = Object.values(data.actions).find((a) => a.action_name === ACTION_NAMES.ASSEMBLY);
66880
- const packagingAction = Object.values(data.actions).find((a) => a.action_name === ACTION_NAMES.PACKAGING);
66881
- if (!assemblyAction || !packagingAction) {
67556
+ const assemblyAction = data.actions[ACTION_FAMILIES.ASSEMBLY] || Object.values(data.actions).find((a) => normalizeActionFamily({
67557
+ actionFamily: a.action_family,
67558
+ actionName: a.action_name
67559
+ }) === ACTION_FAMILIES.ASSEMBLY);
67560
+ const outputAction = data.actions[ACTION_FAMILIES.OUTPUT] || Object.values(data.actions).find((a) => normalizeActionFamily({
67561
+ actionFamily: a.action_family,
67562
+ actionName: a.action_name
67563
+ }) === ACTION_FAMILIES.OUTPUT);
67564
+ if (!assemblyAction || !outputAction) {
66882
67565
  throw new Error("Could not find required actions in bulk response");
66883
67566
  }
66884
67567
  const actionIdsData = {
66885
67568
  assembly: assemblyAction.id,
66886
- packaging: packagingAction.id
67569
+ output: outputAction.id
66887
67570
  };
66888
67571
  setActionIds(actionIdsData);
66889
67572
  const newAllShiftsData = {};
@@ -66928,12 +67611,16 @@ var TargetsView = ({
66928
67611
  let actionType = "assembly";
66929
67612
  let actionId = actionIdsData.assembly;
66930
67613
  const effectiveActionId = threshold?.action_id ?? ws.action_id;
66931
- if (effectiveActionId === packagingAction.id || !effectiveActionId && ws.action_type === "packaging") {
66932
- actionType = "packaging";
66933
- actionId = packagingAction.id;
66934
- } else if (effectiveActionId === assemblyAction.id || !effectiveActionId && ws.action_type === "assembly") {
67614
+ const effectiveActionFamily = normalizeActionFamily({
67615
+ actionFamily: threshold?.action_family,
67616
+ actionType: ws.action_type
67617
+ });
67618
+ if (effectiveActionFamily === ACTION_FAMILIES.OUTPUT || effectiveActionId === outputAction.id || !effectiveActionId && ws.action_type === "output") {
67619
+ actionType = "output";
67620
+ actionId = effectiveActionId || outputAction.id;
67621
+ } else if (effectiveActionFamily === ACTION_FAMILIES.ASSEMBLY || effectiveActionId === assemblyAction.id || !effectiveActionId && ws.action_type === "assembly") {
66935
67622
  actionType = "assembly";
66936
- actionId = assemblyAction.id;
67623
+ actionId = effectiveActionId || assemblyAction.id;
66937
67624
  }
66938
67625
  return {
66939
67626
  id: ws.id,
@@ -67205,7 +67892,9 @@ var TargetsView = ({
67205
67892
  // Round to whole number
67206
67893
  ideal_cycle_time: Number(ws.targetCycleTime) || 0,
67207
67894
  total_day_output: Number(ws.targetDayOutput) || 0,
67208
- action_name: ws.actionType === "assembly" ? ACTION_NAMES.ASSEMBLY : ACTION_NAMES.PACKAGING,
67895
+ action_name: ws.actionType === "assembly" ? ACTION_NAMES.ASSEMBLY : ACTION_NAMES.OUTPUT,
67896
+ action_family: ws.actionType,
67897
+ display_name: ws.actionType === "assembly" ? ACTION_NAMES.ASSEMBLY : ACTION_NAMES.OUTPUT,
67209
67898
  updated_by: currentEffectiveUserId,
67210
67899
  // Use the potentially hardcoded ID
67211
67900
  ...skuEnabled && lineDataToSave.selectedSKU ? { sku_id: lineDataToSave.selectedSKU.id } : {}
@@ -67213,7 +67902,7 @@ var TargetsView = ({
67213
67902
  console.log(`[handleSaveLine] workspaceThresholdUpdates for ${lineId}:`, workspaceThresholdUpdates);
67214
67903
  await workspaceService.updateActionThresholds(workspaceThresholdUpdates);
67215
67904
  console.log(`[handleSaveLine] Successfully updated action thresholds for ${lineId}`);
67216
- const packagingWorkspaces = lineDataToSave.workspaces.filter((ws) => ws.actionType === "packaging");
67905
+ const outputWorkspaces = lineDataToSave.workspaces.filter((ws) => ws.actionType === "output");
67217
67906
  let resolvedLineThresholdSkuId = lineDataToSave.selectedSKU?.id || null;
67218
67907
  if (!resolvedLineThresholdSkuId) {
67219
67908
  const { data: dummySkuRows, error: dummySkuError } = await supabase.from("skus").select("id").eq("line_id", lineId).eq("sku_definition", "dummy_definition").eq("is_active", true).limit(1);
@@ -67231,8 +67920,8 @@ var TargetsView = ({
67231
67920
  date: currentDate,
67232
67921
  shift_id: selectedShift,
67233
67922
  product_code: lineDataToSave.productId,
67234
- threshold_day_output: packagingWorkspaces.reduce((acc, ws) => acc + (Number(ws.targetDayOutput) || 0), 0),
67235
- threshold_pph: packagingWorkspaces.reduce((acc, ws) => acc + (ws.targetPPH ? Math.round(Number(ws.targetPPH)) : 0), 0),
67923
+ threshold_day_output: outputWorkspaces.reduce((acc, ws) => acc + (Number(ws.targetDayOutput) || 0), 0),
67924
+ threshold_pph: outputWorkspaces.reduce((acc, ws) => acc + (ws.targetPPH ? Math.round(Number(ws.targetPPH)) : 0), 0),
67236
67925
  // Round each PPH value
67237
67926
  sku_id: resolvedLineThresholdSkuId
67238
67927
  };
@@ -67760,6 +68449,7 @@ var WorkspaceDetailView = ({
67760
68449
  avg_efficiency: Number(cachedOverviewMetrics.efficiency || 0),
67761
68450
  total_actions: totalActions,
67762
68451
  hourly_action_counts: [],
68452
+ hourly_cycle_times: [],
67763
68453
  workspace_rank: 0,
67764
68454
  total_workspaces: 0,
67765
68455
  ideal_output_until_now: idealOutput,
@@ -67770,6 +68460,22 @@ var WorkspaceDetailView = ({
67770
68460
  }, [cachedOverviewMetrics, shiftConfig?.shifts]);
67771
68461
  const workspace = (isHistoricView ? historicMetrics : liveMetrics) || cachedDetailedMetrics || overviewFallback;
67772
68462
  const detailedWorkspaceMetrics = (isHistoricView ? historicMetrics : liveMetrics) || cachedDetailedMetrics;
68463
+ const cycleTimeChartData = React141.useMemo(
68464
+ () => Array.isArray(workspace?.hourly_cycle_times) ? workspace.hourly_cycle_times.map((value) => {
68465
+ const numericValue = Number(value);
68466
+ return Number.isFinite(numericValue) ? numericValue : 0;
68467
+ }) : [],
68468
+ [workspace?.hourly_cycle_times]
68469
+ );
68470
+ const cycleTimeDatasetKey = React141.useMemo(
68471
+ () => [
68472
+ workspace?.workspace_id || workspaceId || "workspace",
68473
+ date || workspace?.date || "live",
68474
+ parsedShiftId ?? workspace?.shift_id ?? "current",
68475
+ "hourly"
68476
+ ].join(":"),
68477
+ [workspace?.workspace_id, workspaceId, date, workspace?.date, parsedShiftId, workspace?.shift_id]
68478
+ );
67773
68479
  const hasWorkspaceSnapshot = Boolean(workspace);
67774
68480
  const loading = ((isHistoricView ? historicLoading : liveLoading) || isShiftConfigLoading) && !hasWorkspaceSnapshot;
67775
68481
  const error = isHistoricView ? historicError : liveError;
@@ -68011,10 +68717,47 @@ var WorkspaceDetailView = ({
68011
68717
  return filterDataByDateKeyRange(monthlyData, range);
68012
68718
  }, [monthlyData, range]);
68013
68719
  const formattedWorkspaceName = displayName || formatWorkspaceName3(workspace?.workspace_name || "", effectiveLineId);
68014
- const shouldShowCycleTimeChart = !isUptimeMode && (showCycleTimeChart ?? formattedWorkspaceName.startsWith("FINAL ASSY"));
68720
+ const workspaceActionType = workspace && "action_type" in workspace ? workspace.action_type : void 0;
68721
+ const workspaceAssemblyEnabled = workspace && "line_assembly_enabled" in workspace ? workspace.line_assembly_enabled === true : false;
68722
+ const isAssemblyWorkspace = workspaceActionType === "assembly" || workspaceAssemblyEnabled;
68723
+ const shouldShowCycleTimeChart = !isUptimeMode && (showCycleTimeChart ?? isAssemblyWorkspace);
68015
68724
  const showIdleBreakdownChart = !shouldShowCycleTimeChart && idleTimeVlmEnabled;
68016
68725
  const idleClipDate = date || workspace?.date || calculatedOperationalDate || getOperationalDate(timezone);
68017
68726
  const idleClipShiftId = parsedShiftId ?? workspace?.shift_id;
68727
+ const hourlyIdleMinutes = React141.useMemo(() => {
68728
+ if (!shouldShowCycleTimeChart || !workspace?.idle_time_hourly || !workspace?.shift_start) return [];
68729
+ const parseTimeToMinutes3 = (time2) => {
68730
+ const [h, m] = time2.split(":").map(Number);
68731
+ if (!Number.isFinite(h) || !Number.isFinite(m)) return 0;
68732
+ return h * 60 + m;
68733
+ };
68734
+ const startTotal = parseTimeToMinutes3(workspace.shift_start);
68735
+ const endTotalRaw = workspace.shift_end ? parseTimeToMinutes3(workspace.shift_end) : startTotal + 11 * 60;
68736
+ const endTotal = endTotalRaw <= startTotal ? endTotalRaw + 24 * 60 : endTotalRaw;
68737
+ const shiftDuration = Math.max(60, endTotal - startTotal);
68738
+ const totalHourSlots = Math.max(1, Math.ceil(shiftDuration / 60));
68739
+ const idleTimeHourlyObj = workspace.idle_time_hourly || {};
68740
+ const result = Array(totalHourSlots).fill(0);
68741
+ for (let i = 0; i < totalHourSlots; i++) {
68742
+ const startSlotMins = startTotal + i * 60;
68743
+ let endSlotMins = startSlotMins + 60;
68744
+ if (endSlotMins > endTotal) endSlotMins = endTotal;
68745
+ let idleCount = 0;
68746
+ for (let m = startSlotMins; m < endSlotMins; m++) {
68747
+ const hourKey = Math.floor(m / 60) % 24;
68748
+ const minuteKey = m % 60;
68749
+ const hourData = idleTimeHourlyObj[hourKey.toString()] || [];
68750
+ if (Array.isArray(hourData)) {
68751
+ const val = hourData[minuteKey];
68752
+ if (val === 1 || val === "1") {
68753
+ idleCount++;
68754
+ }
68755
+ }
68756
+ }
68757
+ result[i] = idleCount;
68758
+ }
68759
+ return result;
68760
+ }, [shouldShowCycleTimeChart, workspace?.idle_time_hourly, workspace?.shift_start, workspace?.shift_end]);
68018
68761
  const shiftDurationMinutes = React141.useMemo(
68019
68762
  () => getShiftDurationMinutes(workspace?.shift_start, workspace?.shift_end),
68020
68763
  [workspace?.shift_start, workspace?.shift_end]
@@ -68087,7 +68830,7 @@ var WorkspaceDetailView = ({
68087
68830
  }
68088
68831
  }, [returnUrl]);
68089
68832
  const handleBackNavigation = () => {
68090
- if (date || shift) {
68833
+ if (isHistoricView) {
68091
68834
  setActiveTab("monthly_history");
68092
68835
  if (onNavigate) {
68093
68836
  const params = new URLSearchParams();
@@ -68251,7 +68994,7 @@ var WorkspaceDetailView = ({
68251
68994
  BackButtonMinimal,
68252
68995
  {
68253
68996
  onClick: handleBackNavigation,
68254
- text: previousView === "line_monthly_history" ? "Back to Line History" : returnUrl && returnUrl.includes("monthly_history") ? "Back to Line History" : returnUrl && returnUrl.includes("/kpis/") ? "Back to KPIs" : returnUrl && returnUrl.includes("/leaderboard/") ? "Back to Leaderboard" : (date || shift) && activeTab !== "monthly_history" ? "Back to Monthly History" : "Back",
68997
+ text: previousView === "line_monthly_history" ? "Back to Line History" : returnUrl && returnUrl.includes("monthly_history") ? "Back to Line History" : returnUrl && returnUrl.includes("/kpis/") ? "Back to KPIs" : returnUrl && returnUrl.includes("/leaderboard/") ? "Back to Leaderboard" : isHistoricView && activeTab !== "monthly_history" ? "Back to Monthly History" : "Back",
68255
68998
  size: "default",
68256
68999
  "aria-label": "Navigate back to previous page"
68257
69000
  }
@@ -68428,7 +69171,8 @@ var WorkspaceDetailView = ({
68428
69171
  {
68429
69172
  workspace,
68430
69173
  idleTimeReasons: idleTimeChartData,
68431
- efficiencyLegend
69174
+ efficiencyLegend,
69175
+ hourlyCycleTimes: cycleTimeChartData
68432
69176
  }
68433
69177
  ) }),
68434
69178
  activeTab === "monthly_history" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
@@ -68509,7 +69253,7 @@ var WorkspaceDetailView = ({
68509
69253
  animate: "animate",
68510
69254
  children: [
68511
69255
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center mb-4", children: [
68512
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-gray-700", children: isUptimeMode ? "Machine Utilization" : shouldShowCycleTimeChart ? "Cycle Time (last 60 minutes)" : "Hourly Output" }),
69256
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-bold text-gray-700", children: isUptimeMode ? "Machine Utilization" : shouldShowCycleTimeChart ? "Cycle time trend" : "Hourly Output" }),
68513
69257
  !isUptimeMode && /* @__PURE__ */ jsxRuntime.jsx(
68514
69258
  "button",
68515
69259
  {
@@ -68542,9 +69286,14 @@ var WorkspaceDetailView = ({
68542
69286
  ) : shouldShowCycleTimeChart ? /* @__PURE__ */ jsxRuntime.jsx(
68543
69287
  CycleTimeOverTimeChart,
68544
69288
  {
68545
- data: workspace.hourly_action_counts || [],
69289
+ data: cycleTimeChartData,
68546
69290
  idealCycleTime: workspace.ideal_cycle_time || 0,
68547
- shiftStart: workspace.shift_start || ""
69291
+ shiftStart: workspace.shift_start || "",
69292
+ shiftEnd: workspace.shift_end || "",
69293
+ xAxisMode: "hourly",
69294
+ datasetKey: cycleTimeDatasetKey,
69295
+ showIdleTime: showChartIdleTime,
69296
+ idleTimeData: hourlyIdleMinutes
68548
69297
  }
68549
69298
  ) : /* @__PURE__ */ jsxRuntime.jsx(
68550
69299
  HourlyOutputChart2,
@@ -68591,7 +69340,8 @@ var WorkspaceDetailView = ({
68591
69340
  {
68592
69341
  workspace,
68593
69342
  legend: efficiencyLegend,
68594
- layout: "stack"
69343
+ layout: "stack",
69344
+ idleTimeData: idleTimeVlmEnabled ? idleTimeData : void 0
68595
69345
  }
68596
69346
  ) : /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx(WorkspaceMetricCards, { workspace, legend: efficiencyLegend, className: "flex-1" }) })
68597
69347
  ] }),
@@ -68636,7 +69386,7 @@ var WorkspaceDetailView = ({
68636
69386
  animate: "animate",
68637
69387
  children: [
68638
69388
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-3 mb-4 flex-none", children: [
68639
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-gray-700", children: isUptimeMode ? "Machine Utilization" : shouldShowCycleTimeChart ? "Cycle Time (last 60 minutes)" : "Hourly Output" }),
69389
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-bold text-gray-700", children: isUptimeMode ? "Machine Utilization" : shouldShowCycleTimeChart ? "Cycle time trend" : "Hourly Output" }),
68640
69390
  !isUptimeMode && /* @__PURE__ */ jsxRuntime.jsx(
68641
69391
  "button",
68642
69392
  {
@@ -68665,9 +69415,14 @@ var WorkspaceDetailView = ({
68665
69415
  ) : shouldShowCycleTimeChart ? /* @__PURE__ */ jsxRuntime.jsx(
68666
69416
  CycleTimeOverTimeChart,
68667
69417
  {
68668
- data: workspace.hourly_action_counts || [],
69418
+ data: cycleTimeChartData,
68669
69419
  idealCycleTime: workspace.ideal_cycle_time || 0,
68670
- shiftStart: workspace.shift_start || ""
69420
+ shiftStart: workspace.shift_start || "",
69421
+ shiftEnd: workspace.shift_end || "",
69422
+ xAxisMode: "hourly",
69423
+ datasetKey: cycleTimeDatasetKey,
69424
+ showIdleTime: showChartIdleTime,
69425
+ idleTimeData: hourlyIdleMinutes
68671
69426
  }
68672
69427
  ) : /* @__PURE__ */ jsxRuntime.jsx(
68673
69428
  HourlyOutputChart2,
@@ -68719,7 +69474,8 @@ var WorkspaceDetailView = ({
68719
69474
  workspace,
68720
69475
  legend: efficiencyLegend,
68721
69476
  layout: "grid",
68722
- className: desktopBottomSectionClass
69477
+ className: desktopBottomSectionClass,
69478
+ idleTimeData: idleTimeVlmEnabled ? idleTimeData : void 0
68723
69479
  }
68724
69480
  ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: clsx("flex min-h-0", desktopBottomSectionClass), children: /* @__PURE__ */ jsxRuntime.jsx(WorkspaceMetricCards, { workspace, legend: efficiencyLegend, className: "flex-1" }) })
68725
69481
  ] })
@@ -68749,6 +69505,7 @@ var WorkspaceDetailView = ({
68749
69505
  availableShifts: shiftConfig?.shifts?.map((s) => ({ id: s.shiftId, name: s.shiftName })),
68750
69506
  legend: efficiencyLegend,
68751
69507
  trendSummary: workspaceMonthlyTrend,
69508
+ isAssemblyWorkspace,
68752
69509
  onDateSelect: (selectedDate, shiftId) => {
68753
69510
  if (onDateSelect) {
68754
69511
  onDateSelect(selectedDate, shiftId);
@@ -74836,6 +75593,11 @@ var normalizeTrend = (value) => ({
74836
75593
  });
74837
75594
  var normalizeIdleBreakdown = (value) => (value || []).map((item) => ({
74838
75595
  reason: item?.reason?.trim() || "Unknown",
75596
+ reason_key: item?.reason_key?.trim() || item?.reason?.trim() || "unknown",
75597
+ display_name: item?.display_name?.trim() || void 0,
75598
+ palette_token: item?.palette_token?.trim() || void 0,
75599
+ icon_token: item?.icon_token?.trim() || void 0,
75600
+ is_known: typeof item?.is_known === "boolean" ? item.is_known : null,
74839
75601
  percentage: normalizeNumber(item?.percentage),
74840
75602
  total_duration_seconds: normalizeNumber(item?.total_duration_seconds),
74841
75603
  efficiency_loss_percentage: normalizeNumber(item?.efficiency_loss_percentage),
@@ -75887,7 +76649,12 @@ var IdleBreakdownCard = React141__namespace.default.memo(({
75887
76649
  const showInitialSkeleton = idle.loading && idle.lastUpdated === null;
75888
76650
  const idleBreakdown = React141__namespace.default.useMemo(() => {
75889
76651
  return idle.data.map((item) => ({
75890
- name: item.reason?.trim() || "Unknown",
76652
+ name: item.display_name?.trim() || item.reason?.trim() || "Unknown",
76653
+ reasonKey: item.reason_key?.trim() || item.reason?.trim() || "unknown",
76654
+ displayName: item.display_name?.trim() || item.reason?.trim() || "Unknown",
76655
+ paletteToken: item.palette_token?.trim(),
76656
+ iconToken: item.icon_token?.trim(),
76657
+ isKnown: item.is_known ?? void 0,
75891
76658
  value: toNumber3(item.percentage) || 0,
75892
76659
  totalDurationSeconds: toNumber3(item.total_duration_seconds),
75893
76660
  efficiencyLossPercentage: toNumber3(item.efficiency_loss_percentage),
@@ -77179,6 +77946,7 @@ var streamProxyConfig = {
77179
77946
  }
77180
77947
  };
77181
77948
 
77949
+ exports.ACTION_FAMILIES = ACTION_FAMILIES;
77182
77950
  exports.ACTION_NAMES = ACTION_NAMES;
77183
77951
  exports.AIAgentView = AIAgentView_default;
77184
77952
  exports.AcceptInvite = AcceptInvite;
@@ -77291,7 +78059,7 @@ exports.LINE_1_UUID = LINE_1_UUID;
77291
78059
  exports.LINE_2_UUID = LINE_2_UUID;
77292
78060
  exports.LargeOutputProgressChart = LargeOutputProgressChart;
77293
78061
  exports.LeaderboardDetailView = LeaderboardDetailView_default;
77294
- exports.Legend = Legend6;
78062
+ exports.Legend = Legend5;
77295
78063
  exports.LineAssignmentDropdown = LineAssignmentDropdown;
77296
78064
  exports.LineChart = LineChart;
77297
78065
  exports.LineHistoryCalendar = LineHistoryCalendar;
@@ -77473,6 +78241,7 @@ exports.formatReasonLabel = formatReasonLabel;
77473
78241
  exports.formatRelativeTime = formatRelativeTime;
77474
78242
  exports.formatTimeInZone = formatTimeInZone;
77475
78243
  exports.fromUrlFriendlyName = fromUrlFriendlyName;
78244
+ exports.getActionDisplayName = getActionDisplayName;
77476
78245
  exports.getAllLineDisplayNames = getAllLineDisplayNames;
77477
78246
  exports.getAllThreadMessages = getAllThreadMessages;
77478
78247
  exports.getAllWorkspaceDisplayNamesAsync = getAllWorkspaceDisplayNamesAsync;
@@ -77511,7 +78280,6 @@ exports.getMonthKeyBounds = getMonthKeyBounds;
77511
78280
  exports.getMonthWeekRanges = getMonthWeekRanges;
77512
78281
  exports.getNextUpdateInterval = getNextUpdateInterval;
77513
78282
  exports.getOperationalDate = getOperationalDate;
77514
- exports.getReasonColor = getReasonColor;
77515
78283
  exports.getRoleAssignmentKind = getRoleAssignmentKind;
77516
78284
  exports.getRoleDescription = getRoleDescription;
77517
78285
  exports.getRoleLabel = getRoleLabel;
@@ -77546,6 +78314,7 @@ exports.isFactoryScopedRole = isFactoryScopedRole;
77546
78314
  exports.isFullMonthRange = isFullMonthRange;
77547
78315
  exports.isLegacyConfiguration = isLegacyConfiguration;
77548
78316
  exports.isPrefetchError = isPrefetchError;
78317
+ exports.isRecentFlowVideoGridMetricMode = isRecentFlowVideoGridMetricMode;
77549
78318
  exports.isSafari = isSafari;
77550
78319
  exports.isSupervisorRole = isSupervisorRole;
77551
78320
  exports.isTransitionPeriod = isTransitionPeriod;
@@ -77556,14 +78325,17 @@ exports.isValidPrefetchParams = isValidPrefetchParams;
77556
78325
  exports.isValidPrefetchStatus = isValidPrefetchStatus;
77557
78326
  exports.isValidWorkspaceDetailedMetricsPayload = isValidWorkspaceDetailedMetricsPayload;
77558
78327
  exports.isValidWorkspaceMetricsPayload = isValidWorkspaceMetricsPayload;
78328
+ exports.isWipGatedVideoGridMetricMode = isWipGatedVideoGridMetricMode;
77559
78329
  exports.isWorkspaceDisplayNamesLoaded = isWorkspaceDisplayNamesLoaded;
77560
78330
  exports.isWorkspaceDisplayNamesLoading = isWorkspaceDisplayNamesLoading;
77561
78331
  exports.lineLeaderboardService = lineLeaderboardService;
77562
78332
  exports.linesService = linesService;
77563
78333
  exports.mergeWithDefaultConfig = mergeWithDefaultConfig;
77564
78334
  exports.migrateLegacyConfiguration = migrateLegacyConfiguration;
78335
+ exports.normalizeActionFamily = normalizeActionFamily;
77565
78336
  exports.normalizeDateKeyRange = normalizeDateKeyRange;
77566
78337
  exports.normalizeRoleLevel = normalizeRoleLevel;
78338
+ exports.normalizeVideoGridMetricMode = normalizeVideoGridMetricMode;
77567
78339
  exports.optifyeAgentClient = optifyeAgentClient;
77568
78340
  exports.parseDateKeyToDate = parseDateKeyToDate;
77569
78341
  exports.parseS3Uri = parseS3Uri;