@optifye/dashboard-core 6.11.12 → 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,40 @@ 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);
33566
- var getVideoGridBaseColorState = (workspace, legend = DEFAULT_EFFICIENCY_LEGEND) => getEfficiencyColor(getVideoGridMetricValue(workspace), legend);
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
+ };
33567
33811
  var isLowWipGreenOverride = (workspace, legend = DEFAULT_EFFICIENCY_LEGEND) => {
33568
- if (!hasAssemblyRecentFlow(workspace)) {
33812
+ if (!hasVideoGridRecentFlow(workspace) || !isVideoGridWipGated(workspace)) {
33569
33813
  return false;
33570
33814
  }
33571
33815
  if (getVideoGridBaseColorState(workspace, legend) !== "red") {
@@ -33604,7 +33848,10 @@ var getSyntheticLowWipDisplayValue = (workspace, minuteBucket) => {
33604
33848
  var getVideoGridDisplayValue = (workspace, legend = DEFAULT_EFFICIENCY_LEGEND, minuteBucket) => isLowWipGreenOverride(workspace, legend) ? getSyntheticLowWipDisplayValue(workspace, minuteBucket) : getVideoGridMetricValue(workspace);
33605
33849
  var getVideoGridColorState = (workspace, legend = DEFAULT_EFFICIENCY_LEGEND) => {
33606
33850
  const baseColor = getVideoGridBaseColorState(workspace, legend);
33607
- if (!hasAssemblyRecentFlow(workspace)) {
33851
+ if (!hasVideoGridRecentFlow(workspace)) {
33852
+ return baseColor;
33853
+ }
33854
+ if (!isVideoGridWipGated(workspace)) {
33608
33855
  return baseColor;
33609
33856
  }
33610
33857
  if (baseColor !== "red") {
@@ -33626,11 +33873,11 @@ var getVideoGridLegendLabel = (workspaces) => {
33626
33873
  if (visibleWorkspaces.length === 0) {
33627
33874
  return MAP_GRID_LEGEND_LABEL;
33628
33875
  }
33629
- const assemblyEnabledCount = visibleWorkspaces.filter(isAssemblyFlowEnabled).length;
33630
- if (assemblyEnabledCount === 0) {
33876
+ const recentFlowEnabledCount = visibleWorkspaces.filter(isVideoGridRecentFlowEnabled).length;
33877
+ if (recentFlowEnabledCount === 0) {
33631
33878
  return MAP_GRID_LEGEND_LABEL;
33632
33879
  }
33633
- if (assemblyEnabledCount === visibleWorkspaces.length) {
33880
+ if (recentFlowEnabledCount === visibleWorkspaces.length) {
33634
33881
  return VIDEO_GRID_LEGEND_LABEL;
33635
33882
  }
33636
33883
  return MIXED_VIDEO_GRID_LEGEND_LABEL;
@@ -33680,7 +33927,11 @@ var VideoCard = React141__namespace.default.memo(({
33680
33927
  const videoGridMetricValue = getVideoGridMetricValue(workspace);
33681
33928
  const videoGridDisplayValue = getVideoGridDisplayValue(workspace, effectiveLegend, displayMinuteBucket);
33682
33929
  const videoGridColorState = getVideoGridColorState(workspace, effectiveLegend);
33683
- const badgeTitle = hasAssemblyRecentFlow(workspace) ? `Flow ${Math.round(videoGridDisplayValue)}%` : `Efficiency ${Math.round(videoGridDisplayValue)}%`;
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";
33684
33935
  const efficiencyOverlayClass = videoGridColorState === "green" ? "bg-[#00D654]/25" : videoGridColorState === "yellow" ? "bg-[#FFD700]/30" : videoGridColorState === "red" ? "bg-[#FF2D0A]/30" : "bg-transparent";
33685
33936
  const efficiencyBarClass = videoGridColorState === "green" ? "bg-[#00AB45]" : videoGridColorState === "yellow" ? "bg-[#FFB020]" : videoGridColorState === "red" ? "bg-[#E34329]" : "bg-gray-500/70";
33686
33937
  const efficiencyStatus = videoGridColorState === "green" ? "High" : videoGridColorState === "yellow" ? "Medium" : videoGridColorState === "red" ? "Low" : "Neutral";
@@ -33762,10 +34013,7 @@ var VideoCard = React141__namespace.default.memo(({
33762
34013
  "data-testid": "video-card-metric-badge",
33763
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`,
33764
34015
  title: badgeTitle,
33765
- children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: `${compact ? "text-[10px]" : "text-xs"} font-semibold`, children: [
33766
- Math.round(videoGridDisplayValue),
33767
- "%"
33768
- ] })
34016
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${compact ? "text-[10px]" : "text-xs"} font-semibold`, children: badgeLabel })
33769
34017
  }
33770
34018
  ) }),
33771
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(
@@ -33773,7 +34021,7 @@ var VideoCard = React141__namespace.default.memo(({
33773
34021
  {
33774
34022
  "data-testid": "video-card-metric-bar",
33775
34023
  className: `h-full ${efficiencyBarClass} transition-all duration-500`,
33776
- style: { width: `${Math.min(100, videoGridMetricValue)}%` }
34024
+ style: { width: `${hasBarMetric ? Math.min(100, Math.max(0, videoGridMetricValue)) : videoGridColorState === "neutral" ? 100 : 0}%` }
33777
34025
  }
33778
34026
  ) })
33779
34027
  ] }),
@@ -33804,7 +34052,7 @@ var VideoCard = React141__namespace.default.memo(({
33804
34052
  }
33805
34053
  );
33806
34054
  }, (prevProps, nextProps) => {
33807
- 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) {
33808
34056
  return false;
33809
34057
  }
33810
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) {
@@ -39328,45 +39576,56 @@ var FileManagerFilters = ({
39328
39576
  });
39329
39577
  const [loadingPercentile, setLoadingPercentile] = React141.useState(false);
39330
39578
  const resolvedTargetCycleTime = targetCycleTime && targetCycleTime > 0 ? targetCycleTime : null;
39331
- const getRootCauseConfig = (reason) => {
39332
- const colorConfig = getIdleTimeReasonColor(reason);
39333
- let Icon2 = lucideReact.HelpCircle;
39334
- if (reason.includes("Machine Breakdown")) Icon2 = lucideReact.AlertTriangle;
39335
- else if (reason.includes("Material")) Icon2 = lucideReact.Package;
39336
- else if (reason.includes("Worker") || reason.includes("Absent")) Icon2 = lucideReact.UserX;
39337
- else if (reason.includes("Quality")) Icon2 = lucideReact.XCircle;
39338
- else if (reason.includes("Power")) Icon2 = lucideReact.Zap;
39339
- else if (reason.includes("Maintenance") || reason.includes("Tool")) Icon2 = lucideReact.Wrench;
39340
- 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
+ });
39341
39594
  return {
39342
- color: colorConfig.text,
39343
- bgColor: colorConfig.bg,
39344
- borderColor: colorConfig.border,
39345
- Icon: Icon2,
39346
- 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
39347
39601
  };
39348
- };
39349
- const normalizeIdleReasonLabel = React141.useCallback((label) => {
39350
- return label.replace(/_/g, " ").trim();
39351
39602
  }, []);
39352
- const getIdleTimeRootCause = React141.useCallback((clipId) => {
39353
- const classification = mergedClipClassifications[clipId];
39354
- if (!classification) return "processing";
39355
- if (classification.status === "processing") return "processing";
39356
- return classification.label || "processing";
39357
- }, [mergedClipClassifications]);
39358
39603
  const idleReasonOptions = React141.useMemo(() => {
39359
39604
  const idleClips = clipMetadata["idle_time"] || [];
39360
- const uniqueReasons = /* @__PURE__ */ new Set();
39605
+ const uniqueReasons = /* @__PURE__ */ new Map();
39361
39606
  idleClips.forEach((clip) => {
39362
39607
  const clipId = clip.clipId || clip.id;
39363
39608
  if (!clipId) return;
39364
- const reason = getIdleTimeRootCause(clipId);
39365
- if (!reason || reason === "processing") return;
39366
- 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
+ }
39367
39622
  });
39368
- return Array.from(uniqueReasons).sort((a, b) => a.localeCompare(b));
39369
- }, [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
+ );
39370
39629
  const getClipBadge = React141.useCallback((node) => {
39371
39630
  if (node.categoryId === "idle_time" || node.categoryId === "low_value") {
39372
39631
  return { text: "Idle", className: "bg-red-100 text-red-700" };
@@ -39438,6 +39697,10 @@ var FileManagerFilters = ({
39438
39697
  seededClassifications[clip.clipId] = {
39439
39698
  status: clip.classification_status,
39440
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,
39441
39704
  confidence: clip.classification_confidence ?? void 0
39442
39705
  };
39443
39706
  }
@@ -39821,7 +40084,7 @@ var FileManagerFilters = ({
39821
40084
  return slot ? slot.label : value;
39822
40085
  }, [timeSlots]);
39823
40086
  const isClipInTimeRange = React141.useCallback((clipTimestamp) => {
39824
- if (!isTimeFilterActive || !startTime || !endTime) {
40087
+ if (!startTime || !endTime) {
39825
40088
  return true;
39826
40089
  }
39827
40090
  try {
@@ -39847,9 +40110,8 @@ var FileManagerFilters = ({
39847
40110
  let filteredClips = categoryClips.filter((clip) => isClipInTimeRange(clip.clip_timestamp));
39848
40111
  if (category.id === "idle_time" && idleLabelFilter) {
39849
40112
  filteredClips = filteredClips.filter((clip) => {
39850
- const rootCause = getIdleTimeRootCause(clip.clipId || clip.id);
39851
- const normalizedRootCause = rootCause.replace(/_/g, " ");
39852
- return normalizedRootCause === idleLabelFilter;
40113
+ const classification = getIdleTimeClassification(clip.clipId || clip.id);
40114
+ return classification?.label === idleLabelFilter;
39853
40115
  });
39854
40116
  }
39855
40117
  const displayCount = isTimeFilterActive || category.id === "idle_time" && idleLabelFilter ? filteredClips.length : categoryCount;
@@ -39879,8 +40141,9 @@ var FileManagerFilters = ({
39879
40141
  clipPosition: index + 1,
39880
40142
  // Store 1-based position
39881
40143
  cycleTimeSeconds: cycleTime,
39882
- duration: clip.duration
40144
+ duration: clip.duration,
39883
40145
  // Store duration for custom badge rendering
40146
+ cycleItemCount: clip.cycle_item_count ?? null
39884
40147
  };
39885
40148
  });
39886
40149
  regularCategoryNodes.push({
@@ -40136,14 +40399,14 @@ var FileManagerFilters = ({
40136
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") ? (
40137
40400
  // Show root cause icon for idle time clips
40138
40401
  (() => {
40139
- const rootCause = getIdleTimeRootCause(node.clipId || node.id);
40140
- if (rootCause === "processing") {
40402
+ const classification = getIdleTimeClassification(node.clipId || node.id);
40403
+ if (!classification || classification.status === "processing") {
40141
40404
  return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "h-3.5 w-3.5 text-purple-500", strokeWidth: 2.5 });
40142
40405
  }
40143
40406
  if (!idleTimeVlmEnabled && node.categoryId === "idle_time") {
40144
40407
  return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "h-3.5 w-3.5 text-purple-500", strokeWidth: 2.5 });
40145
40408
  }
40146
- const config = getRootCauseConfig(rootCause.replace(/_/g, " "));
40409
+ const config = getRootCauseConfig(classification);
40147
40410
  if (config) {
40148
40411
  const IconComponent = config.Icon;
40149
40412
  return /* @__PURE__ */ jsxRuntime.jsx(IconComponent, { className: `h-3.5 w-3.5 ${config.iconColor}`, strokeWidth: 2.5 });
@@ -40165,10 +40428,10 @@ var FileManagerFilters = ({
40165
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" });
40166
40429
  }
40167
40430
  const clipId = node.clipId || node.id;
40168
- const rootCause = getIdleTimeRootCause(clipId);
40169
- const isProcessing = rootCause === "processing";
40170
- const displayLabel = isProcessing ? "Analyzing..." : rootCause.replace(/_/g, " ");
40171
- 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...";
40172
40435
  const bgClass = config ? config.bgColor : "bg-slate-50";
40173
40436
  const textClass = config ? config.color : "text-slate-700";
40174
40437
  const borderClass = config ? config.borderColor : "border-slate-200";
@@ -40178,7 +40441,13 @@ var FileManagerFilters = ({
40178
40441
  // Show badge for other clips
40179
40442
  (() => {
40180
40443
  const badge = getClipBadge(node);
40181
- 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
+ ] });
40182
40451
  })()
40183
40452
  ) })
40184
40453
  ] }),
@@ -40266,7 +40535,7 @@ var FileManagerFilters = ({
40266
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: [
40267
40536
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-medium", children: [
40268
40537
  "Reason: ",
40269
- idleLabelFilter.replace(/_/g, " ")
40538
+ selectedIdleReasonOption?.displayName || idleLabelFilter.replace(/_/g, " ")
40270
40539
  ] }),
40271
40540
  /* @__PURE__ */ jsxRuntime.jsx(
40272
40541
  "button",
@@ -40315,21 +40584,31 @@ var FileManagerFilters = ({
40315
40584
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 animate-spin" }),
40316
40585
  "Loading idle reasons..."
40317
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) => {
40318
- 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
+ }
40319
40598
  return /* @__PURE__ */ jsxRuntime.jsxs(
40320
40599
  "button",
40321
40600
  {
40322
40601
  onClick: () => {
40323
- setIdleLabelFilter(reason);
40602
+ setIdleLabelFilter(reason.label);
40324
40603
  setShowIdleLabelFilterModal(false);
40325
40604
  },
40326
- 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"}`,
40327
40606
  children: [
40328
40607
  /* @__PURE__ */ jsxRuntime.jsx(config.Icon, { className: `h-3.5 w-3.5 ${config.iconColor}` }),
40329
- reason
40608
+ reason.displayName
40330
40609
  ]
40331
40610
  },
40332
- reason
40611
+ reason.label
40333
40612
  );
40334
40613
  }) })
40335
40614
  ]
@@ -40444,33 +40723,14 @@ var FileManagerFilters = ({
40444
40723
  slot.value
40445
40724
  )) })
40446
40725
  ] })
40447
- ] }) }),
40448
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-3 border-t border-slate-200 bg-white rounded-b-xl", children: /* @__PURE__ */ jsxRuntime.jsx(
40449
- "button",
40450
- {
40451
- onClick: () => {
40452
- if (startTime && endTime) {
40453
- setIsTimeFilterActive(true);
40454
- }
40455
- setShowTimeFilterModal(false);
40456
- setStartSearchTerm("");
40457
- setEndSearchTerm("");
40458
- },
40459
- disabled: !startTime || !endTime,
40460
- className: `
40461
- w-full px-4 py-2 text-sm font-semibold rounded-lg transition-all
40462
- ${startTime && endTime ? "bg-blue-600 text-white hover:bg-blue-700 active:scale-[0.98]" : "bg-slate-200 text-slate-400 cursor-not-allowed"}
40463
- `,
40464
- children: "Apply"
40465
- }
40466
- ) })
40726
+ ] }) })
40467
40727
  ]
40468
40728
  }
40469
40729
  )
40470
40730
  ] }),
40471
40731
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-4 py-3 flex-1 min-h-0 overflow-y-auto scrollbar-thin", children: [
40472
40732
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: filterTree.map((node) => renderNode(node)) }),
40473
- 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: [
40474
40734
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-slate-300 mb-4", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "h-12 w-12 mx-auto" }) }),
40475
40735
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-slate-700 mb-2", children: "No clips found" }),
40476
40736
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-slate-500 mb-4", children: "No clips match the selected time range" }),
@@ -40480,18 +40740,19 @@ var FileManagerFilters = ({
40480
40740
  onClick: () => {
40481
40741
  setStartTime("");
40482
40742
  setEndTime("");
40743
+ setIsTimeFilterActive(false);
40483
40744
  },
40484
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",
40485
40746
  children: "Clear time filter"
40486
40747
  }
40487
40748
  )
40488
40749
  ] }),
40489
- 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: [
40490
40751
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-slate-300 mb-4", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.HelpCircle, { className: "h-12 w-12 mx-auto" }) }),
40491
40752
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-slate-700 mb-2", children: "No clip types available" }),
40492
40753
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-slate-500", children: "Loading clip categories..." })
40493
40754
  ] }),
40494
- 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: [
40495
40756
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-slate-300 mb-4", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Play, { className: "h-12 w-12 mx-auto" }) }),
40496
40757
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-slate-700 mb-2", children: "No clips available" }),
40497
40758
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-slate-500", children: "No clips found for the selected time period" })
@@ -40895,6 +41156,34 @@ function useClipsRealtimeUpdates({
40895
41156
  hasNewClips: newClipsNotification !== null && newClipsNotification.count > 0
40896
41157
  };
40897
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
+ };
40898
41187
  var BottlenecksContent = ({
40899
41188
  workspaceId,
40900
41189
  workspaceName,
@@ -41635,6 +41924,10 @@ var BottlenecksContent = ({
41635
41924
  classificationUpdates[clipId] = {
41636
41925
  status,
41637
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,
41638
41931
  confidence: clip.classification_confidence ?? void 0
41639
41932
  };
41640
41933
  });
@@ -42419,79 +42712,13 @@ var BottlenecksContent = ({
42419
42712
  return () => window.removeEventListener("keydown", handleEscape);
42420
42713
  }
42421
42714
  }, [isFullscreen, exitFullscreen]);
42422
- const ROOT_CAUSE_CONFIG = {
42423
- "Machine Breakdown": {
42424
- color: "text-white",
42425
- bgColor: "bg-black/60 backdrop-blur-sm",
42426
- borderColor: "border-transparent",
42427
- Icon: lucideReact.AlertTriangle,
42428
- iconColor: "text-red-500",
42429
- // Standard red for high visibility on dark
42430
- dotColor: "bg-red-600"
42431
- },
42432
- "Other": {
42433
- color: "text-white",
42434
- bgColor: "bg-black/60 backdrop-blur-sm",
42435
- borderColor: "border-transparent",
42436
- Icon: lucideReact.HelpCircle,
42437
- iconColor: "text-gray-400",
42438
- dotColor: "bg-gray-500"
42439
- },
42440
- "Power Outage": {
42441
- color: "text-white",
42442
- bgColor: "bg-black/60 backdrop-blur-sm",
42443
- borderColor: "border-transparent",
42444
- Icon: lucideReact.Zap,
42445
- iconColor: "text-red-400",
42446
- // Lighter red
42447
- dotColor: "bg-red-500"
42448
- },
42449
- "Worker Absent": {
42450
- color: "text-white",
42451
- bgColor: "bg-black/60 backdrop-blur-sm",
42452
- borderColor: "border-transparent",
42453
- Icon: lucideReact.UserX,
42454
- iconColor: "text-red-400",
42455
- // Lighter red
42456
- dotColor: "bg-red-500"
42457
- },
42458
- "Material Shortage": {
42459
- color: "text-white",
42460
- bgColor: "bg-black/60 backdrop-blur-sm",
42461
- borderColor: "border-transparent",
42462
- Icon: lucideReact.Package,
42463
- iconColor: "text-red-300",
42464
- // Even lighter red
42465
- dotColor: "bg-red-400"
42466
- },
42467
- "Quality Issue": {
42468
- color: "text-white",
42469
- bgColor: "bg-black/60 backdrop-blur-sm",
42470
- borderColor: "border-transparent",
42471
- Icon: lucideReact.XCircle,
42472
- iconColor: "text-red-300",
42473
- // Even lighter red
42474
- dotColor: "bg-red-400"
42475
- },
42476
- "Scheduled Maintenance": {
42477
- color: "text-white",
42478
- bgColor: "bg-black/60 backdrop-blur-sm",
42479
- borderColor: "border-transparent",
42480
- Icon: lucideReact.Wrench,
42481
- iconColor: "text-red-200",
42482
- // Very light red
42483
- dotColor: "bg-red-300"
42484
- }
42485
- };
42486
- const getIdleTimeRootCause = React141.useCallback((video) => {
42715
+ const getIdleTimeClassification = React141.useCallback((video) => {
42487
42716
  if (video.type !== "idle_time") return null;
42488
42717
  const videoId = video.id || video.clipId;
42489
- if (!videoId) return "processing";
42718
+ if (!videoId) return null;
42490
42719
  const classification = clipClassifications[videoId];
42491
- console.log(`[getIdleTimeRootCause] Looking up ${videoId}: `, classification);
42492
- if (!classification) return "processing";
42493
- if (classification.status === "processing") return "processing";
42494
- return classification.label || "processing";
42720
+ console.log(`[getIdleTimeClassification] Looking up ${videoId}: `, classification);
42721
+ return classification || null;
42495
42722
  }, [clipClassifications]);
42496
42723
  const getIdleTimeConfidence = React141.useCallback((video) => {
42497
42724
  if (video.type !== "idle_time") return null;
@@ -42502,36 +42729,35 @@ var BottlenecksContent = ({
42502
42729
  if (classification.status === "processing") return null;
42503
42730
  return classification.confidence ?? null;
42504
42731
  }, [clipClassifications]);
42505
- const formatDisplayLabel = React141.useCallback((label) => {
42506
- return label.replace(/_/g, " ");
42507
- }, []);
42508
- const getRootCauseConfig = React141.useCallback((rootCause) => {
42509
- if (!rootCause) return null;
42510
- if (rootCause === "processing") {
42732
+ const getRootCauseConfig = React141.useCallback((classification) => {
42733
+ if (!classification || classification.status === "processing") {
42511
42734
  return {
42512
42735
  color: "text-white",
42513
42736
  bgColor: "bg-black/60 backdrop-blur-sm",
42514
- borderColor: "border-gray-500/30",
42737
+ borderColor: "border-white/10",
42515
42738
  Icon: lucideReact.Clock,
42516
- iconColor: "text-purple-500",
42517
- dotColor: "bg-gray-400",
42739
+ iconColor: "text-purple-300",
42740
+ dotColor: "bg-purple-400",
42518
42741
  displayName: "Analyzing..."
42519
42742
  };
42520
42743
  }
42521
- const hardcodedConfig = ROOT_CAUSE_CONFIG[rootCause];
42522
- if (hardcodedConfig) {
42523
- return hardcodedConfig;
42524
- }
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
+ });
42525
42751
  return {
42526
42752
  color: "text-white",
42527
42753
  bgColor: "bg-black/60 backdrop-blur-sm",
42528
- borderColor: "border-purple-500/30",
42529
- Icon: lucideReact.Tag,
42530
- iconColor: "text-purple-400",
42531
- dotColor: "bg-purple-400",
42532
- 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
42533
42759
  };
42534
- }, [formatDisplayLabel]);
42760
+ }, []);
42535
42761
  const getClipTypeLabel = React141.useCallback((video) => {
42536
42762
  if (!video) return "";
42537
42763
  const currentFilter = activeFilterRef.current;
@@ -42696,15 +42922,15 @@ var BottlenecksContent = ({
42696
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" ? (
42697
42923
  // Show full colored badge for idle time
42698
42924
  (() => {
42699
- const rootCause = getIdleTimeRootCause(currentVideo);
42925
+ const classification = getIdleTimeClassification(currentVideo);
42700
42926
  const confidence = getIdleTimeConfidence(currentVideo);
42701
- const config = getRootCauseConfig(rootCause);
42927
+ const config = getRootCauseConfig(classification);
42702
42928
  if (!config) return null;
42703
42929
  const IconComponent = config.Icon;
42704
42930
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
42705
- 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: [
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: 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..." })
42708
42934
  ] }) }),
42709
42935
  idleTimeVlmEnabled && confidence !== null && (() => {
42710
42936
  const confidencePercent = confidence * 100;
@@ -42739,9 +42965,9 @@ var BottlenecksContent = ({
42739
42965
  currentVideo.type === "idle_time" ? (
42740
42966
  // Show full colored badge for idle time
42741
42967
  (() => {
42742
- const rootCause = getIdleTimeRootCause(currentVideo);
42968
+ const classification = getIdleTimeClassification(currentVideo);
42743
42969
  const confidence = getIdleTimeConfidence(currentVideo);
42744
- const config = getRootCauseConfig(rootCause);
42970
+ const config = getRootCauseConfig(classification);
42745
42971
  if (!config) return null;
42746
42972
  const IconComponent = config.Icon;
42747
42973
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
@@ -42750,9 +42976,9 @@ var BottlenecksContent = ({
42750
42976
  (confidence * 100).toFixed(0),
42751
42977
  "%"
42752
42978
  ] }) }) }),
42753
- 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: [
42754
- /* @__PURE__ */ jsxRuntime.jsx(IconComponent, { className: `h-3.5 w-3.5 ${config.iconColor}`, strokeWidth: 2.5 }),
42755
- /* @__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..." })
42756
42982
  ] }) })
42757
42983
  ] });
42758
42984
  })()
@@ -42792,6 +43018,7 @@ var BottlenecksContent = ({
42792
43018
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500", children: "No clips found" })
42793
43019
  ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: triageClips.map((clip, index) => {
42794
43020
  const isIdleTime = clip.categoryId === "idle_time";
43021
+ const cycleItemCount = getMultiCycleItemCount(clip);
42795
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", {
42796
43023
  hour12: true,
42797
43024
  hour: "numeric",
@@ -42844,15 +43071,15 @@ var BottlenecksContent = ({
42844
43071
  // Idle time clips - show root cause label below time
42845
43072
  (() => {
42846
43073
  const clipId = clip.id || clip.clipId;
42847
- const rootCause = getIdleTimeRootCause({
43074
+ const classification = getIdleTimeClassification({
42848
43075
  id: clipId,
42849
43076
  type: "idle_time",
42850
43077
  creation_timestamp: clip.clip_timestamp
42851
43078
  });
42852
- console.log(`[BottlenecksContent] Sidebar clip ${clipId}: rootCause=${rootCause}, classification=`, clipClassifications[clipId]);
42853
- const config = getRootCauseConfig(rootCause);
43079
+ console.log(`[BottlenecksContent] Sidebar clip ${clipId}: classification=`, clipClassifications[clipId]);
43080
+ const config = getRootCauseConfig(classification);
42854
43081
  if (!config) {
42855
- console.log(`[BottlenecksContent] No config found for rootCause: ${rootCause}`);
43082
+ console.log(`[BottlenecksContent] No config found for classification on clip: ${clipId}`);
42856
43083
  return null;
42857
43084
  }
42858
43085
  const IconComponent = config.Icon;
@@ -42867,7 +43094,7 @@ var BottlenecksContent = ({
42867
43094
  ] }) })
42868
43095
  ] }),
42869
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: (() => {
42870
- const displayText = config.displayName || (rootCause ? formatDisplayLabel(rootCause) : "Analyzing...");
43097
+ const displayText = config.displayName || "Analyzing...";
42871
43098
  console.log(`[BottlenecksContent] Displaying label: "${displayText}" for clip ${clipId}`);
42872
43099
  return displayText;
42873
43100
  })() }) : (() => {
@@ -42887,7 +43114,13 @@ var BottlenecksContent = ({
42887
43114
  clip.duration ? `${clip.duration.toFixed(1)}s` : "N/A",
42888
43115
  ")"
42889
43116
  ] }) }),
42890
- /* @__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
+ ] })
42891
43124
  ] })
42892
43125
  )
42893
43126
  },
@@ -42980,15 +43213,15 @@ var BottlenecksContent = ({
42980
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" ? (
42981
43214
  // Show full colored badge for idle time
42982
43215
  (() => {
42983
- const rootCause = getIdleTimeRootCause(currentVideo);
43216
+ const classification = getIdleTimeClassification(currentVideo);
42984
43217
  const confidence = getIdleTimeConfidence(currentVideo);
42985
- const config = getRootCauseConfig(rootCause);
43218
+ const config = getRootCauseConfig(classification);
42986
43219
  if (!config) return null;
42987
43220
  const IconComponent = config.Icon;
42988
43221
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
42989
- 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: [
42990
- /* @__PURE__ */ jsxRuntime.jsx(IconComponent, { className: `h-4 w-4 ${config.iconColor}`, strokeWidth: 2.5 }),
42991
- /* @__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..." })
42992
43225
  ] }) }),
42993
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: [
42994
43227
  "AI Confidence: ",
@@ -43010,9 +43243,9 @@ var BottlenecksContent = ({
43010
43243
  ] }) }) : currentVideo.type === "idle_time" ? (
43011
43244
  // Show full colored badge for idle time
43012
43245
  (() => {
43013
- const rootCause = getIdleTimeRootCause(currentVideo);
43246
+ const classification = getIdleTimeClassification(currentVideo);
43014
43247
  const confidence = getIdleTimeConfidence(currentVideo);
43015
- const config = getRootCauseConfig(rootCause);
43248
+ const config = getRootCauseConfig(classification);
43016
43249
  if (!config) return null;
43017
43250
  const IconComponent = config.Icon;
43018
43251
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
@@ -43021,9 +43254,9 @@ var BottlenecksContent = ({
43021
43254
  (confidence * 100).toFixed(0),
43022
43255
  "%"
43023
43256
  ] }) }) }),
43024
- 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: [
43025
- /* @__PURE__ */ jsxRuntime.jsx(IconComponent, { className: `h-4 w-4 ${config.iconColor}`, strokeWidth: 2.5 }),
43026
- /* @__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..." })
43027
43260
  ] }) })
43028
43261
  ] });
43029
43262
  })()
@@ -44972,16 +45205,6 @@ var LineHistoryCalendar = ({
44972
45205
  ] });
44973
45206
  };
44974
45207
  var LineHistoryCalendar_default = LineHistoryCalendar;
44975
- var STATIC_COLORS = {
44976
- "Operator Absent": "#dc2626",
44977
- // red-600 - Critical/Urgent
44978
- "No Material": "#f59e0b",
44979
- // amber-500 - Warning/Supply Chain
44980
- "Machine Downtime": "#3b82f6",
44981
- // blue-500 - Scheduled/Technical
44982
- "Operator Idle": "#8b5cf6"
44983
- // violet-500 - Low Priority/Behavioral
44984
- };
44985
45208
  var PRODUCTIVE_COLOR = "#00AB45";
44986
45209
  var IDLE_COLOR = "#e5e7eb";
44987
45210
  var formatDuration = (seconds) => {
@@ -45004,19 +45227,21 @@ var formatDuration = (seconds) => {
45004
45227
  }
45005
45228
  return parts.join(" ");
45006
45229
  };
45007
- var getColorForEntry = (name, index) => {
45008
- const normalized = name.trim().toLowerCase();
45230
+ var getColorForEntry = (entry) => {
45231
+ const normalized = entry.name.trim().toLowerCase();
45009
45232
  if (normalized === "productive" || normalized === "productive time") {
45010
45233
  return PRODUCTIVE_COLOR;
45011
45234
  }
45012
45235
  if (normalized === "idle" || normalized === "idle time") {
45013
45236
  return IDLE_COLOR;
45014
45237
  }
45015
- if (STATIC_COLORS[name]) {
45016
- return STATIC_COLORS[name];
45017
- }
45018
- const snakeCaseName = name.replace(/ /g, "_");
45019
- 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
+ });
45020
45245
  };
45021
45246
  var CustomTooltip = ({ active, payload, hideTotalDuration }) => {
45022
45247
  if (active && payload && payload.length) {
@@ -45030,8 +45255,8 @@ var CustomTooltip = ({ active, payload, hideTotalDuration }) => {
45030
45255
  const hasContributors = contributors.length > 0;
45031
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: [
45032
45257
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 pb-2 mb-2 border-b border-slate-100", children: [
45033
- /* @__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) } }),
45034
- /* @__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, " ") })
45035
45260
  ] }),
45036
45261
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5 pb-1", children: [
45037
45262
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center gap-4", children: [
@@ -45092,7 +45317,8 @@ var IdleTimeReasonChartComponent = ({
45092
45317
  isLoading = false,
45093
45318
  error = null,
45094
45319
  hideTotalDuration = false,
45095
- updateAnimation = "replay"
45320
+ updateAnimation = "replay",
45321
+ variant = "pie"
45096
45322
  }) => {
45097
45323
  const [activeData, setActiveData] = React141__namespace.default.useState([]);
45098
45324
  React141__namespace.default.useEffect(() => {
@@ -45137,6 +45363,73 @@ var IdleTimeReasonChartComponent = ({
45137
45363
  if (!data || data.length === 0) {
45138
45364
  return /* @__PURE__ */ jsxRuntime.jsx(EmptyState, { message: "No Idle Time reasons available" });
45139
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
+ }
45140
45433
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
45141
45434
  /* @__PURE__ */ jsxRuntime.jsx("style", { children: `
45142
45435
  .recharts-wrapper:focus,
@@ -45198,7 +45491,7 @@ var IdleTimeReasonChartComponent = ({
45198
45491
  children: activeData.map((entry, index) => /* @__PURE__ */ jsxRuntime.jsx(
45199
45492
  recharts.Cell,
45200
45493
  {
45201
- fill: getColorForEntry(entry.name, index),
45494
+ fill: getColorForEntry(entry),
45202
45495
  strokeWidth: 0
45203
45496
  },
45204
45497
  `cell-${index}`
@@ -45215,10 +45508,10 @@ var IdleTimeReasonChartComponent = ({
45215
45508
  "span",
45216
45509
  {
45217
45510
  className: "inline-block w-2.5 h-2.5 rounded-full mr-1.5 mt-0.5 flex-shrink-0",
45218
- style: { backgroundColor: getColorForEntry(entry.name, index) }
45511
+ style: { backgroundColor: getColorForEntry(entry) }
45219
45512
  }
45220
45513
  ),
45221
- /* @__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, " ") })
45222
45515
  ] }, `item-${index}`)) }) })
45223
45516
  ]
45224
45517
  }
@@ -45343,7 +45636,6 @@ var LineMonthlyHistory = ({
45343
45636
  const { isIdleTimeVlmEnabled } = useIdleTimeVlmConfig();
45344
45637
  const idleTimeVlmEnabled = isIdleTimeVlmEnabled(lineId);
45345
45638
  const isUptimeMode = monitoringMode === "uptime";
45346
- const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;
45347
45639
  const chartKey = React141.useMemo(() => `${lineId}-${month}-${year}-${selectedShiftId}-${rangeStart}-${rangeEnd}`, [lineId, month, year, selectedShiftId, rangeStart, rangeEnd]);
45348
45640
  const monthBounds = React141.useMemo(() => getMonthKeyBounds(year, month), [year, month]);
45349
45641
  const normalizedRange = React141.useMemo(() => {
@@ -45391,20 +45683,20 @@ var LineMonthlyHistory = ({
45391
45683
  { efficiency: 0, underperforming: 0, totalWorkspaces: 0, count: 0 }
45392
45684
  );
45393
45685
  const avgEfficiency = averages.count > 0 ? averages.efficiency / averages.count : 0;
45394
- const { underperformingDays, totalDays } = (analysisMonthlyData || []).reduce(
45686
+ const outputAverages = (analysisMonthlyData || []).reduce(
45395
45687
  (acc, day) => {
45396
45688
  const shiftData = getShiftData2(day, selectedShiftId);
45397
45689
  if (!shiftData || !hasRealData(shiftData)) {
45398
45690
  return acc;
45399
45691
  }
45400
- const status = getEfficiencyColor(shiftData.avg_efficiency || 0, effectiveLegend);
45401
45692
  return {
45402
- totalDays: acc.totalDays + 1,
45403
- underperformingDays: acc.underperformingDays + (status === "red" ? 1 : 0)
45693
+ output: acc.output + (shiftData.output || 0),
45694
+ count: acc.count + 1
45404
45695
  };
45405
45696
  },
45406
- { underperformingDays: 0, totalDays: 0 }
45697
+ { output: 0, count: 0 }
45407
45698
  );
45699
+ const avgOutput = outputAverages.count > 0 ? outputAverages.output / outputAverages.count : 0;
45408
45700
  const uptimeSummary = React141.useMemo(() => {
45409
45701
  if (!isUptimeMode) return null;
45410
45702
  const validDays = (analysisMonthlyData || []).map((day) => getShiftData2(day, selectedShiftId)).filter((shiftData) => shiftData && hasRealData(shiftData)).map((shiftData) => ({ shiftData, totals: getUptimeTotals(shiftData) }));
@@ -45437,6 +45729,10 @@ var LineMonthlyHistory = ({
45437
45729
  const efficiencyImproved = efficiencyDelta >= 0;
45438
45730
  const EfficiencyTrendIcon = efficiencyImproved ? lucideReact.ArrowUp : lucideReact.ArrowDown;
45439
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)}%`;
45440
45736
  const utilizationDelta = efficiencyDelta;
45441
45737
  const utilizationImproved = utilizationDelta >= 0;
45442
45738
  const UtilizationTrendIcon = utilizationImproved ? lucideReact.ArrowUp : lucideReact.ArrowDown;
@@ -45721,12 +46017,17 @@ var LineMonthlyHistory = ({
45721
46017
  ] })
45722
46018
  ] }),
45723
46019
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-lg shadow-sm border border-gray-100 p-4 flex flex-col justify-between", children: [
45724
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-bold text-gray-700 mb-2", children: "No. of underperforming days" }),
45725
- /* @__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: [
45726
- underperformingDays,
45727
- "/",
45728
- totalDays
45729
- ] }) })
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
+ ] })
45730
46031
  ] })
45731
46032
  ] }),
45732
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: [
@@ -46245,11 +46546,6 @@ var LineMonthlyPdfGenerator = ({
46245
46546
  doc.text("Working Days:", 25, kpiStartY + kpiSpacing * 2);
46246
46547
  doc.setFont("helvetica", "bold");
46247
46548
  doc.text(`${outputMetrics.totalDays} days`, 120, kpiStartY + kpiSpacing * 2);
46248
- createKPIBox(kpiStartY + kpiSpacing * 3);
46249
- doc.setFont("helvetica", "normal");
46250
- doc.text("Underperforming Days:", 25, kpiStartY + kpiSpacing * 3);
46251
- doc.setFont("helvetica", "bold");
46252
- doc.text(`${outputMetrics.underperformingDays} of ${outputMetrics.totalDays}`, 120, kpiStartY + kpiSpacing * 3);
46253
46549
  }
46254
46550
  } else {
46255
46551
  doc.setFontSize(12);
@@ -47827,7 +48123,8 @@ var WorkspaceMonthlyHistory = ({
47827
48123
  availableShifts,
47828
48124
  monthlyDataLoading = false,
47829
48125
  className = "",
47830
- trendSummary
48126
+ trendSummary,
48127
+ isAssemblyWorkspace = false
47831
48128
  }) => {
47832
48129
  const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;
47833
48130
  const isUptimeMode = monitoringMode === "uptime";
@@ -48066,12 +48363,8 @@ var WorkspaceMonthlyHistory = ({
48066
48363
  }, [analysisMonthlyData, selectedShiftId, isUptimeMode, shiftWorkSeconds]);
48067
48364
  const efficiencyDelta = trendSummary?.avg_efficiency?.delta_pp ?? 0;
48068
48365
  const efficiencyImproved = efficiencyDelta >= 0;
48069
- const outputDeltaRaw = trendSummary?.avg_daily_output?.delta_pp ?? 0;
48070
48366
  const cycleDeltaRaw = trendSummary?.avg_cycle_time?.delta_seconds ?? 0;
48071
- const outputPrev = trendSummary?.avg_daily_output?.previous ?? 0;
48072
48367
  const cyclePrev = trendSummary?.avg_cycle_time?.previous ?? 0;
48073
- const outputDelta = outputPrev ? outputDeltaRaw / outputPrev * 100 : 0;
48074
- const outputImproved = outputDelta >= 0;
48075
48368
  const cycleDelta = cyclePrev ? cycleDeltaRaw / cyclePrev * 100 : 0;
48076
48369
  const cycleWorsened = cycleDelta > 0;
48077
48370
  const utilizationDelta = efficiencyDelta;
@@ -48151,7 +48444,7 @@ var WorkspaceMonthlyHistory = ({
48151
48444
  shift.id
48152
48445
  )) }) }),
48153
48446
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-2 gap-6", children: [
48154
- /* @__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: [
48155
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)) }),
48156
48449
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-7 gap-2 mt-6", children: calendarData.calendar.map((day, index) => {
48157
48450
  const dayNumber = index >= calendarData.startOffset ? index - calendarData.startOffset + 1 : null;
@@ -48239,8 +48532,32 @@ var WorkspaceMonthlyHistory = ({
48239
48532
  ) }, index);
48240
48533
  }) })
48241
48534
  ] }),
48242
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
48243
- /* @__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: [
48244
48561
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-lg shadow-sm border border-gray-100 p-4 flex flex-col justify-between", children: [
48245
48562
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-semibold text-gray-600 mb-1", children: isUptimeMode ? "Avg Utilization" : "Avg Efficiency" }),
48246
48563
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 flex-nowrap", children: [
@@ -48254,16 +48571,13 @@ var WorkspaceMonthlyHistory = ({
48254
48571
  ] })
48255
48572
  ] })
48256
48573
  ] }),
48257
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-lg shadow-sm border border-gray-100 p-4 flex flex-col justify-between", children: [
48258
- /* @__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" }),
48259
48576
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 flex-nowrap", children: [
48260
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-2xl font-bold text-gray-900", children: isUptimeMode ? formatIdleTime(metrics2?.avgIdleTime ?? 0) : metrics2?.avgDailyOutput?.toLocaleString?.() ?? 0 }),
48261
- 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: [
48262
48579
  idleImproved ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDown, { className: "w-3 h-3" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "w-3 h-3" }),
48263
48580
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: idleTrendText })
48264
- ] }) : /* @__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: [
48265
- outputImproved ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "w-3 h-3" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDown, { className: "w-3 h-3" }),
48266
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: `${Math.abs(outputDelta).toFixed(1)}% vs last month` })
48267
48581
  ] })
48268
48582
  ] })
48269
48583
  ] }),
@@ -48285,9 +48599,9 @@ var WorkspaceMonthlyHistory = ({
48285
48599
  ] })
48286
48600
  ] }),
48287
48601
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `grid grid-cols-1 ${idleTimeVlmEnabled ? "sm:grid-cols-2" : "sm:grid-cols-1"} gap-4`, children: [
48288
- /* @__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: [
48289
48603
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-bold text-gray-700 mb-3 text-left", children: isUptimeMode ? "Utilization" : "Time Utilization" }),
48290
- 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: [
48291
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: [
48292
48606
  /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsx(recharts.PieChart, { children: /* @__PURE__ */ jsxRuntime.jsx(
48293
48607
  recharts.Pie,
@@ -48360,7 +48674,7 @@ var WorkspaceMonthlyHistory = ({
48360
48674
  ] }) })
48361
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" }) })
48362
48676
  ] }),
48363
- 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: [
48364
48678
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-bold text-gray-700 mb-3 text-left", children: "Idle Time Breakdown" }),
48365
48679
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[160px]", children: /* @__PURE__ */ jsxRuntime.jsx(
48366
48680
  IdleTimeReasonChart,
@@ -48372,7 +48686,7 @@ var WorkspaceMonthlyHistory = ({
48372
48686
  ) })
48373
48687
  ] })
48374
48688
  ] }),
48375
- /* @__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: [
48376
48690
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-bold text-gray-700 mb-3 text-left", children: isUptimeMode ? "Daily Utilization" : "Daily Output" }),
48377
48691
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: { height: "220px" }, children: /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(
48378
48692
  recharts.BarChart,
@@ -48570,7 +48884,7 @@ var WorkspaceWhatsAppShareButton = ({
48570
48884
  }
48571
48885
  );
48572
48886
  };
48573
- var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiencyLegend }) => {
48887
+ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiencyLegend, hourlyCycleTimes }) => {
48574
48888
  const [isGenerating, setIsGenerating] = React141.useState(false);
48575
48889
  const entityConfig = useEntityConfig();
48576
48890
  const effectiveLegend = efficiencyLegend || DEFAULT_EFFICIENCY_LEGEND;
@@ -48578,6 +48892,7 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
48578
48892
  setIsGenerating(true);
48579
48893
  try {
48580
48894
  const isUptimeMode = workspace.monitoring_mode === "uptime";
48895
+ const isAssemblyCycleMode = !isUptimeMode && workspace.line_assembly_enabled === true && workspace.action_type === "assembly";
48581
48896
  const shiftMinutes = getShiftDurationMinutes(workspace.shift_start, workspace.shift_end);
48582
48897
  const shiftSeconds = shiftMinutes ? shiftMinutes * 60 : 0;
48583
48898
  const idleSeconds = Math.max(workspace.idle_time || 0, 0);
@@ -48638,9 +48953,38 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
48638
48953
  minute: "2-digit",
48639
48954
  hour12: true
48640
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;
48641
48985
  doc.setFontSize(12);
48642
48986
  doc.setTextColor(80, 80, 80);
48643
- doc.text(`Report Period: ${shiftStartTime} - ${currentTime}`, 20, 79);
48987
+ doc.text(`Report Period: ${shiftStartTime} - ${reportPeriodEndTime}`, 20, 79);
48644
48988
  doc.setTextColor(0, 0, 0);
48645
48989
  doc.setDrawColor(180, 180, 180);
48646
48990
  doc.setLineWidth(0.8);
@@ -48654,10 +48998,8 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
48654
48998
  };
48655
48999
  const perfOverviewStartY = 93;
48656
49000
  const hasIdleTimeReason = idleTimeReasons && idleTimeReasons.length > 0;
48657
- let perfOverviewHeight = hasIdleTimeReason ? 80 : 70;
48658
- if (isUptimeMode) {
48659
- perfOverviewHeight = hasIdleTimeReason ? 70 : 60;
48660
- }
49001
+ const perfOverviewRows = isUptimeMode ? 3 : isAssemblyCycleMode ? 2 : 4;
49002
+ const perfOverviewHeight = 30 + perfOverviewRows * 10 + (hasIdleTimeReason ? 10 : 0);
48661
49003
  doc.setFillColor(245, 245, 245);
48662
49004
  doc.roundedRect(15, perfOverviewStartY, 180, perfOverviewHeight, 3, 3, "F");
48663
49005
  doc.setFontSize(18);
@@ -48685,6 +49027,19 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
48685
49027
  doc.text("Stoppages:", 25, kpiStartY + kpiSpacing * 2);
48686
49028
  doc.setFont("helvetica", "bold");
48687
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);
48688
49043
  } else {
48689
49044
  createKPIBox(kpiStartY);
48690
49045
  doc.setFontSize(11);
@@ -48721,17 +49076,11 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
48721
49076
  const reasonText = `${reasonName} (${topReason.value.toFixed(1)}%)`;
48722
49077
  doc.text(reasonText, 120, reasonY);
48723
49078
  }
48724
- let separatorBeforeHourlyY = hasIdleTimeReason ? 183 : 173;
48725
- if (isUptimeMode) {
48726
- separatorBeforeHourlyY -= 10;
48727
- }
49079
+ const separatorBeforeHourlyY = perfOverviewStartY + perfOverviewHeight + 10;
48728
49080
  doc.setDrawColor(180, 180, 180);
48729
49081
  doc.setLineWidth(0.8);
48730
49082
  doc.line(20, separatorBeforeHourlyY, 190, separatorBeforeHourlyY);
48731
- let hourlyPerfStartY = hasIdleTimeReason ? 188 : 178;
48732
- if (isUptimeMode) {
48733
- hourlyPerfStartY -= 10;
48734
- }
49083
+ const hourlyPerfStartY = separatorBeforeHourlyY + 5;
48735
49084
  const uptimeSeries = isUptimeMode ? buildUptimeSeries({
48736
49085
  idleTimeHourly: workspace.idle_time_hourly,
48737
49086
  shiftStart: workspace.shift_start,
@@ -48749,8 +49098,9 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
48749
49098
  const uptimePercent = total > 0 ? Math.round(activeMinutes / total * 100) : 0;
48750
49099
  return { activeMinutes, idleMinutes, uptimePercent };
48751
49100
  }) : [];
48752
- 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 || [];
48753
49102
  const hourlyTarget = workspace.pph_threshold;
49103
+ const cycleTarget = workspace.ideal_cycle_time || 0;
48754
49104
  const pageHeight = doc.internal.pageSize.height;
48755
49105
  const maxContentY = pageHeight - 15;
48756
49106
  const baseTableStartY = hourlyPerfStartY + 31;
@@ -48775,12 +49125,16 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
48775
49125
  doc.setFont("helvetica", "bold");
48776
49126
  doc.setTextColor(40, 40, 40);
48777
49127
  const hourlyTitleY = hourlyPerfStartY + 10;
48778
- 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
+ );
48779
49133
  doc.setTextColor(0, 0, 0);
48780
49134
  const headerY = titleFontSize === 16 ? hourlyPerfStartY + 18 : hourlyPerfStartY + 20;
48781
49135
  const gridTopY = headerY - 5;
48782
49136
  const headerBottomY = gridTopY + 8;
48783
- 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];
48784
49138
  const totalRows = hourlyData.length;
48785
49139
  const gridBottomY = headerBottomY + totalRows * rowHeight;
48786
49140
  const tableHeight = gridBottomY - gridTopY;
@@ -48801,6 +49155,10 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
48801
49155
  doc.text("Time Range", 25, headerTextY);
48802
49156
  if (isUptimeMode) {
48803
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);
48804
49162
  } else {
48805
49163
  doc.text("Output", 75, headerTextY);
48806
49164
  doc.text("Target", 105, headerTextY);
@@ -48849,6 +49207,25 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
48849
49207
  if (isUptimeMode) {
48850
49208
  const utilizationStr = dataCollected ? `${uptimePercent}%` : "TBD";
48851
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);
48852
49229
  } else {
48853
49230
  doc.text(outputStr, 75, yPos);
48854
49231
  doc.text(targetStr, 105, yPos);
@@ -48920,7 +49297,8 @@ var WorkspaceMonthlyPdfGenerator = ({
48920
49297
  shiftConfig,
48921
49298
  efficiencyLegend,
48922
49299
  className,
48923
- compact = false
49300
+ compact = false,
49301
+ isAssemblyWorkspace = false
48924
49302
  }) => {
48925
49303
  const [isGenerating, setIsGenerating] = React141.useState(false);
48926
49304
  const effectiveLegend = efficiencyLegend || DEFAULT_EFFICIENCY_LEGEND;
@@ -49043,6 +49421,7 @@ var WorkspaceMonthlyPdfGenerator = ({
49043
49421
  avgOutput: filteredShifts.reduce((sum, shift) => sum + shift.output, 0) / filteredShifts.length,
49044
49422
  avgCycleTime: filteredShifts.reduce((sum, shift) => sum + shift.cycleTime, 0) / filteredShifts.length,
49045
49423
  avgPph: filteredShifts.reduce((sum, shift) => sum + shift.pph, 0) / filteredShifts.length,
49424
+ avgIdleTime: filteredShifts.reduce((sum, shift) => sum + shift.idleTime, 0) / filteredShifts.length,
49046
49425
  totalDays: filteredShifts.length,
49047
49426
  underperformingDays: filteredShifts.filter((shift) => shift.efficiency < effectiveLegend.green_min).length
49048
49427
  } : null;
@@ -49054,7 +49433,8 @@ var WorkspaceMonthlyPdfGenerator = ({
49054
49433
  doc.roundedRect(22, y - 7, 165, 12, 2, 2, "S");
49055
49434
  };
49056
49435
  doc.setFillColor(245, 245, 245);
49057
- 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");
49058
49438
  doc.setFontSize(18);
49059
49439
  doc.setFont("helvetica", "bold");
49060
49440
  doc.setTextColor(40, 40, 40);
@@ -49093,32 +49473,41 @@ var WorkspaceMonthlyPdfGenerator = ({
49093
49473
  doc.text(`${uptimeMetrics.underperformingDays} of ${uptimeMetrics.totalDays}`, 120, kpiStartY + kpiSpacing * 4);
49094
49474
  } else {
49095
49475
  const outputMetrics = monthlyMetrics;
49096
- createKPIBox(kpiStartY);
49097
- doc.setFontSize(11);
49098
- doc.setFont("helvetica", "normal");
49099
- doc.text("Average Efficiency:", 25, kpiStartY);
49100
- doc.setFont("helvetica", "bold");
49101
- doc.text(`${outputMetrics.avgEfficiency.toFixed(1)}% (Target: ${Math.round(effectiveLegend.green_min)}%)`, 120, kpiStartY);
49102
- createKPIBox(kpiStartY + kpiSpacing);
49103
- doc.setFont("helvetica", "normal");
49104
- doc.text("Average Output:", 25, kpiStartY + kpiSpacing);
49105
- doc.setFont("helvetica", "bold");
49106
- doc.text(`${Math.round(outputMetrics.avgOutput)} pieces`, 120, kpiStartY + kpiSpacing);
49107
- createKPIBox(kpiStartY + kpiSpacing * 2);
49108
- doc.setFont("helvetica", "normal");
49109
- doc.text("Average PPH:", 25, kpiStartY + kpiSpacing * 2);
49110
- doc.setFont("helvetica", "bold");
49111
- doc.text(`${outputMetrics.avgPph.toFixed(1)} per hour`, 120, kpiStartY + kpiSpacing * 2);
49112
- createKPIBox(kpiStartY + kpiSpacing * 3);
49113
- doc.setFont("helvetica", "normal");
49114
- doc.text("Working Days:", 25, kpiStartY + kpiSpacing * 3);
49115
- doc.setFont("helvetica", "bold");
49116
- doc.text(`${outputMetrics.totalDays} days`, 120, kpiStartY + kpiSpacing * 3);
49117
- createKPIBox(kpiStartY + kpiSpacing * 4);
49118
- doc.setFont("helvetica", "normal");
49119
- doc.text("Underperforming Days:", 25, kpiStartY + kpiSpacing * 4);
49120
- doc.setFont("helvetica", "bold");
49121
- 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
+ }
49122
49511
  }
49123
49512
  } else {
49124
49513
  doc.setFontSize(12);
@@ -49129,29 +49518,33 @@ var WorkspaceMonthlyPdfGenerator = ({
49129
49518
  }
49130
49519
  doc.setDrawColor(180, 180, 180);
49131
49520
  doc.setLineWidth(0.8);
49132
- doc.line(20, 180, 190, 180);
49521
+ const separatorY = isAssemblyWorkspaceAndNotUptime ? 150 : 180;
49522
+ doc.line(20, separatorY, 190, separatorY);
49133
49523
  doc.setFillColor(245, 245, 245);
49134
- 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");
49135
49526
  doc.setFontSize(18);
49136
49527
  doc.setFont("helvetica", "bold");
49137
49528
  doc.setTextColor(40, 40, 40);
49138
- doc.text(isUptimeMode ? "Daily Utilization Summary" : "Daily Performance Summary", 20, 195);
49529
+ doc.text(isUptimeMode ? "Daily Utilization Summary" : "Daily Performance Summary", 20, dailySectionY + 10);
49139
49530
  doc.setTextColor(0, 0, 0);
49140
49531
  if (validDays.length > 0) {
49141
49532
  doc.setFontSize(10);
49142
49533
  doc.setFont("helvetica", "bold");
49143
49534
  doc.setFillColor(240, 240, 240);
49144
- doc.roundedRect(20, 200, 170, 7, 1, 1, "F");
49145
- doc.text("Date", 25, 205);
49146
- doc.text(isUptimeMode ? "Productive" : "Actual", 60, 205);
49147
- doc.text(isUptimeMode ? "Idle" : "Standard", 95, 205);
49148
- doc.text(isUptimeMode ? "Utilization" : "Efficiency", 135, 205);
49149
- 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);
49150
49543
  doc.setLineWidth(0.2);
49151
49544
  doc.setDrawColor(220, 220, 220);
49152
- doc.line(20, 208, 190, 208);
49545
+ doc.line(20, textY + 3, 190, textY + 3);
49153
49546
  doc.setFont("helvetica", "normal");
49154
- let yPos = 215;
49547
+ let yPos = textY + 10;
49155
49548
  const recentDays = validDays.slice(-10).reverse();
49156
49549
  recentDays.forEach((dayData, index) => {
49157
49550
  if (yPos > 260) return;
@@ -49185,8 +49578,13 @@ var WorkspaceMonthlyPdfGenerator = ({
49185
49578
  }
49186
49579
  doc.setTextColor(0, 0, 0);
49187
49580
  } else {
49188
- doc.text(`${shift.output}`, 60, yPos);
49189
- 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
+ }
49190
49588
  doc.text(`${shift.efficiency.toFixed(1)}%`, 135, yPos);
49191
49589
  if (shift.efficiency >= effectiveLegend.green_min) {
49192
49590
  doc.setTextColor(0, 171, 69);
@@ -49201,12 +49599,12 @@ var WorkspaceMonthlyPdfGenerator = ({
49201
49599
  });
49202
49600
  doc.setLineWidth(0.2);
49203
49601
  doc.setDrawColor(220, 220, 220);
49204
- doc.roundedRect(20, 200, 170, yPos - 200 - 3, 1, 1, "S");
49602
+ doc.roundedRect(20, tableHeaderY, 170, yPos - tableHeaderY - 3, 1, 1, "S");
49205
49603
  } else {
49206
49604
  doc.setFontSize(12);
49207
49605
  doc.setFont("helvetica", "normal");
49208
49606
  doc.setTextColor(100, 100, 100);
49209
- doc.text("No daily data available for this month", 25, 215);
49607
+ doc.text("No daily data available for this month", 25, dailySectionY + 30);
49210
49608
  doc.setTextColor(0, 0, 0);
49211
49609
  }
49212
49610
  doc.setFontSize(9);
@@ -49239,46 +49637,64 @@ var WorkspaceCycleTimeMetricCards = ({
49239
49637
  workspace,
49240
49638
  className,
49241
49639
  legend,
49242
- layout: layout2 = "grid"
49640
+ layout: layout2 = "grid",
49641
+ isAssemblyWorkspace = false,
49642
+ idleTimeData
49243
49643
  }) => {
49244
49644
  const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;
49245
49645
  const efficiencyValue = workspace.avg_efficiency || 0;
49246
49646
  const efficiencyTarget = effectiveLegend.green_min;
49247
49647
  const efficiencyColor = getEfficiencyHexColor(efficiencyValue, effectiveLegend);
49248
- const hideEfficiencyCard = shouldHideWorkspaceEfficiencyCard(workspace);
49249
- 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 || ""}`;
49250
49654
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: containerClassName, children: [
49251
- !hideEfficiencyCard && /* @__PURE__ */ jsxRuntime.jsxs(Card2, { children: [
49252
- /* @__PURE__ */ jsxRuntime.jsx(CardHeader2, { className: "pb-2 flex-none", children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle2, { className: "text-lg text-center", children: "Efficiency" }) }),
49253
- /* @__PURE__ */ jsxRuntime.jsx(CardContent2, { className: "flex-1 flex items-center justify-center py-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
49254
- /* @__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: [
49255
49659
  efficiencyValue.toFixed(1),
49256
49660
  "%"
49257
49661
  ] }),
49258
- /* @__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: [
49259
49663
  "Target: ",
49260
49664
  Math.round(efficiencyTarget),
49261
49665
  "%"
49262
49666
  ] })
49263
- ] }) })
49667
+ ] })
49264
49668
  ] }),
49265
- /* @__PURE__ */ jsxRuntime.jsxs(Card2, { children: [
49266
- /* @__PURE__ */ jsxRuntime.jsx(CardHeader2, { className: "pb-2 flex-none", children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle2, { className: "text-lg text-center", children: "Cycle Time (s)" }) }),
49267
- /* @__PURE__ */ jsxRuntime.jsx(CardContent2, { className: "flex-1 flex items-center justify-center py-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
49268
- /* @__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) }),
49269
- /* @__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: [
49270
49674
  "Standard: ",
49271
49675
  workspace.ideal_cycle_time?.toFixed(1) || 0,
49272
49676
  "s"
49273
49677
  ] })
49274
- ] }) })
49678
+ ] })
49275
49679
  ] }),
49276
- /* @__PURE__ */ jsxRuntime.jsxs(Card2, { children: [
49277
- /* @__PURE__ */ jsxRuntime.jsx(CardHeader2, { className: "pb-2 flex-none", children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle2, { className: "text-lg text-center", children: "Idle Time" }) }),
49278
- /* @__PURE__ */ jsxRuntime.jsx(CardContent2, { className: "flex-1 flex items-center justify-center py-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
49279
- /* @__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) }),
49280
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500 mt-2", children: "Total idle time" })
49281
- ] }) })
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
+ ) })
49282
49698
  ] })
49283
49699
  ] });
49284
49700
  };
@@ -49416,13 +49832,13 @@ var getWorkspaceStyles = (position, isPlaceholder = false) => {
49416
49832
  ${isPlaceholder ? "cursor-default" : ""}`;
49417
49833
  };
49418
49834
  var formatPercentRange = (min, max) => {
49419
- const format8 = (value) => Number.isInteger(value) ? `${value}` : value.toFixed(1);
49835
+ const format9 = (value) => Number.isInteger(value) ? `${value}` : value.toFixed(1);
49420
49836
  if (min >= 100 || max >= 100) {
49421
- return `${format8(min)}+%`;
49837
+ return `${format9(min)}+%`;
49422
49838
  }
49423
- return `${format8(min)}-${format8(max)}%`;
49839
+ return `${format9(min)}-${format9(max)}%`;
49424
49840
  };
49425
- var Legend6 = ({
49841
+ var Legend5 = ({
49426
49842
  useBottleneckLabel = false,
49427
49843
  legend,
49428
49844
  metricLabel = "Efficiency"
@@ -49608,7 +50024,7 @@ var WorkspaceGrid = React141__namespace.default.memo(({
49608
50024
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `relative w-full h-full overflow-hidden ${className}`, children: [
49609
50025
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute top-0 left-2 sm:left-4 right-2 sm:right-8 z-20", children: [
49610
50026
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-row items-center justify-between py-1 sm:py-1.5 gap-2", children: [
49611
- /* @__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 }) }),
49612
50028
  mapViewEnabled && /* @__PURE__ */ jsxRuntime.jsx(
49613
50029
  "button",
49614
50030
  {
@@ -49625,7 +50041,7 @@ var WorkspaceGrid = React141__namespace.default.memo(({
49625
50041
  }
49626
50042
  )
49627
50043
  ] }),
49628
- /* @__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 }) })
49629
50045
  ] }),
49630
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(
49631
50047
  motion.div,
@@ -50136,7 +50552,7 @@ var KPISection = React141.memo(({
50136
50552
  value: showSkeleton ? "" : kpis.efficiency.value,
50137
50553
  change: effChange,
50138
50554
  trend: effTrend,
50139
- trendLabel: "today",
50555
+ trendLabel: "vs yesterday",
50140
50556
  trendMode: "pill",
50141
50557
  suffix: "%",
50142
50558
  showZeroChange: true,
@@ -52372,7 +52788,7 @@ var SideNavBar = React141.memo(({
52372
52788
  setIsAlertsOpen(willOpen);
52373
52789
  setIsSettingsOpen(false);
52374
52790
  if (willOpen) {
52375
- trackCoreEvent("Alerts Page Clicked", { source: "side_nav" });
52791
+ trackCoreEvent("Alerts page clicked", { source: "side_nav" });
52376
52792
  void refreshAlertsSummary();
52377
52793
  }
52378
52794
  },
@@ -52527,7 +52943,7 @@ var SideNavBar = React141.memo(({
52527
52943
  {
52528
52944
  onClick: () => {
52529
52945
  setIsAlertsOpen(true);
52530
- trackCoreEvent("Alerts Page Clicked", { source: "side_nav_mobile" });
52946
+ trackCoreEvent("Alerts page clicked", { source: "side_nav_mobile" });
52531
52947
  void refreshAlertsSummary();
52532
52948
  onMobileMenuClose?.();
52533
52949
  },
@@ -59737,7 +60153,8 @@ var MonthlyRangeFilter = ({
59737
60153
  onMonthNavigate,
59738
60154
  className,
59739
60155
  variant = "default",
59740
- showLabel = true
60156
+ showLabel = true,
60157
+ singleDateOnly = false
59741
60158
  }) => {
59742
60159
  const todayKey = React141.useMemo(
59743
60160
  () => dateFnsTz.formatInTimeZone(/* @__PURE__ */ new Date(), timezone || "UTC", "yyyy-MM-dd"),
@@ -59825,6 +60242,12 @@ var MonthlyRangeFilter = ({
59825
60242
  }
59826
60243
  setActivePreset("Custom");
59827
60244
  setPendingChangeMeta({ source: "custom" });
60245
+ if (singleDateOnly) {
60246
+ setRangeStart(day);
60247
+ setRangeEnd(day);
60248
+ setSelecting(false);
60249
+ return;
60250
+ }
59828
60251
  if (!selecting || !rangeStart) {
59829
60252
  setRangeStart(day);
59830
60253
  setRangeEnd(null);
@@ -59859,6 +60282,13 @@ var MonthlyRangeFilter = ({
59859
60282
  };
59860
60283
  const handleApply = () => {
59861
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
+ }
59862
60292
  const boundedStart = dateFns.startOfDay(rangeStart) > dateFns.startOfDay(today) ? dateFns.startOfDay(today) : dateFns.startOfDay(rangeStart);
59863
60293
  const candidateEnd = rangeEnd || rangeStart;
59864
60294
  const boundedEnd = dateFns.startOfDay(candidateEnd) > dateFns.startOfDay(today) ? dateFns.startOfDay(today) : dateFns.startOfDay(candidateEnd);
@@ -59953,7 +60383,7 @@ var MonthlyRangeFilter = ({
59953
60383
  "overflow-hidden bg-white animate-in fade-in zoom-in-95 duration-200 flex",
59954
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"
59955
60385
  ), children: [
59956
- /* @__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: [
59957
60387
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
59958
60388
  presets.map((preset) => /* @__PURE__ */ jsxRuntime.jsx(
59959
60389
  "button",
@@ -59962,7 +60392,7 @@ var MonthlyRangeFilter = ({
59962
60392
  onClick: () => handlePresetClick(preset),
59963
60393
  className: clsx(
59964
60394
  "w-full text-left px-3 py-2 text-sm rounded-lg transition-all duration-200",
59965
- 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"
59966
60396
  ),
59967
60397
  children: preset.label
59968
60398
  },
@@ -59975,7 +60405,7 @@ var MonthlyRangeFilter = ({
59975
60405
  onClick: () => setActivePreset("Custom"),
59976
60406
  className: clsx(
59977
60407
  "w-full text-left px-3 py-2 text-sm rounded-lg transition-all duration-200",
59978
- 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"
59979
60409
  ),
59980
60410
  children: "Custom"
59981
60411
  }
@@ -59997,7 +60427,7 @@ var MonthlyRangeFilter = ({
59997
60427
  type: "button",
59998
60428
  onClick: handleApply,
59999
60429
  disabled: !rangeStart,
60000
- 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",
60001
60431
  children: "Apply"
60002
60432
  }
60003
60433
  )
@@ -60047,9 +60477,9 @@ var MonthlyRangeFilter = ({
60047
60477
  const dayNum = day.getDate();
60048
60478
  const isFutureDay = dateFns.startOfDay(day) > dateFns.startOfDay(today);
60049
60479
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
60050
- inRange && !isStart && !isEnd && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 -inset-x-0.5 bg-[#EEF2FF] z-0" }),
60051
- inRange && isStart && !isSingleDaySelection && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 right-0 left-1/2 bg-[#EEF2FF] z-0" }),
60052
- 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" }),
60053
60483
  /* @__PURE__ */ jsxRuntime.jsx(
60054
60484
  "button",
60055
60485
  {
@@ -60058,19 +60488,47 @@ var MonthlyRangeFilter = ({
60058
60488
  disabled: isFutureDay,
60059
60489
  className: clsx(
60060
60490
  "h-10 w-full flex items-center justify-center text-sm font-semibold transition-all duration-150 relative z-10",
60061
- isFutureDay && "text-gray-200 cursor-not-allowed",
60491
+ // Future day NOT in range
60492
+ isFutureDay && !inRange && "text-gray-300 cursor-not-allowed",
60062
60493
  // Not in range
60063
- !isFutureDay && !inRange && "text-gray-700 hover:bg-gray-50 rounded-lg",
60494
+ !isFutureDay && !inRange && "text-gray-700 hover:bg-gray-100 rounded-lg",
60064
60495
  // Middle of range
60065
- inRange && !isStart && !isEnd && "text-[#4F46E5]",
60496
+ inRange && !isStart && !isEnd && clsx(
60497
+ "text-blue-600",
60498
+ isFutureDay && "cursor-not-allowed opacity-60"
60499
+ ),
60066
60500
  // Start/End of range or Single selection
60067
- (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
+ )
60068
60505
  ),
60069
60506
  children: dayNum
60070
60507
  }
60071
60508
  )
60072
60509
  ] }, day.toISOString());
60073
- }) })
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
+ ] })
60074
60532
  ] })
60075
60533
  ] })
60076
60534
  ] });
@@ -61847,7 +62305,36 @@ var getMonthDateInfo = (timezone) => {
61847
62305
  const monthEndDate = dateFnsTz.fromZonedTime(`${monthEndKey}T23:59:59`, timezone);
61848
62306
  return { startDate, endDate, monthEndDate };
61849
62307
  };
61850
- 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 }) => {
61851
62338
  const [time2, setTime] = React141.useState("");
61852
62339
  const hasFinishedRef = React141.useRef(false);
61853
62340
  React141.useEffect(() => {
@@ -61869,7 +62356,7 @@ var LeaderboardCountdown = ({ targetDate, format: format8, finishedLabel = "Fini
61869
62356
  }
61870
62357
  return;
61871
62358
  }
61872
- if (format8 === "days") {
62359
+ if (format9 === "days") {
61873
62360
  const days = Math.floor(diff / (1e3 * 60 * 60 * 24));
61874
62361
  const hours = Math.floor(diff % (1e3 * 60 * 60 * 24) / (1e3 * 60 * 60));
61875
62362
  setTime(`${days} days ${hours} hours`);
@@ -61884,7 +62371,7 @@ var LeaderboardCountdown = ({ targetDate, format: format8, finishedLabel = "Fini
61884
62371
  tick();
61885
62372
  const interval = setInterval(tick, 1e3);
61886
62373
  return () => clearInterval(interval);
61887
- }, [targetDate, format8, finishedLabel, placeholder, onFinished]);
62374
+ }, [targetDate, format9, finishedLabel, placeholder, onFinished]);
61888
62375
  return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: time2 });
61889
62376
  };
61890
62377
  var LinesLeaderboard = ({
@@ -61902,7 +62389,9 @@ var LinesLeaderboard = ({
61902
62389
  shiftEndDate,
61903
62390
  monthEndDate,
61904
62391
  viewType,
61905
- setViewType
62392
+ setViewType,
62393
+ timezone: _timezone,
62394
+ isHistoricalDaily
61906
62395
  }) => {
61907
62396
  const formatEfficiency = (value) => typeof value === "number" && Number.isFinite(value) ? `${value.toFixed(1)}%` : "--";
61908
62397
  const assignedLineIdSet = React141__namespace.default.useMemo(
@@ -61995,10 +62484,10 @@ var LinesLeaderboard = ({
61995
62484
  }
61996
62485
  }, [timeRange, leaderboardData, isLoadingToday, isLoadingMonthly]);
61997
62486
  const topThree = leaderboardData.slice(0, 3);
61998
- leaderboardData.slice(3);
61999
62487
  const countdownTarget = timeRange === "monthly" ? monthEndDate : shiftEndDate;
62000
62488
  const countdownFormat = timeRange === "monthly" ? "days" : "clock";
62001
62489
  const countdownFinishedLabel = timeRange === "monthly" ? "Finished" : "Shift Ended";
62490
+ const showCountdown = timeRange === "monthly" || !isHistoricalDaily;
62002
62491
  const handleCountdownFinished = React141__namespace.default.useCallback(() => {
62003
62492
  trackCoreEvent("Leaderboard Countdown Finished", {
62004
62493
  countdown_type: timeRange === "monthly" ? "month_end" : "shift_end",
@@ -62072,7 +62561,7 @@ var LinesLeaderboard = ({
62072
62561
  }
62073
62562
  )
62074
62563
  ] }),
62075
- /* @__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: [
62076
62565
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "w-4 h-4 text-orange-500" }),
62077
62566
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline gap-2", children: [
62078
62567
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-semibold text-gray-400 uppercase tracking-wider", children: "Ends in" }),
@@ -62193,7 +62682,7 @@ var LinesLeaderboard = ({
62193
62682
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-gray-900", children: item.supervisorName })
62194
62683
  ] }) }),
62195
62684
  /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-3 whitespace-nowrap text-sm text-gray-500", children: item.line.line_name }),
62196
- /* @__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) }) }) })
62197
62686
  ]
62198
62687
  },
62199
62688
  item.id
@@ -62367,6 +62856,9 @@ var KPIsOverviewView = ({
62367
62856
  const [activeTab, setActiveTab] = React141.useState("today");
62368
62857
  const [timeRange, setTimeRange] = React141.useState("today");
62369
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);
62370
62862
  const [loading, setLoading] = React141.useState(true);
62371
62863
  const [error, setError] = React141.useState(null);
62372
62864
  const [topPerformer, setTopPerformer] = React141.useState({
@@ -62388,13 +62880,6 @@ var KPIsOverviewView = ({
62388
62880
  const [monthlyError, setMonthlyError] = React141.useState(null);
62389
62881
  const dailyRequestKeyRef = React141.useRef(null);
62390
62882
  const monthlyRequestKeyRef = React141.useRef(null);
62391
- React141.useEffect(() => {
62392
- if (!router$1.isReady) return;
62393
- const tab = router$1.query.tab;
62394
- if (tab === "leaderboard") {
62395
- setActiveTab("leaderboard");
62396
- }
62397
- }, [router$1.isReady, router$1.query.tab]);
62398
62883
  const supabase = useSupabase();
62399
62884
  const { user } = useAuth();
62400
62885
  const dashboardConfig = useDashboardConfig();
@@ -62480,7 +62965,77 @@ var KPIsOverviewView = ({
62480
62965
  () => getShiftEndDate(currentShiftDetails, configuredTimezone),
62481
62966
  [currentShiftDetails, configuredTimezone]
62482
62967
  );
62483
- 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
+ ]);
62484
63039
  const factoryViewId = entityConfig.factoryViewId || "factory";
62485
63040
  const {
62486
63041
  lineMetrics,
@@ -62655,10 +63210,10 @@ var KPIsOverviewView = ({
62655
63210
  }, [supabase, resolvedCompanyId, leaderboardLinesForView, monthStartDate, monthEndDateKey, viewType]);
62656
63211
  const fetchDailyLeaderboard = React141.useCallback(async () => {
62657
63212
  if (!supabase || !resolvedCompanyId || leaderboardLinesForView.length === 0) return;
62658
- if (!currentShiftDate) return;
63213
+ if (!effectiveLeaderboardDate) return;
62659
63214
  const targetLineIds = leaderboardLinesForView.map((line) => line.id);
62660
63215
  const lineIdsKey = targetLineIds.slice().sort().join(",");
62661
- const requestKey = `${resolvedCompanyId}|${currentShiftDate}|${currentShiftId}|${lineIdsKey}`;
63216
+ const requestKey = `${resolvedCompanyId}|${effectiveLeaderboardDate}|${effectiveLeaderboardShiftId}|${lineIdsKey}`;
62662
63217
  if (dailyRequestKeyRef.current === requestKey) return;
62663
63218
  dailyRequestKeyRef.current = requestKey;
62664
63219
  setDailyLoading(true);
@@ -62666,8 +63221,8 @@ var KPIsOverviewView = ({
62666
63221
  try {
62667
63222
  const entries = await lineLeaderboardService.getDailyLineLeaderboard(supabase, {
62668
63223
  companyId: resolvedCompanyId,
62669
- date: currentShiftDate,
62670
- shiftId: currentShiftId,
63224
+ date: effectiveLeaderboardDate,
63225
+ shiftId: effectiveLeaderboardShiftId,
62671
63226
  lineIds: targetLineIds,
62672
63227
  lineMode: viewType === "machine" ? "uptime" : "output"
62673
63228
  });
@@ -62685,7 +63240,14 @@ var KPIsOverviewView = ({
62685
63240
  } finally {
62686
63241
  setDailyLoading(false);
62687
63242
  }
62688
- }, [supabase, resolvedCompanyId, leaderboardLinesForView, currentShiftDate, currentShiftId, viewType]);
63243
+ }, [
63244
+ supabase,
63245
+ resolvedCompanyId,
63246
+ leaderboardLinesForView,
63247
+ effectiveLeaderboardDate,
63248
+ effectiveLeaderboardShiftId,
63249
+ viewType
63250
+ ]);
62689
63251
  React141.useEffect(() => {
62690
63252
  if (activeTab !== "leaderboard") return;
62691
63253
  fetchMonthlyLeaderboard();
@@ -62774,6 +63336,12 @@ var KPIsOverviewView = ({
62774
63336
  trackProps.status = isEfficiencyOnTrack(kpis.efficiency?.value) ? "On Track" : "Behind";
62775
63337
  }
62776
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
+ }
62777
63345
  navigation.navigate(`/kpis/${line.id}`);
62778
63346
  };
62779
63347
  const handleBackClick = React141.useCallback(() => {
@@ -62799,21 +63367,24 @@ var KPIsOverviewView = ({
62799
63367
  });
62800
63368
  setActiveTab(newTab);
62801
63369
  }, [activeTab, leaderboardLines.length, lines.length]);
62802
- const formatLocalDate2 = (date) => {
62803
- const options = {
63370
+ const formatLocalDate2 = React141.useCallback((dateKey) => {
63371
+ return formatDateKey(dateKey, configuredTimezone, {
62804
63372
  year: "numeric",
62805
63373
  month: "long",
62806
63374
  day: "numeric"
62807
- };
62808
- return date.toLocaleDateString("en-US", options);
62809
- };
63375
+ });
63376
+ }, [configuredTimezone]);
62810
63377
  const getMonthRange = () => {
62811
- const now4 = /* @__PURE__ */ new Date();
62812
- const startOfMonth2 = new Date(now4.getFullYear(), now4.getMonth(), 1);
62813
- const endOfMonth2 = new Date(now4.getFullYear(), now4.getMonth() + 1, 0);
62814
- const startLabel = startOfMonth2.toLocaleDateString("en-US", { month: "short", day: "numeric" });
62815
- const endLabel = endOfMonth2.toLocaleDateString("en-US", { month: "short", day: "numeric" });
62816
- 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()}`;
62817
63388
  };
62818
63389
  const isMonthlyMode = activeTab === "leaderboard" && timeRange === "monthly";
62819
63390
  const isLeaderboardLoading = timeRange === "today" ? dailyLoading : monthlyLoading;
@@ -62822,8 +63393,13 @@ var KPIsOverviewView = ({
62822
63393
  const showTopPerformerImage = Boolean(topPerformer.imageUrl) && !topPerformerImageError;
62823
63394
  typeof topPerformer.efficiency === "number" && Number.isFinite(topPerformer.efficiency);
62824
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);
62825
63401
  const getShiftIcon = (shiftId) => {
62826
- const shiftNameLower = shiftName.toLowerCase();
63402
+ const shiftNameLower = getShiftNameById(shiftId, configuredTimezone, shiftConfig).toLowerCase();
62827
63403
  if (shiftNameLower.includes("day") || shiftNameLower.includes("morning") || shiftId === 0) {
62828
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" }) });
62829
63405
  }
@@ -62926,18 +63502,26 @@ var KPIsOverviewView = ({
62926
63502
  ),
62927
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: [
62928
63504
  /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-lg font-semibold text-gray-900", children: activeTab === "leaderboard" ? "Leaderboard" : "Overview" }),
62929
- /* @__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
+ )
62930
63511
  ] }) }),
62931
63512
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-12" })
62932
63513
  ] }),
62933
63514
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 flex flex-wrap items-center justify-center gap-2", children: [
62934
- /* @__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 }) }),
62935
63516
  !isMonthlyMode && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
62936
63517
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center gap-1 px-2.5 py-1 bg-gray-100 rounded-full", children: [
62937
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-700 scale-90", children: getShiftIcon(currentShiftDetails.shiftId) }),
62938
- /* @__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 })
62939
63520
  ] }),
62940
- /* @__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, {}) }) })
62941
63525
  ] })
62942
63526
  ] }),
62943
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: [
@@ -63038,7 +63622,12 @@ var KPIsOverviewView = ({
63038
63622
  ) }),
63039
63623
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
63040
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" }),
63041
- /* @__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
+ )
63042
63631
  ] }),
63043
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: [
63044
63633
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
@@ -63108,7 +63697,7 @@ var KPIsOverviewView = ({
63108
63697
  ] }) })
63109
63698
  ] }),
63110
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: [
63111
- !isMonthlyMode && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
63700
+ !isMonthlyMode && !showHistoricalLeaderboardHeader && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
63112
63701
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-blue-700", children: [
63113
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" }) }),
63114
63703
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-base font-semibold tabular-nums", children: /* @__PURE__ */ jsxRuntime.jsx(ISTTimer_default, {}) })
@@ -63117,16 +63706,23 @@ var KPIsOverviewView = ({
63117
63706
  ] }),
63118
63707
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-blue-600", children: [
63119
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" }) }),
63120
- /* @__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 })
63121
63710
  ] }),
63122
63711
  !isMonthlyMode && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
63123
63712
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-200" }),
63124
63713
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-blue-600", children: [
63125
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "opacity-70", children: getShiftIcon(currentShiftDetails.shiftId) }),
63714
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "opacity-70", children: getShiftIcon(headerShiftId) }),
63126
63715
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm font-semibold uppercase tracking-wider", children: [
63127
- shiftName,
63716
+ headerShiftName,
63128
63717
  " Shift"
63129
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
+ ] })
63130
63726
  ] })
63131
63727
  ] })
63132
63728
  ] }) }),
@@ -63149,19 +63745,52 @@ var KPIsOverviewView = ({
63149
63745
  }
63150
63746
  )
63151
63747
  ] }),
63152
- (activeTab === "leaderboard" || activeTab === "today") && showViewTypeDropdown && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative", children: /* @__PURE__ */ jsxRuntime.jsxs(
63153
- "select",
63154
- {
63155
- value: viewType,
63156
- onChange: (e) => setViewType(e.target.value),
63157
- 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",
63158
- 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` },
63159
- children: [
63160
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "operator", children: "Workforce" }),
63161
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "machine", children: "Machine" })
63162
- ]
63163
- }
63164
- ) })
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
+ ] })
63165
63794
  ] })
63166
63795
  ] })
63167
63796
  ] }) }),
@@ -63206,7 +63835,9 @@ var KPIsOverviewView = ({
63206
63835
  shiftEndDate,
63207
63836
  monthEndDate,
63208
63837
  viewType,
63209
- setViewType
63838
+ setViewType,
63839
+ timezone: configuredTimezone,
63840
+ isHistoricalDaily: isHistoricalLeaderboardDaily
63210
63841
  }
63211
63842
  ) })
63212
63843
  ) })
@@ -64748,51 +65379,41 @@ var ClipsCostView = () => {
64748
65379
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-[120px]" })
64749
65380
  ] }) })
64750
65381
  ] }) }),
64751
- /* @__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: [
64752
65383
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { className: "h-5 w-5 flex-shrink-0 mt-0.5" }),
64753
65384
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
64754
65385
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "font-medium", children: "Error loading usage data" }),
64755
65386
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm mt-1 text-red-600", children: error })
64756
65387
  ] })
64757
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-xl mt-8 animate-in fade-in slide-in-from-bottom-4 duration-500", children: [
64758
- /* @__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: [
64759
- /* @__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" }) }),
64760
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-sm font-medium text-gray-500 uppercase tracking-wider mb-2", children: "Clips Analyzed" }),
64761
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-500 mb-3", children: formatMonthLabel(data?.monthStart) }),
64762
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-5xl font-bold text-gray-900 mb-2 tabular-nums tracking-tight", children: formatNumber(data?.monthlyClassifications || 0) })
64763
- ] }) }) }),
64764
- /* @__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: [
64765
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-semibold text-gray-900 mb-3 uppercase tracking-wider", children: "Previous Months" }),
64766
- (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(
64767
65402
  "div",
64768
65403
  {
64769
- 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",
64770
65405
  children: [
64771
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-gray-700", children: formatMonthLabel(item.monthStart) }),
64772
- /* @__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) })
64773
65408
  ]
64774
65409
  },
64775
65410
  item.monthStart
64776
65411
  )) })
64777
- ] }) })
65412
+ ] }) }) })
64778
65413
  ] }) })
64779
65414
  ] });
64780
65415
  };
64781
65416
  var ClipsCostView_default = ClipsCostView;
64782
-
64783
- // src/lib/constants/actions.ts
64784
- var ACTION_NAMES = {
64785
- /** Assembly operations */
64786
- ASSEMBLY: "Assembly",
64787
- /** Packaging operations */
64788
- PACKAGING: "Packaging",
64789
- /** Inspection operations */
64790
- INSPECTION: "Inspection",
64791
- /** Testing operations */
64792
- TESTING: "Testing",
64793
- /** Quality control operations */
64794
- QUALITY_CONTROL: "Quality Control"
64795
- };
64796
65417
  var calculateShiftHours = (startTime, endTime, breaks = []) => {
64797
65418
  if (!startTime || !endTime) return 8;
64798
65419
  const [startHour, startMinute] = startTime.split(":").map(Number);
@@ -65374,9 +65995,9 @@ var ShiftsView = ({
65374
65995
  const actionIds = Array.from(
65375
65996
  new Set(currentThresholds.map((threshold) => threshold.action_id).filter(Boolean))
65376
65997
  );
65377
- const actionNameById = /* @__PURE__ */ new Map();
65998
+ const actionMetadataById = /* @__PURE__ */ new Map();
65378
65999
  if (actionIds.length > 0) {
65379
- 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);
65380
66001
  if (actionsError) {
65381
66002
  console.warn(
65382
66003
  `[ShiftsView] Failed to resolve action names for line ${lineId}, shift ${shift.shiftId}: ${actionsError.message}`
@@ -65384,7 +66005,13 @@ var ShiftsView = ({
65384
66005
  } else {
65385
66006
  (actionRows || []).forEach((actionRow) => {
65386
66007
  if (actionRow.id && actionRow.action_name) {
65387
- 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
+ });
65388
66015
  }
65389
66016
  });
65390
66017
  }
@@ -65405,7 +66032,7 @@ var ShiftsView = ({
65405
66032
  nextDayOutput = dayOutputToKeep;
65406
66033
  nextPPH = newShiftHours > 0 ? Math.round(dayOutputToKeep / newShiftHours) : 0;
65407
66034
  }
65408
- 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;
65409
66036
  return {
65410
66037
  line_id: threshold.line_id || lineId,
65411
66038
  shift_id: shift.shiftId,
@@ -65426,18 +66053,20 @@ var ShiftsView = ({
65426
66053
  `Failed to update action thresholds for line ${lineId}, shift ${shift.shiftId}: ${thresholdsUpsertError.message}`
65427
66054
  );
65428
66055
  }
65429
- const packagingActionIds = new Set(
65430
- 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)
65431
66058
  );
65432
- const packagingThresholds = recalculatedThresholds.filter((threshold) => {
65433
- if (packagingActionIds.has(threshold.action_id)) return true;
65434
- 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;
65435
66064
  });
65436
- const thresholdDayOutput = packagingThresholds.reduce(
66065
+ const thresholdDayOutput = outputThresholds.reduce(
65437
66066
  (sum, threshold) => sum + (Number(threshold.total_day_output) || 0),
65438
66067
  0
65439
66068
  );
65440
- const thresholdPPH = packagingThresholds.reduce(
66069
+ const thresholdPPH = outputThresholds.reduce(
65441
66070
  (sum, threshold) => sum + (Number(threshold.pph_threshold) || 0),
65442
66071
  0
65443
66072
  );
@@ -66648,7 +67277,7 @@ var TargetsViewUI = ({
66648
67277
  "aria-label": `Action type for ${formattedName}`,
66649
67278
  children: [
66650
67279
  /* @__PURE__ */ jsxRuntime.jsx("option", { value: "assembly", className: "py-2", children: "Assembly" }),
66651
- /* @__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 })
66652
67281
  ]
66653
67282
  }
66654
67283
  ) }),
@@ -66719,7 +67348,7 @@ var TargetsViewUI = ({
66719
67348
  "aria-label": `Action type for ${formattedName}`,
66720
67349
  children: [
66721
67350
  /* @__PURE__ */ jsxRuntime.jsx("option", { value: "assembly", children: "Assembly" }),
66722
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "packaging", children: "Packaging" })
67351
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "output", children: ACTION_NAMES.OUTPUT })
66723
67352
  ]
66724
67353
  }
66725
67354
  )
@@ -66924,14 +67553,20 @@ var TargetsView = ({
66924
67553
  throw new Error("Failed to fetch bulk targets data");
66925
67554
  }
66926
67555
  const { data } = bulkResponse;
66927
- const assemblyAction = Object.values(data.actions).find((a) => a.action_name === ACTION_NAMES.ASSEMBLY);
66928
- const packagingAction = Object.values(data.actions).find((a) => a.action_name === ACTION_NAMES.PACKAGING);
66929
- 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) {
66930
67565
  throw new Error("Could not find required actions in bulk response");
66931
67566
  }
66932
67567
  const actionIdsData = {
66933
67568
  assembly: assemblyAction.id,
66934
- packaging: packagingAction.id
67569
+ output: outputAction.id
66935
67570
  };
66936
67571
  setActionIds(actionIdsData);
66937
67572
  const newAllShiftsData = {};
@@ -66976,12 +67611,16 @@ var TargetsView = ({
66976
67611
  let actionType = "assembly";
66977
67612
  let actionId = actionIdsData.assembly;
66978
67613
  const effectiveActionId = threshold?.action_id ?? ws.action_id;
66979
- if (effectiveActionId === packagingAction.id || !effectiveActionId && ws.action_type === "packaging") {
66980
- actionType = "packaging";
66981
- actionId = packagingAction.id;
66982
- } 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") {
66983
67622
  actionType = "assembly";
66984
- actionId = assemblyAction.id;
67623
+ actionId = effectiveActionId || assemblyAction.id;
66985
67624
  }
66986
67625
  return {
66987
67626
  id: ws.id,
@@ -67253,7 +67892,9 @@ var TargetsView = ({
67253
67892
  // Round to whole number
67254
67893
  ideal_cycle_time: Number(ws.targetCycleTime) || 0,
67255
67894
  total_day_output: Number(ws.targetDayOutput) || 0,
67256
- 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,
67257
67898
  updated_by: currentEffectiveUserId,
67258
67899
  // Use the potentially hardcoded ID
67259
67900
  ...skuEnabled && lineDataToSave.selectedSKU ? { sku_id: lineDataToSave.selectedSKU.id } : {}
@@ -67261,7 +67902,7 @@ var TargetsView = ({
67261
67902
  console.log(`[handleSaveLine] workspaceThresholdUpdates for ${lineId}:`, workspaceThresholdUpdates);
67262
67903
  await workspaceService.updateActionThresholds(workspaceThresholdUpdates);
67263
67904
  console.log(`[handleSaveLine] Successfully updated action thresholds for ${lineId}`);
67264
- const packagingWorkspaces = lineDataToSave.workspaces.filter((ws) => ws.actionType === "packaging");
67905
+ const outputWorkspaces = lineDataToSave.workspaces.filter((ws) => ws.actionType === "output");
67265
67906
  let resolvedLineThresholdSkuId = lineDataToSave.selectedSKU?.id || null;
67266
67907
  if (!resolvedLineThresholdSkuId) {
67267
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);
@@ -67279,8 +67920,8 @@ var TargetsView = ({
67279
67920
  date: currentDate,
67280
67921
  shift_id: selectedShift,
67281
67922
  product_code: lineDataToSave.productId,
67282
- threshold_day_output: packagingWorkspaces.reduce((acc, ws) => acc + (Number(ws.targetDayOutput) || 0), 0),
67283
- 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),
67284
67925
  // Round each PPH value
67285
67926
  sku_id: resolvedLineThresholdSkuId
67286
67927
  };
@@ -67808,6 +68449,7 @@ var WorkspaceDetailView = ({
67808
68449
  avg_efficiency: Number(cachedOverviewMetrics.efficiency || 0),
67809
68450
  total_actions: totalActions,
67810
68451
  hourly_action_counts: [],
68452
+ hourly_cycle_times: [],
67811
68453
  workspace_rank: 0,
67812
68454
  total_workspaces: 0,
67813
68455
  ideal_output_until_now: idealOutput,
@@ -67818,6 +68460,22 @@ var WorkspaceDetailView = ({
67818
68460
  }, [cachedOverviewMetrics, shiftConfig?.shifts]);
67819
68461
  const workspace = (isHistoricView ? historicMetrics : liveMetrics) || cachedDetailedMetrics || overviewFallback;
67820
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
+ );
67821
68479
  const hasWorkspaceSnapshot = Boolean(workspace);
67822
68480
  const loading = ((isHistoricView ? historicLoading : liveLoading) || isShiftConfigLoading) && !hasWorkspaceSnapshot;
67823
68481
  const error = isHistoricView ? historicError : liveError;
@@ -68059,10 +68717,47 @@ var WorkspaceDetailView = ({
68059
68717
  return filterDataByDateKeyRange(monthlyData, range);
68060
68718
  }, [monthlyData, range]);
68061
68719
  const formattedWorkspaceName = displayName || formatWorkspaceName3(workspace?.workspace_name || "", effectiveLineId);
68062
- 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);
68063
68724
  const showIdleBreakdownChart = !shouldShowCycleTimeChart && idleTimeVlmEnabled;
68064
68725
  const idleClipDate = date || workspace?.date || calculatedOperationalDate || getOperationalDate(timezone);
68065
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]);
68066
68761
  const shiftDurationMinutes = React141.useMemo(
68067
68762
  () => getShiftDurationMinutes(workspace?.shift_start, workspace?.shift_end),
68068
68763
  [workspace?.shift_start, workspace?.shift_end]
@@ -68135,7 +68830,7 @@ var WorkspaceDetailView = ({
68135
68830
  }
68136
68831
  }, [returnUrl]);
68137
68832
  const handleBackNavigation = () => {
68138
- if (date || shift) {
68833
+ if (isHistoricView) {
68139
68834
  setActiveTab("monthly_history");
68140
68835
  if (onNavigate) {
68141
68836
  const params = new URLSearchParams();
@@ -68299,7 +68994,7 @@ var WorkspaceDetailView = ({
68299
68994
  BackButtonMinimal,
68300
68995
  {
68301
68996
  onClick: handleBackNavigation,
68302
- 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",
68303
68998
  size: "default",
68304
68999
  "aria-label": "Navigate back to previous page"
68305
69000
  }
@@ -68476,7 +69171,8 @@ var WorkspaceDetailView = ({
68476
69171
  {
68477
69172
  workspace,
68478
69173
  idleTimeReasons: idleTimeChartData,
68479
- efficiencyLegend
69174
+ efficiencyLegend,
69175
+ hourlyCycleTimes: cycleTimeChartData
68480
69176
  }
68481
69177
  ) }),
68482
69178
  activeTab === "monthly_history" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
@@ -68557,7 +69253,7 @@ var WorkspaceDetailView = ({
68557
69253
  animate: "animate",
68558
69254
  children: [
68559
69255
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center mb-4", children: [
68560
- /* @__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" }),
68561
69257
  !isUptimeMode && /* @__PURE__ */ jsxRuntime.jsx(
68562
69258
  "button",
68563
69259
  {
@@ -68590,9 +69286,14 @@ var WorkspaceDetailView = ({
68590
69286
  ) : shouldShowCycleTimeChart ? /* @__PURE__ */ jsxRuntime.jsx(
68591
69287
  CycleTimeOverTimeChart,
68592
69288
  {
68593
- data: workspace.hourly_action_counts || [],
69289
+ data: cycleTimeChartData,
68594
69290
  idealCycleTime: workspace.ideal_cycle_time || 0,
68595
- 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
68596
69297
  }
68597
69298
  ) : /* @__PURE__ */ jsxRuntime.jsx(
68598
69299
  HourlyOutputChart2,
@@ -68639,7 +69340,8 @@ var WorkspaceDetailView = ({
68639
69340
  {
68640
69341
  workspace,
68641
69342
  legend: efficiencyLegend,
68642
- layout: "stack"
69343
+ layout: "stack",
69344
+ idleTimeData: idleTimeVlmEnabled ? idleTimeData : void 0
68643
69345
  }
68644
69346
  ) : /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx(WorkspaceMetricCards, { workspace, legend: efficiencyLegend, className: "flex-1" }) })
68645
69347
  ] }),
@@ -68684,7 +69386,7 @@ var WorkspaceDetailView = ({
68684
69386
  animate: "animate",
68685
69387
  children: [
68686
69388
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-3 mb-4 flex-none", children: [
68687
- /* @__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" }),
68688
69390
  !isUptimeMode && /* @__PURE__ */ jsxRuntime.jsx(
68689
69391
  "button",
68690
69392
  {
@@ -68713,9 +69415,14 @@ var WorkspaceDetailView = ({
68713
69415
  ) : shouldShowCycleTimeChart ? /* @__PURE__ */ jsxRuntime.jsx(
68714
69416
  CycleTimeOverTimeChart,
68715
69417
  {
68716
- data: workspace.hourly_action_counts || [],
69418
+ data: cycleTimeChartData,
68717
69419
  idealCycleTime: workspace.ideal_cycle_time || 0,
68718
- 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
68719
69426
  }
68720
69427
  ) : /* @__PURE__ */ jsxRuntime.jsx(
68721
69428
  HourlyOutputChart2,
@@ -68767,7 +69474,8 @@ var WorkspaceDetailView = ({
68767
69474
  workspace,
68768
69475
  legend: efficiencyLegend,
68769
69476
  layout: "grid",
68770
- className: desktopBottomSectionClass
69477
+ className: desktopBottomSectionClass,
69478
+ idleTimeData: idleTimeVlmEnabled ? idleTimeData : void 0
68771
69479
  }
68772
69480
  ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: clsx("flex min-h-0", desktopBottomSectionClass), children: /* @__PURE__ */ jsxRuntime.jsx(WorkspaceMetricCards, { workspace, legend: efficiencyLegend, className: "flex-1" }) })
68773
69481
  ] })
@@ -68797,6 +69505,7 @@ var WorkspaceDetailView = ({
68797
69505
  availableShifts: shiftConfig?.shifts?.map((s) => ({ id: s.shiftId, name: s.shiftName })),
68798
69506
  legend: efficiencyLegend,
68799
69507
  trendSummary: workspaceMonthlyTrend,
69508
+ isAssemblyWorkspace,
68800
69509
  onDateSelect: (selectedDate, shiftId) => {
68801
69510
  if (onDateSelect) {
68802
69511
  onDateSelect(selectedDate, shiftId);
@@ -74884,6 +75593,11 @@ var normalizeTrend = (value) => ({
74884
75593
  });
74885
75594
  var normalizeIdleBreakdown = (value) => (value || []).map((item) => ({
74886
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,
74887
75601
  percentage: normalizeNumber(item?.percentage),
74888
75602
  total_duration_seconds: normalizeNumber(item?.total_duration_seconds),
74889
75603
  efficiency_loss_percentage: normalizeNumber(item?.efficiency_loss_percentage),
@@ -75935,7 +76649,12 @@ var IdleBreakdownCard = React141__namespace.default.memo(({
75935
76649
  const showInitialSkeleton = idle.loading && idle.lastUpdated === null;
75936
76650
  const idleBreakdown = React141__namespace.default.useMemo(() => {
75937
76651
  return idle.data.map((item) => ({
75938
- 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,
75939
76658
  value: toNumber3(item.percentage) || 0,
75940
76659
  totalDurationSeconds: toNumber3(item.total_duration_seconds),
75941
76660
  efficiencyLossPercentage: toNumber3(item.efficiency_loss_percentage),
@@ -77227,6 +77946,7 @@ var streamProxyConfig = {
77227
77946
  }
77228
77947
  };
77229
77948
 
77949
+ exports.ACTION_FAMILIES = ACTION_FAMILIES;
77230
77950
  exports.ACTION_NAMES = ACTION_NAMES;
77231
77951
  exports.AIAgentView = AIAgentView_default;
77232
77952
  exports.AcceptInvite = AcceptInvite;
@@ -77339,7 +78059,7 @@ exports.LINE_1_UUID = LINE_1_UUID;
77339
78059
  exports.LINE_2_UUID = LINE_2_UUID;
77340
78060
  exports.LargeOutputProgressChart = LargeOutputProgressChart;
77341
78061
  exports.LeaderboardDetailView = LeaderboardDetailView_default;
77342
- exports.Legend = Legend6;
78062
+ exports.Legend = Legend5;
77343
78063
  exports.LineAssignmentDropdown = LineAssignmentDropdown;
77344
78064
  exports.LineChart = LineChart;
77345
78065
  exports.LineHistoryCalendar = LineHistoryCalendar;
@@ -77521,6 +78241,7 @@ exports.formatReasonLabel = formatReasonLabel;
77521
78241
  exports.formatRelativeTime = formatRelativeTime;
77522
78242
  exports.formatTimeInZone = formatTimeInZone;
77523
78243
  exports.fromUrlFriendlyName = fromUrlFriendlyName;
78244
+ exports.getActionDisplayName = getActionDisplayName;
77524
78245
  exports.getAllLineDisplayNames = getAllLineDisplayNames;
77525
78246
  exports.getAllThreadMessages = getAllThreadMessages;
77526
78247
  exports.getAllWorkspaceDisplayNamesAsync = getAllWorkspaceDisplayNamesAsync;
@@ -77559,7 +78280,6 @@ exports.getMonthKeyBounds = getMonthKeyBounds;
77559
78280
  exports.getMonthWeekRanges = getMonthWeekRanges;
77560
78281
  exports.getNextUpdateInterval = getNextUpdateInterval;
77561
78282
  exports.getOperationalDate = getOperationalDate;
77562
- exports.getReasonColor = getReasonColor;
77563
78283
  exports.getRoleAssignmentKind = getRoleAssignmentKind;
77564
78284
  exports.getRoleDescription = getRoleDescription;
77565
78285
  exports.getRoleLabel = getRoleLabel;
@@ -77594,6 +78314,7 @@ exports.isFactoryScopedRole = isFactoryScopedRole;
77594
78314
  exports.isFullMonthRange = isFullMonthRange;
77595
78315
  exports.isLegacyConfiguration = isLegacyConfiguration;
77596
78316
  exports.isPrefetchError = isPrefetchError;
78317
+ exports.isRecentFlowVideoGridMetricMode = isRecentFlowVideoGridMetricMode;
77597
78318
  exports.isSafari = isSafari;
77598
78319
  exports.isSupervisorRole = isSupervisorRole;
77599
78320
  exports.isTransitionPeriod = isTransitionPeriod;
@@ -77604,14 +78325,17 @@ exports.isValidPrefetchParams = isValidPrefetchParams;
77604
78325
  exports.isValidPrefetchStatus = isValidPrefetchStatus;
77605
78326
  exports.isValidWorkspaceDetailedMetricsPayload = isValidWorkspaceDetailedMetricsPayload;
77606
78327
  exports.isValidWorkspaceMetricsPayload = isValidWorkspaceMetricsPayload;
78328
+ exports.isWipGatedVideoGridMetricMode = isWipGatedVideoGridMetricMode;
77607
78329
  exports.isWorkspaceDisplayNamesLoaded = isWorkspaceDisplayNamesLoaded;
77608
78330
  exports.isWorkspaceDisplayNamesLoading = isWorkspaceDisplayNamesLoading;
77609
78331
  exports.lineLeaderboardService = lineLeaderboardService;
77610
78332
  exports.linesService = linesService;
77611
78333
  exports.mergeWithDefaultConfig = mergeWithDefaultConfig;
77612
78334
  exports.migrateLegacyConfiguration = migrateLegacyConfiguration;
78335
+ exports.normalizeActionFamily = normalizeActionFamily;
77613
78336
  exports.normalizeDateKeyRange = normalizeDateKeyRange;
77614
78337
  exports.normalizeRoleLevel = normalizeRoleLevel;
78338
+ exports.normalizeVideoGridMetricMode = normalizeVideoGridMetricMode;
77615
78339
  exports.optifyeAgentClient = optifyeAgentClient;
77616
78340
  exports.parseDateKeyToDate = parseDateKeyToDate;
77617
78341
  exports.parseS3Uri = parseS3Uri;