@optifye/dashboard-core 6.12.6 → 6.12.7

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
@@ -5257,46 +5257,83 @@ var dashboardService = {
5257
5257
  const dbConfig = config.databaseConfig ?? DEFAULT_DATABASE_CONFIG;
5258
5258
  const linesTable = getTable(dbConfig, "lines");
5259
5259
  const companyId = config.entityConfig?.companyId;
5260
+ const baseLineSelect = `
5261
+ id,
5262
+ line_name,
5263
+ factory_id,
5264
+ factories!lines_factory_id_fkey(factory_name),
5265
+ company_id,
5266
+ companies!lines_company_id_fkey(company_name:name),
5267
+ enable,
5268
+ monitoring_mode,
5269
+ assembly,
5270
+ video_grid_metric_mode,
5271
+ recent_flow_window_minutes
5272
+ `;
5273
+ const lineSelectWithArea = `
5274
+ ${baseLineSelect},
5275
+ factory_area_id,
5276
+ factory_line_areas!lines_factory_area_id_factory_id_fkey(
5277
+ area_key,
5278
+ area_name,
5279
+ sort_order,
5280
+ enabled
5281
+ )
5282
+ `;
5283
+ const shouldRetryWithoutFactoryArea = (error) => {
5284
+ const errorText = [
5285
+ error?.message,
5286
+ error?.details,
5287
+ error?.hint,
5288
+ error?.code
5289
+ ].filter(Boolean).join(" ").toLowerCase();
5290
+ return errorText.includes("factory_area_id") || errorText.includes("factory_line_areas") || errorText.includes("lines_factory_area_id_factory_id_fkey");
5291
+ };
5260
5292
  try {
5261
- let query = supabase.from(linesTable).select(`
5262
- id,
5263
- line_name,
5264
- factory_id,
5265
- factories!lines_factory_id_fkey(factory_name),
5266
- company_id,
5267
- companies!lines_company_id_fkey(company_name:name),
5268
- enable,
5269
- monitoring_mode,
5270
- assembly,
5271
- video_grid_metric_mode,
5272
- recent_flow_window_minutes
5273
- `).eq("enable", true);
5274
- if (companyId) {
5275
- query = query.eq("company_id", companyId);
5276
- }
5277
- const { data, error } = await query;
5293
+ const fetchLines = async (selectClause) => {
5294
+ let query = supabase.from(linesTable).select(selectClause).eq("enable", true);
5295
+ if (companyId) {
5296
+ query = query.eq("company_id", companyId);
5297
+ }
5298
+ return await query;
5299
+ };
5300
+ let { data, error } = await fetchLines(lineSelectWithArea);
5301
+ if (error && shouldRetryWithoutFactoryArea(error)) {
5302
+ console.warn("[dashboardService.getAllLines] Factory area metadata unavailable, falling back to line-only metadata:", error);
5303
+ const fallbackResult = await fetchLines(baseLineSelect);
5304
+ data = fallbackResult.data;
5305
+ error = fallbackResult.error;
5306
+ }
5278
5307
  if (error) {
5279
5308
  console.error("Error fetching all lines:", error);
5280
5309
  throw error;
5281
5310
  }
5282
5311
  if (!data) return [];
5283
- const transformedLines = data.map((line) => ({
5284
- id: line.id,
5285
- line_name: line.line_name,
5286
- factory_id: line.factory_id,
5287
- factory_name: line.factories?.factory_name ?? "N/A",
5288
- company_id: line.company_id,
5289
- company_name: line.companies?.company_name ?? "N/A",
5290
- enable: line.enable ?? true,
5291
- // Default to true if not specified
5292
- monitoring_mode: line.monitoring_mode ?? "output",
5293
- assembly: line.assembly ?? false,
5294
- video_grid_metric_mode: normalizeVideoGridMetricMode(
5295
- line.video_grid_metric_mode,
5296
- line.assembly ?? false
5297
- ),
5298
- recent_flow_window_minutes: line.recent_flow_window_minutes ?? 7
5299
- }));
5312
+ const transformedLines = data.map((line) => {
5313
+ const factoryArea = Array.isArray(line.factory_line_areas) ? line.factory_line_areas[0] : line.factory_line_areas;
5314
+ return {
5315
+ id: line.id,
5316
+ line_name: line.line_name,
5317
+ factory_id: line.factory_id,
5318
+ factory_name: line.factories?.factory_name ?? "N/A",
5319
+ company_id: line.company_id,
5320
+ company_name: line.companies?.company_name ?? "N/A",
5321
+ enable: line.enable ?? true,
5322
+ // Default to true if not specified
5323
+ monitoring_mode: line.monitoring_mode ?? "output",
5324
+ assembly: line.assembly ?? false,
5325
+ video_grid_metric_mode: normalizeVideoGridMetricMode(
5326
+ line.video_grid_metric_mode,
5327
+ line.assembly ?? false
5328
+ ),
5329
+ recent_flow_window_minutes: line.recent_flow_window_minutes ?? 7,
5330
+ factory_area_id: line.factory_area_id ?? null,
5331
+ factory_area_key: factoryArea?.area_key ?? null,
5332
+ factory_area_name: factoryArea?.area_name ?? null,
5333
+ factory_area_sort_order: factoryArea?.sort_order ?? null,
5334
+ factory_area_enabled: factoryArea?.enabled ?? null
5335
+ };
5336
+ });
5300
5337
  return transformedLines;
5301
5338
  } catch (err) {
5302
5339
  console.error("Exception in getAllLines:", err);
@@ -14265,7 +14302,9 @@ var useLeaderboardMetrics = (date, shiftId, limit = 10, filter2 = "all") => {
14265
14302
  `/api/dashboard/leaderboard?${params.toString()}`
14266
14303
  );
14267
14304
  let entries = (data.entries || []).slice();
14268
- entries.sort((a, b) => (b.efficiency || 0) - (a.efficiency || 0));
14305
+ entries.sort(
14306
+ (a, b) => (b.leaderboard_value ?? b.efficiency ?? 0) - (a.leaderboard_value ?? a.efficiency ?? 0)
14307
+ );
14269
14308
  if (filter2 === "top") {
14270
14309
  entries = entries.slice(0, limit);
14271
14310
  } else if (filter2 === "bottom") {
@@ -22029,6 +22068,84 @@ var aggregateKPIsFromLineMetricsRows = (rows) => {
22029
22068
  };
22030
22069
  };
22031
22070
 
22071
+ // src/lib/utils/kpiHierarchy.ts
22072
+ var FACTORY_LEVEL_MIN_FACTORY_COUNT = 3;
22073
+ var compareText = (left, right) => left.localeCompare(right, void 0, { sensitivity: "base", numeric: true });
22074
+ var compareLinesByName = (left, right) => compareText(left.line_name || "", right.line_name || "");
22075
+ var normalizeAreaSortOrder = (value) => {
22076
+ if (typeof value === "number" && Number.isFinite(value)) return value;
22077
+ if (typeof value === "string" && value.trim() !== "") {
22078
+ const parsed = Number(value);
22079
+ return Number.isFinite(parsed) ? parsed : null;
22080
+ }
22081
+ return null;
22082
+ };
22083
+ var getEnabledAreaInfo = (line) => {
22084
+ if (!line.factory_area_id) return null;
22085
+ if (line.factory_area_enabled !== true) return null;
22086
+ if (!line.factory_area_name || line.factory_area_name.trim().length === 0) return null;
22087
+ return {
22088
+ id: line.factory_area_id,
22089
+ areaName: line.factory_area_name,
22090
+ areaKey: line.factory_area_key ?? null,
22091
+ sortOrder: normalizeAreaSortOrder(line.factory_area_sort_order)
22092
+ };
22093
+ };
22094
+ var buildKpiLineHierarchy = (lines) => {
22095
+ const factoryMap = /* @__PURE__ */ new Map();
22096
+ lines.forEach((line) => {
22097
+ const factoryId = line.factory_id || "unknown-factory";
22098
+ let factory = factoryMap.get(factoryId);
22099
+ if (!factory) {
22100
+ factory = {
22101
+ id: factoryId,
22102
+ factoryName: line.factory_name || "Factory",
22103
+ lines: [],
22104
+ areas: [],
22105
+ ungroupedLines: [],
22106
+ areaMap: /* @__PURE__ */ new Map()
22107
+ };
22108
+ factoryMap.set(factoryId, factory);
22109
+ }
22110
+ factory.lines.push(line);
22111
+ const areaInfo = getEnabledAreaInfo(line);
22112
+ if (!areaInfo) {
22113
+ factory.ungroupedLines.push(line);
22114
+ return;
22115
+ }
22116
+ let area = factory.areaMap.get(areaInfo.id);
22117
+ if (!area) {
22118
+ area = {
22119
+ id: areaInfo.id,
22120
+ areaName: areaInfo.areaName,
22121
+ areaKey: areaInfo.areaKey,
22122
+ sortOrder: areaInfo.sortOrder,
22123
+ lines: []
22124
+ };
22125
+ factory.areaMap.set(areaInfo.id, area);
22126
+ factory.areas.push(area);
22127
+ }
22128
+ area.lines.push(line);
22129
+ });
22130
+ const factories = Array.from(factoryMap.values()).map(({ areaMap: _areaMap, ...factory }) => {
22131
+ factory.lines.sort(compareLinesByName);
22132
+ factory.ungroupedLines.sort(compareLinesByName);
22133
+ factory.areas.forEach((area) => area.lines.sort(compareLinesByName));
22134
+ factory.areas.sort((left, right) => {
22135
+ const leftOrder = left.sortOrder ?? Number.POSITIVE_INFINITY;
22136
+ const rightOrder = right.sortOrder ?? Number.POSITIVE_INFINITY;
22137
+ if (leftOrder !== rightOrder) return leftOrder - rightOrder;
22138
+ return compareText(left.areaName, right.areaName);
22139
+ });
22140
+ return factory;
22141
+ });
22142
+ factories.sort((left, right) => compareText(left.factoryName, right.factoryName));
22143
+ return {
22144
+ factories,
22145
+ showFactoryLevel: factories.length >= FACTORY_LEVEL_MIN_FACTORY_COUNT
22146
+ };
22147
+ };
22148
+
22032
22149
  // src/lib/utils/awards.ts
22033
22150
  var toNumber2 = (value) => {
22034
22151
  if (typeof value === "number" && Number.isFinite(value)) return value;
@@ -38263,7 +38380,8 @@ var WorkspaceMetricCardsImpl = ({
38263
38380
  legend,
38264
38381
  skuAware,
38265
38382
  skuBreakdown,
38266
- activeSkuId
38383
+ activeSkuId,
38384
+ liveSkuId
38267
38385
  }) => {
38268
38386
  const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;
38269
38387
  const activeSku = React144__namespace.default.useMemo(() => {
@@ -38272,6 +38390,13 @@ var WorkspaceMetricCardsImpl = ({
38272
38390
  }
38273
38391
  return null;
38274
38392
  }, [skuAware, activeSkuId, skuBreakdown]);
38393
+ const displaySku = React144__namespace.default.useMemo(() => {
38394
+ if (activeSku) return activeSku;
38395
+ if (skuAware && !activeSkuId && liveSkuId && skuBreakdown) {
38396
+ return skuBreakdown.find((s) => s.sku_id === liveSkuId) ?? null;
38397
+ }
38398
+ return null;
38399
+ }, [activeSku, skuAware, activeSkuId, liveSkuId, skuBreakdown]);
38275
38400
  const pphValue = activeSku ? activeSku.avg_pph : workspace.avg_pph;
38276
38401
  const pphThreshold = activeSku ? activeSku.pph_threshold : workspace.pph_threshold;
38277
38402
  const cycleValue = activeSku ? activeSku.avg_cycle_time : workspace.avg_cycle_time;
@@ -38306,7 +38431,7 @@ var WorkspaceMetricCardsImpl = ({
38306
38431
  /* @__PURE__ */ jsxRuntime.jsxs(Card2, { className: "flex flex-col bg-white shadow-sm h-full min-h-[150px] sm:min-h-0", children: [
38307
38432
  /* @__PURE__ */ jsxRuntime.jsxs(CardHeader2, { className: "pb-2 flex-none text-center", children: [
38308
38433
  /* @__PURE__ */ jsxRuntime.jsx(CardTitle2, { className: "text-lg", children: "PPH" }),
38309
- activeSku && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-blue-600 mt-1 truncate px-2", title: activeSku.sku_code, children: activeSku.sku_code })
38434
+ displaySku && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-blue-600 mt-1 truncate px-2", title: displaySku.sku_code, children: displaySku.sku_code })
38310
38435
  ] }),
38311
38436
  /* @__PURE__ */ jsxRuntime.jsx(CardContent2, { className: "flex-1 flex items-center justify-center py-6 sm:py-3", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
38312
38437
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: `text-5xl font-bold ${pphValue >= pphThreshold ? "text-green-500" : "text-red-500"}`, children: pphValue.toFixed(1) }),
@@ -38319,7 +38444,7 @@ var WorkspaceMetricCardsImpl = ({
38319
38444
  /* @__PURE__ */ jsxRuntime.jsxs(Card2, { className: "flex flex-col bg-white shadow-sm h-full min-h-[150px] sm:min-h-0", children: [
38320
38445
  /* @__PURE__ */ jsxRuntime.jsxs(CardHeader2, { className: "pb-2 flex-none text-center", children: [
38321
38446
  /* @__PURE__ */ jsxRuntime.jsx(CardTitle2, { className: "text-lg", children: "Avg. Cycle Time" }),
38322
- activeSku && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-blue-600 mt-1 truncate px-2", title: activeSku.sku_code, children: activeSku.sku_code })
38447
+ displaySku && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-blue-600 mt-1 truncate px-2", title: displaySku.sku_code, children: displaySku.sku_code })
38323
38448
  ] }),
38324
38449
  /* @__PURE__ */ jsxRuntime.jsx(CardContent2, { className: "flex-1 flex items-center justify-center py-6 sm:py-3", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
38325
38450
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: `text-5xl font-bold ${cycleValue > (cycleStandard || 0) ? "text-red-500" : "text-green-500"}`, children: cycleValue.toFixed(1) }),
@@ -54301,7 +54426,8 @@ var WorkspaceCycleTimeMetricCards = ({
54301
54426
  idleTimeData,
54302
54427
  skuAware,
54303
54428
  skuBreakdown,
54304
- activeSkuId
54429
+ activeSkuId,
54430
+ liveSkuId
54305
54431
  }) => {
54306
54432
  const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;
54307
54433
  const activeSku = React144__namespace.default.useMemo(() => {
@@ -54310,6 +54436,13 @@ var WorkspaceCycleTimeMetricCards = ({
54310
54436
  }
54311
54437
  return null;
54312
54438
  }, [skuAware, activeSkuId, skuBreakdown]);
54439
+ const displaySku = React144__namespace.default.useMemo(() => {
54440
+ if (activeSku) return activeSku;
54441
+ if (skuAware && !activeSkuId && liveSkuId && skuBreakdown) {
54442
+ return skuBreakdown.find((s) => s.sku_id === liveSkuId) ?? null;
54443
+ }
54444
+ return null;
54445
+ }, [activeSku, skuAware, activeSkuId, liveSkuId, skuBreakdown]);
54313
54446
  const cycleValue = activeSku ? activeSku.avg_cycle_time : workspace.avg_cycle_time;
54314
54447
  const cycleStandard = activeSku ? activeSku.ideal_cycle_time : workspace.ideal_cycle_time;
54315
54448
  const efficiencyValue = workspace.avg_efficiency || 0;
@@ -54339,7 +54472,7 @@ var WorkspaceCycleTimeMetricCards = ({
54339
54472
  /* @__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: [
54340
54473
  /* @__PURE__ */ jsxRuntime.jsxs(CardHeader2, { className: "pb-1 pt-5 flex-none text-center", children: [
54341
54474
  /* @__PURE__ */ jsxRuntime.jsx(CardTitle2, { className: "text-[15px] font-bold text-gray-900 tracking-wide", children: "Cycle Time (s)" }),
54342
- activeSku && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-blue-600 mt-1 truncate px-2", title: activeSku.sku_code, children: activeSku.sku_code })
54475
+ displaySku && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-blue-600 mt-1 truncate px-2", title: displaySku.sku_code, children: displaySku.sku_code })
54343
54476
  ] }),
54344
54477
  /* @__PURE__ */ jsxRuntime.jsxs(CardContent2, { className: "flex-1 flex flex-col items-center justify-center pb-6", children: [
54345
54478
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: `text-5xl font-bold tracking-tight ${cycleValue > (cycleStandard || 0) ? "text-red-500" : "text-[#34C759]"}`, children: cycleValue.toFixed(1) }),
@@ -68429,6 +68562,9 @@ var KPIDetailView = ({
68429
68562
  var KPIDetailViewWithDisplayNames = withSelectedLineDisplayNames(KPIDetailView);
68430
68563
  var KPIDetailView_default = KPIDetailViewWithDisplayNames;
68431
68564
  var isNonEmptyString = (value) => typeof value === "string" && value.trim().length > 0;
68565
+ var KPI_FACTORY_QUERY_PARAM = "factory_id";
68566
+ var KPI_FACTORY_AREA_QUERY_PARAM = "factory_area_id";
68567
+ var getSingleQueryValue = (value) => typeof value === "string" && value.length > 0 ? value : void 0;
68432
68568
  var resolveCompanyId = (...candidates) => candidates.find(isNonEmptyString);
68433
68569
  var parseTimeToMinutes3 = (value) => {
68434
68570
  if (!value) return null;
@@ -68465,7 +68601,9 @@ var getMonthDateInfo = (timezone) => {
68465
68601
  var createKpisOverviewUrl = ({
68466
68602
  tab,
68467
68603
  date,
68468
- shift
68604
+ shift,
68605
+ factoryId,
68606
+ factoryAreaId
68469
68607
  }) => {
68470
68608
  const params = new URLSearchParams();
68471
68609
  if (tab) {
@@ -68477,6 +68615,12 @@ var createKpisOverviewUrl = ({
68477
68615
  if (typeof shift === "number" && Number.isFinite(shift)) {
68478
68616
  params.set("shift", shift.toString());
68479
68617
  }
68618
+ if (!tab && factoryId) {
68619
+ params.set(KPI_FACTORY_QUERY_PARAM, factoryId);
68620
+ }
68621
+ if (!tab && factoryId && factoryAreaId) {
68622
+ params.set(KPI_FACTORY_AREA_QUERY_PARAM, factoryAreaId);
68623
+ }
68480
68624
  const queryString = params.toString();
68481
68625
  return queryString ? `/kpis?${queryString}` : "/kpis";
68482
68626
  };
@@ -68998,6 +69142,101 @@ var LineCard = ({
68998
69142
  }
68999
69143
  );
69000
69144
  };
69145
+ var KpiGroupCard = ({
69146
+ title,
69147
+ subtitle,
69148
+ kpis,
69149
+ isLoading,
69150
+ error,
69151
+ isUptimeMode,
69152
+ onClick
69153
+ }) => {
69154
+ const isOnTrack = React144__namespace.default.useMemo(() => {
69155
+ if (!kpis) return null;
69156
+ return isEfficiencyOnTrack(kpis.efficiency.value);
69157
+ }, [kpis]);
69158
+ const outputTarget = Number(kpis?.outputProgress?.target ?? 0);
69159
+ const outputCurrent = Number(kpis?.outputProgress?.current ?? 0);
69160
+ const progressPercent = outputTarget > 0 ? Math.min(outputCurrent / outputTarget * 100, 100) : 0;
69161
+ return /* @__PURE__ */ jsxRuntime.jsxs(
69162
+ motion.div,
69163
+ {
69164
+ initial: { opacity: 0, y: 20 },
69165
+ animate: { opacity: 1, y: 0 },
69166
+ transition: { duration: 0.3 },
69167
+ onClick,
69168
+ className: "relative bg-white border border-gray-200/80 shadow-sm hover:shadow-lg \n rounded-xl p-4 sm:p-5 md:p-6 transition-all duration-200 cursor-pointer \n hover:scale-[1.01] active:scale-[0.99] group",
69169
+ children: [
69170
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-4 sm:mb-5 md:mb-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-start gap-2 sm:gap-3", children: [
69171
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
69172
+ /* @__PURE__ */ jsxRuntime.jsx(
69173
+ FittingTitle,
69174
+ {
69175
+ title,
69176
+ className: "text-[10px] sm:text-xs md:text-sm"
69177
+ }
69178
+ ),
69179
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-xs font-medium text-gray-500", children: subtitle })
69180
+ ] }),
69181
+ !isUptimeMode && kpis && isOnTrack !== null && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-1.5 px-2.5 sm:px-3 py-1 sm:py-1.5 rounded-full text-xs font-medium flex-shrink-0 ${isOnTrack ? "bg-emerald-100 text-emerald-700 border border-emerald-200" : "bg-red-100 text-red-700 border border-red-200"}`, style: { minWidth: "fit-content" }, children: [
69182
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-2 h-2 rounded-full ${isOnTrack ? "bg-emerald-500" : "bg-red-500"} animate-pulse` }),
69183
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: isOnTrack ? "On Track" : "Behind" })
69184
+ ] })
69185
+ ] }) }),
69186
+ isLoading && !kpis && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
69187
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "animate-pulse", children: [
69188
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-gray-200 rounded w-1/3 mb-2" }),
69189
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-8 bg-gray-200 rounded w-1/2" })
69190
+ ] }),
69191
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "animate-pulse", children: [
69192
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-gray-200 rounded w-1/3 mb-2" }),
69193
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-8 bg-gray-200 rounded w-3/4" })
69194
+ ] }),
69195
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "animate-pulse", children: [
69196
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-gray-200 rounded w-1/3 mb-2" }),
69197
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-8 bg-gray-200 rounded w-1/2" })
69198
+ ] })
69199
+ ] }),
69200
+ error && !kpis && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center py-8", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500", children: "Unable to load metrics" }) }),
69201
+ kpis && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4 sm:space-y-5 pb-8 sm:pb-10", children: [
69202
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
69203
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs font-medium text-gray-500 uppercase tracking-wider mb-1.5 sm:mb-2", children: isUptimeMode ? "Utilization" : "Efficiency" }),
69204
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline justify-between", children: [
69205
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-2xl sm:text-3xl font-semibold text-gray-900", children: [
69206
+ kpis.efficiency.value.toFixed(1),
69207
+ "%"
69208
+ ] }),
69209
+ kpis.efficiency.change !== 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: `text-xs sm:text-sm font-medium ${kpis.efficiency.change > 0 ? "text-emerald-600" : "text-red-600"}`, children: [
69210
+ kpis.efficiency.change > 0 ? "+" : "",
69211
+ kpis.efficiency.change.toFixed(1),
69212
+ "%"
69213
+ ] })
69214
+ ] })
69215
+ ] }),
69216
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
69217
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs font-medium text-gray-500 uppercase tracking-wider mb-1.5 sm:mb-2", children: isUptimeMode ? "Stoppages" : "Output Progress" }),
69218
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline justify-between mb-2 sm:mb-3", children: [
69219
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${isUptimeMode ? "text-2xl sm:text-3xl font-bold text-red-600" : "text-xl sm:text-2xl font-semibold text-gray-900"}`, children: kpis.outputProgress.current }),
69220
+ !isUptimeMode && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs sm:text-sm text-gray-500 font-medium", children: [
69221
+ "/ ",
69222
+ kpis.outputProgress.target,
69223
+ " units"
69224
+ ] })
69225
+ ] }),
69226
+ !isUptimeMode && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full bg-gray-200 rounded-full h-2 sm:h-2.5", children: /* @__PURE__ */ jsxRuntime.jsx(
69227
+ "div",
69228
+ {
69229
+ className: "bg-blue-600 h-2 sm:h-2.5 rounded-full transition-all duration-500 ease-out",
69230
+ style: { width: `${progressPercent}%` }
69231
+ }
69232
+ ) })
69233
+ ] })
69234
+ ] }),
69235
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute bottom-3 right-3 sm:bottom-4 sm:right-4", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-2 sm:p-2.5 rounded-full bg-gray-50 group-hover:bg-blue-50 transition-colors shadow-sm", children: /* @__PURE__ */ jsxRuntime.jsx(outline.ArrowRightIcon, { className: "w-4 h-4 sm:w-5 sm:h-5 text-gray-400 group-hover:text-blue-600 transition-colors" }) }) })
69236
+ ]
69237
+ }
69238
+ );
69239
+ };
69001
69240
  var KPIsOverviewView = ({
69002
69241
  companyId,
69003
69242
  navigate,
@@ -69163,6 +69402,20 @@ var KPIsOverviewView = ({
69163
69402
  const effectiveLeaderboardDate = selectedLeaderboardDate || currentShiftDate;
69164
69403
  const effectiveLeaderboardShiftId = Number.isFinite(selectedLeaderboardShiftId) ? selectedLeaderboardShiftId : currentShiftId;
69165
69404
  const isHistoricalLeaderboardDaily = activeTab === "leaderboard" && timeRange === "today" && (effectiveLeaderboardDate !== currentShiftDate || effectiveLeaderboardShiftId !== currentShiftId);
69405
+ const selectedFactoryIdFromUrl = getSingleQueryValue(router$1.query[KPI_FACTORY_QUERY_PARAM]);
69406
+ const selectedFactoryAreaIdFromUrl = getSingleQueryValue(router$1.query[KPI_FACTORY_AREA_QUERY_PARAM]);
69407
+ const kpiLineHierarchy = React144__namespace.default.useMemo(
69408
+ () => buildKpiLineHierarchy(linesForView),
69409
+ [linesForView]
69410
+ );
69411
+ const selectedFactoryNode = React144__namespace.default.useMemo(
69412
+ () => kpiLineHierarchy.showFactoryLevel && selectedFactoryIdFromUrl ? kpiLineHierarchy.factories.find((factory) => factory.id === selectedFactoryIdFromUrl) : void 0,
69413
+ [kpiLineHierarchy, selectedFactoryIdFromUrl]
69414
+ );
69415
+ const selectedFactoryAreaNode = React144__namespace.default.useMemo(
69416
+ () => selectedFactoryNode && selectedFactoryAreaIdFromUrl ? selectedFactoryNode.areas.find((area) => area.id === selectedFactoryAreaIdFromUrl) : void 0,
69417
+ [selectedFactoryNode, selectedFactoryAreaIdFromUrl]
69418
+ );
69166
69419
  React144.useEffect(() => {
69167
69420
  if (!router$1.isReady) return;
69168
69421
  const tabQuery = router$1.query.tab;
@@ -69190,20 +69443,27 @@ var KPIsOverviewView = ({
69190
69443
  ]);
69191
69444
  React144.useEffect(() => {
69192
69445
  if (!router$1.isReady || !hasHydratedLeaderboardRouteState) return;
69446
+ if (activeTab === "today" && loading) return;
69193
69447
  const expectedTab = activeTab === "leaderboard" ? "leaderboard" : void 0;
69194
69448
  const expectedDate = activeTab === "leaderboard" && timeRange === "today" && isHistoricalLeaderboardDaily ? effectiveLeaderboardDate : void 0;
69195
69449
  const expectedShift = expectedDate !== void 0 ? effectiveLeaderboardShiftId.toString() : void 0;
69450
+ const expectedFactory = activeTab === "today" && selectedFactoryNode ? selectedFactoryNode.id : void 0;
69451
+ const expectedFactoryArea = activeTab === "today" && selectedFactoryNode && selectedFactoryAreaNode ? selectedFactoryAreaNode.id : void 0;
69196
69452
  const currentTab = typeof router$1.query.tab === "string" ? router$1.query.tab : void 0;
69197
69453
  const currentDateQuery = typeof router$1.query.date === "string" ? router$1.query.date : void 0;
69198
69454
  const currentShiftQuery = typeof router$1.query.shift === "string" ? router$1.query.shift : void 0;
69199
- if (currentTab === expectedTab && currentDateQuery === expectedDate && currentShiftQuery === expectedShift) {
69455
+ const currentFactoryQuery = getSingleQueryValue(router$1.query[KPI_FACTORY_QUERY_PARAM]);
69456
+ const currentFactoryAreaQuery = getSingleQueryValue(router$1.query[KPI_FACTORY_AREA_QUERY_PARAM]);
69457
+ if (currentTab === expectedTab && currentDateQuery === expectedDate && currentShiftQuery === expectedShift && currentFactoryQuery === expectedFactory && currentFactoryAreaQuery === expectedFactoryArea) {
69200
69458
  return;
69201
69459
  }
69202
69460
  void router$1.replace(
69203
69461
  createKpisOverviewUrl({
69204
69462
  tab: expectedTab === "leaderboard" ? "leaderboard" : void 0,
69205
69463
  date: expectedDate,
69206
- shift: expectedShift !== void 0 ? Number.parseInt(expectedShift, 10) : void 0
69464
+ shift: expectedShift !== void 0 ? Number.parseInt(expectedShift, 10) : void 0,
69465
+ factoryId: expectedFactory,
69466
+ factoryAreaId: expectedFactoryArea
69207
69467
  }),
69208
69468
  void 0,
69209
69469
  { shallow: true }
@@ -69215,7 +69475,10 @@ var KPIsOverviewView = ({
69215
69475
  effectiveLeaderboardDate,
69216
69476
  effectiveLeaderboardShiftId,
69217
69477
  hasHydratedLeaderboardRouteState,
69218
- isHistoricalLeaderboardDaily
69478
+ isHistoricalLeaderboardDaily,
69479
+ loading,
69480
+ selectedFactoryNode,
69481
+ selectedFactoryAreaNode
69219
69482
  ]);
69220
69483
  const factoryViewId = entityConfig.factoryViewId || "factory";
69221
69484
  const {
@@ -69227,13 +69490,42 @@ var KPIsOverviewView = ({
69227
69490
  userAccessibleLineIds: metricsLineIds
69228
69491
  });
69229
69492
  const defaultKPIs = React144__namespace.default.useMemo(() => createDefaultKPIs(), []);
69230
- const kpisByLineId = React144__namespace.default.useMemo(() => {
69493
+ const lineModeById = React144__namespace.default.useMemo(() => {
69494
+ const map = /* @__PURE__ */ new Map();
69495
+ linesForView.forEach((line) => {
69496
+ map.set(line.id, line.monitoring_mode ?? "output");
69497
+ });
69498
+ return map;
69499
+ }, [linesForView]);
69500
+ const lineMetricRowsByLineId = React144__namespace.default.useMemo(() => {
69231
69501
  const map = /* @__PURE__ */ new Map();
69232
69502
  lineMetrics.forEach((row) => {
69233
- if (row?.line_id) map.set(row.line_id, buildKPIsFromLineMetricsRow(row));
69503
+ if (!row?.line_id) return;
69504
+ const monitoringMode = lineModeById.get(row.line_id);
69505
+ map.set(
69506
+ row.line_id,
69507
+ monitoringMode ? { ...row, monitoring_mode: monitoringMode } : row
69508
+ );
69234
69509
  });
69235
69510
  return map;
69236
- }, [lineMetrics]);
69511
+ }, [lineMetrics, lineModeById]);
69512
+ const kpisByLineId = React144__namespace.default.useMemo(() => {
69513
+ const map = /* @__PURE__ */ new Map();
69514
+ lineMetricRowsByLineId.forEach((row, lineId) => {
69515
+ map.set(lineId, buildKPIsFromLineMetricsRow(row));
69516
+ });
69517
+ return map;
69518
+ }, [lineMetricRowsByLineId]);
69519
+ const getLineCardKpis = React144__namespace.default.useCallback((line) => {
69520
+ if (metricsError) return null;
69521
+ return kpisByLineId.get(line.id) ?? (metricsLoading ? null : defaultKPIs);
69522
+ }, [defaultKPIs, kpisByLineId, metricsError, metricsLoading]);
69523
+ const getAggregateCardKpis = React144__namespace.default.useCallback((cardLines) => {
69524
+ if (metricsError) return null;
69525
+ const rows = cardLines.map((line) => lineMetricRowsByLineId.get(line.id)).filter(Boolean);
69526
+ if (metricsLoading && rows.length === 0) return null;
69527
+ return aggregateKPIsFromLineMetricsRows(rows);
69528
+ }, [lineMetricRowsByLineId, metricsError, metricsLoading]);
69237
69529
  const supervisorLineIds = React144__namespace.default.useMemo(
69238
69530
  () => (leaderboardLines.length > 0 ? leaderboardLines : lines).map((l) => l.id),
69239
69531
  [leaderboardLines, lines]
@@ -69437,6 +69729,20 @@ var KPIsOverviewView = ({
69437
69729
  if (activeTab !== "leaderboard" || timeRange !== "today") return;
69438
69730
  fetchDailyLeaderboard();
69439
69731
  }, [activeTab, timeRange, fetchDailyLeaderboard]);
69732
+ const navigateTodayHierarchy = React144.useCallback((factoryId, factoryAreaId) => {
69733
+ void router$1.push(
69734
+ createKpisOverviewUrl({ factoryId, factoryAreaId }),
69735
+ void 0,
69736
+ { shallow: true }
69737
+ );
69738
+ }, [router$1]);
69739
+ const lineDetailReturnTo = React144__namespace.default.useMemo(() => {
69740
+ if (activeTab !== "today" || !selectedFactoryNode) return void 0;
69741
+ return createKpisOverviewUrl({
69742
+ factoryId: selectedFactoryNode.id,
69743
+ factoryAreaId: selectedFactoryAreaNode?.id
69744
+ });
69745
+ }, [activeTab, selectedFactoryNode, selectedFactoryAreaNode]);
69440
69746
  const formatTopPerformerWeek = (periodStart, periodEnd) => {
69441
69747
  const dateToUse = periodStart ? /* @__PURE__ */ new Date(`${periodStart}T00:00:00`) : /* @__PURE__ */ new Date();
69442
69748
  if (Number.isNaN(dateToUse.getTime())) {
@@ -69523,7 +69829,8 @@ var KPIsOverviewView = ({
69523
69829
  );
69524
69830
  return;
69525
69831
  }
69526
- navigation.navigate(`/kpis/${line.id}`);
69832
+ const returnToQuery = lineDetailReturnTo ? `?returnTo=${encodeURIComponent(lineDetailReturnTo)}` : "";
69833
+ navigation.navigate(`/kpis/${line.id}${returnToQuery}`);
69527
69834
  };
69528
69835
  const handleBackClick = React144.useCallback(() => {
69529
69836
  trackCoreEvent("Back Button Clicked", {
@@ -69592,6 +69899,70 @@ var KPIsOverviewView = ({
69592
69899
  }
69593
69900
  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 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" }) });
69594
69901
  };
69902
+ const renderLineCard = (line) => /* @__PURE__ */ jsxRuntime.jsx(
69903
+ LineCard,
69904
+ {
69905
+ line,
69906
+ kpis: getLineCardKpis(line),
69907
+ isLoading: metricsLoading,
69908
+ error: metricsError,
69909
+ onClick: (kpis) => handleLineClick(line, kpis),
69910
+ supervisorEnabled,
69911
+ supervisorName: supervisorNamesByLineId.get(line.id) || null,
69912
+ supervisors: supervisorsByLineId?.get(line.id)
69913
+ },
69914
+ line.id
69915
+ );
69916
+ const renderGroupCard = ({
69917
+ key,
69918
+ title,
69919
+ subtitle,
69920
+ lines: cardLines,
69921
+ onClick
69922
+ }) => /* @__PURE__ */ jsxRuntime.jsx(
69923
+ KpiGroupCard,
69924
+ {
69925
+ title,
69926
+ subtitle,
69927
+ kpis: getAggregateCardKpis(cardLines),
69928
+ isLoading: metricsLoading,
69929
+ error: metricsError,
69930
+ isUptimeMode: viewType === "machine",
69931
+ onClick
69932
+ },
69933
+ key
69934
+ );
69935
+ const renderTodayCards = () => {
69936
+ if (!kpiLineHierarchy.showFactoryLevel) {
69937
+ return linesForView.map(renderLineCard);
69938
+ }
69939
+ if (selectedFactoryNode && selectedFactoryAreaNode) {
69940
+ return selectedFactoryAreaNode.lines.map(renderLineCard);
69941
+ }
69942
+ if (selectedFactoryNode) {
69943
+ return [
69944
+ ...selectedFactoryNode.areas.map(
69945
+ (area) => renderGroupCard({
69946
+ key: `area-${area.id}`,
69947
+ title: area.areaName,
69948
+ subtitle: `${area.lines.length} ${area.lines.length === 1 ? "line" : "lines"}`,
69949
+ lines: area.lines,
69950
+ onClick: () => navigateTodayHierarchy(selectedFactoryNode.id, area.id)
69951
+ })
69952
+ ),
69953
+ ...selectedFactoryNode.ungroupedLines.map(renderLineCard)
69954
+ ];
69955
+ }
69956
+ return kpiLineHierarchy.factories.map(
69957
+ (factory) => renderGroupCard({
69958
+ key: `factory-${factory.id}`,
69959
+ title: factory.factoryName,
69960
+ subtitle: `${factory.lines.length} ${factory.lines.length === 1 ? "line" : "lines"}`,
69961
+ lines: factory.lines,
69962
+ onClick: () => navigateTodayHierarchy(factory.id)
69963
+ })
69964
+ );
69965
+ };
69595
69966
  if (loading || isShiftConfigLoading) {
69596
69967
  return /* @__PURE__ */ jsxRuntime.jsx(LoadingPage, { message: "Loading production lines..." });
69597
69968
  }
@@ -70024,23 +70395,34 @@ var KPIsOverviewView = ({
70024
70395
  ] })
70025
70396
  ] })
70026
70397
  ] }) }),
70027
- /* @__PURE__ */ jsxRuntime.jsx("main", { className: `flex-1 p-3 sm:p-4 md:p-6 bg-slate-50 ${activeTab === "leaderboard" ? "overflow-hidden flex flex-col" : "overflow-y-auto"}`, children: activeTab === "today" ? (
70028
- /* Line Cards Grid */
70029
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-3 sm:gap-4 md:gap-6", children: linesForView.map((line) => /* @__PURE__ */ jsxRuntime.jsx(
70030
- LineCard,
70031
- {
70032
- line,
70033
- kpis: metricsError ? null : kpisByLineId.get(line.id) ?? (metricsLoading ? null : defaultKPIs),
70034
- isLoading: metricsLoading,
70035
- error: metricsError,
70036
- onClick: (kpis) => handleLineClick(line, kpis),
70037
- supervisorEnabled,
70038
- supervisorName: supervisorNamesByLineId.get(line.id) || null,
70039
- supervisors: supervisorsByLineId?.get(line.id)
70040
- },
70041
- line.id
70042
- )) })
70043
- ) : showLeaderboardLoader ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 flex flex-col items-center justify-center bg-white rounded-2xl border border-gray-100 shadow-sm m-2 sm:m-4", children: /* @__PURE__ */ jsxRuntime.jsx(
70398
+ /* @__PURE__ */ jsxRuntime.jsx("main", { className: `flex-1 p-3 sm:p-4 md:p-6 bg-slate-50 ${activeTab === "leaderboard" ? "overflow-hidden flex flex-col" : "overflow-y-auto"}`, children: activeTab === "today" ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3 sm:space-y-4", children: [
70399
+ kpiLineHierarchy.showFactoryLevel && selectedFactoryNode && /* @__PURE__ */ jsxRuntime.jsxs("nav", { className: "flex flex-wrap items-center gap-2 text-sm", "aria-label": "KPI hierarchy", children: [
70400
+ /* @__PURE__ */ jsxRuntime.jsx(
70401
+ "button",
70402
+ {
70403
+ type: "button",
70404
+ onClick: () => navigateTodayHierarchy(),
70405
+ className: "font-medium text-gray-500 hover:text-blue-600 transition-colors",
70406
+ children: "Factories"
70407
+ }
70408
+ ),
70409
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-300", children: "/" }),
70410
+ selectedFactoryAreaNode ? /* @__PURE__ */ jsxRuntime.jsx(
70411
+ "button",
70412
+ {
70413
+ type: "button",
70414
+ onClick: () => navigateTodayHierarchy(selectedFactoryNode.id),
70415
+ className: "font-medium text-gray-500 hover:text-blue-600 transition-colors",
70416
+ children: selectedFactoryNode.factoryName
70417
+ }
70418
+ ) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-gray-900", children: selectedFactoryNode.factoryName }),
70419
+ selectedFactoryAreaNode && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
70420
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-300", children: "/" }),
70421
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-gray-900", children: selectedFactoryAreaNode.areaName })
70422
+ ] })
70423
+ ] }),
70424
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-3 sm:gap-4 md:gap-6", children: renderTodayCards() })
70425
+ ] }) : showLeaderboardLoader ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 flex flex-col items-center justify-center bg-white rounded-2xl border border-gray-100 shadow-sm m-2 sm:m-4", children: /* @__PURE__ */ jsxRuntime.jsx(
70044
70426
  OptifyeLogoLoader_default,
70045
70427
  {
70046
70428
  size: "lg",
@@ -70087,6 +70469,27 @@ var AnimatedEfficiency = React144.memo(({ value }) => {
70087
70469
  return prevProps.value === nextProps.value;
70088
70470
  });
70089
70471
  AnimatedEfficiency.displayName = "AnimatedEfficiency";
70472
+ var getWorkspaceLeaderboardMetricValue = (workspace) => {
70473
+ if (workspace.monitoring_mode === "output") {
70474
+ return toFiniteNumber(workspace.efficiency);
70475
+ }
70476
+ if (workspace.leaderboard_metric_kind === "recent_flow_shift_average") {
70477
+ return toFiniteNumber(workspace.leaderboard_value);
70478
+ }
70479
+ return toFiniteNumber(workspace.leaderboard_value) ?? toFiniteNumber(workspace.efficiency);
70480
+ };
70481
+ var getWorkspaceDisplayedMetricValue = (workspace) => getWorkspaceLeaderboardMetricValue(workspace);
70482
+ var getWorkspaceLeaderboardMetricLabel = (workspace, defaultLabel) => workspace.monitoring_mode !== "output" && workspace.leaderboard_metric_kind === "recent_flow_shift_average" ? "Avg Flow" : defaultLabel;
70483
+ var renderWorkspaceLeaderboardMetric = (workspace, isAssemblyMode) => {
70484
+ if (isAssemblyMode) {
70485
+ return /* @__PURE__ */ jsxRuntime.jsx(CycleTimeComparison, { workspace });
70486
+ }
70487
+ const displayedMetricValue = getWorkspaceDisplayedMetricValue(workspace);
70488
+ if (displayedMetricValue === null) {
70489
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "tabular-nums", children: "--" });
70490
+ }
70491
+ return /* @__PURE__ */ jsxRuntime.jsx(AnimatedEfficiency, { value: displayedMetricValue });
70492
+ };
70090
70493
  var HeaderRibbon = React144.memo(({
70091
70494
  currentDate,
70092
70495
  currentMobileDate,
@@ -70165,13 +70568,13 @@ var MobileWorkspaceCard = React144.memo(({
70165
70568
  ] })
70166
70569
  ] }),
70167
70570
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-right", children: [
70168
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-bold text-gray-900 text-lg flex justify-end", children: isAssemblyMode ? /* @__PURE__ */ jsxRuntime.jsx(CycleTimeComparison, { workspace }) : /* @__PURE__ */ jsxRuntime.jsx(AnimatedEfficiency, { value: workspace.efficiency || 0 }) }),
70169
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-gray-500", children: metricLabel })
70571
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-bold text-gray-900 text-lg flex justify-end", children: renderWorkspaceLeaderboardMetric(workspace, isAssemblyMode) }),
70572
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-gray-500", children: getWorkspaceLeaderboardMetricLabel(workspace, metricLabel) })
70170
70573
  ] })
70171
70574
  ] })
70172
70575
  }
70173
70576
  ), (prevProps, nextProps) => {
70174
- return prevProps.metricLabel === nextProps.metricLabel && prevProps.isAssemblyMode === nextProps.isAssemblyMode && prevProps.rank === nextProps.rank && prevProps.cardClass === nextProps.cardClass && prevProps.isClickable === nextProps.isClickable && prevProps.workspace.workspace_uuid === nextProps.workspace.workspace_uuid && prevProps.workspace.efficiency === nextProps.workspace.efficiency && prevProps.workspace.ideal_cycle_time === nextProps.workspace.ideal_cycle_time && prevProps.workspace.action_count === nextProps.workspace.action_count && prevProps.workspace.action_threshold === nextProps.workspace.action_threshold && prevProps.workspace.avg_cycle_time === nextProps.workspace.avg_cycle_time && prevProps.workspace.displayName === nextProps.workspace.displayName && prevProps.workspace.lineName === nextProps.workspace.lineName;
70577
+ return prevProps.metricLabel === nextProps.metricLabel && prevProps.isAssemblyMode === nextProps.isAssemblyMode && prevProps.rank === nextProps.rank && prevProps.cardClass === nextProps.cardClass && prevProps.isClickable === nextProps.isClickable && prevProps.workspace.workspace_uuid === nextProps.workspace.workspace_uuid && prevProps.workspace.leaderboard_value === nextProps.workspace.leaderboard_value && prevProps.workspace.leaderboard_metric_kind === nextProps.workspace.leaderboard_metric_kind && prevProps.workspace.efficiency === nextProps.workspace.efficiency && prevProps.workspace.ideal_cycle_time === nextProps.workspace.ideal_cycle_time && prevProps.workspace.action_count === nextProps.workspace.action_count && prevProps.workspace.action_threshold === nextProps.workspace.action_threshold && prevProps.workspace.avg_cycle_time === nextProps.workspace.avg_cycle_time && prevProps.workspace.displayName === nextProps.workspace.displayName && prevProps.workspace.lineName === nextProps.workspace.lineName;
70175
70578
  });
70176
70579
  MobileWorkspaceCard.displayName = "MobileWorkspaceCard";
70177
70580
  var DesktopWorkspaceRow = React144.memo(({
@@ -70198,11 +70601,11 @@ var DesktopWorkspaceRow = React144.memo(({
70198
70601
  ] }) }),
70199
70602
  /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-3 py-2.5 sm:p-4 text-sm sm:text-base whitespace-nowrap", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-medium", children: workspace.displayName }) }),
70200
70603
  /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-3 py-2.5 sm:p-4 text-sm sm:text-base whitespace-nowrap", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-medium", children: workspace.lineName }) }),
70201
- /* @__PURE__ */ jsxRuntime.jsx("td", { className: `px-3 py-2.5 sm:p-4 text-sm sm:text-base font-medium ${isAssemblyMode ? "" : "whitespace-nowrap"}`, children: isAssemblyMode ? /* @__PURE__ */ jsxRuntime.jsx(CycleTimeComparison, { workspace }) : /* @__PURE__ */ jsxRuntime.jsx(AnimatedEfficiency, { value: workspace.efficiency || 0 }) })
70604
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: `px-3 py-2.5 sm:p-4 text-sm sm:text-base font-medium ${isAssemblyMode ? "" : "whitespace-nowrap"}`, children: isAssemblyMode ? renderWorkspaceLeaderboardMetric(workspace, true) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col", children: renderWorkspaceLeaderboardMetric(workspace, false) }) })
70202
70605
  ]
70203
70606
  }
70204
70607
  ), (prevProps, nextProps) => {
70205
- return prevProps.index === nextProps.index && prevProps.rowClass === nextProps.rowClass && prevProps.isClickable === nextProps.isClickable && prevProps.isAssemblyMode === nextProps.isAssemblyMode && prevProps.workspace.workspace_uuid === nextProps.workspace.workspace_uuid && prevProps.workspace.efficiency === nextProps.workspace.efficiency && prevProps.workspace.ideal_cycle_time === nextProps.workspace.ideal_cycle_time && prevProps.workspace.avg_cycle_time === nextProps.workspace.avg_cycle_time && prevProps.workspace.displayName === nextProps.workspace.displayName && prevProps.workspace.lineName === nextProps.workspace.lineName;
70608
+ return prevProps.index === nextProps.index && prevProps.rowClass === nextProps.rowClass && prevProps.isClickable === nextProps.isClickable && prevProps.isAssemblyMode === nextProps.isAssemblyMode && prevProps.workspace.workspace_uuid === nextProps.workspace.workspace_uuid && prevProps.workspace.leaderboard_value === nextProps.workspace.leaderboard_value && prevProps.workspace.leaderboard_metric_kind === nextProps.workspace.leaderboard_metric_kind && prevProps.workspace.efficiency === nextProps.workspace.efficiency && prevProps.workspace.ideal_cycle_time === nextProps.workspace.ideal_cycle_time && prevProps.workspace.avg_cycle_time === nextProps.workspace.avg_cycle_time && prevProps.workspace.displayName === nextProps.workspace.displayName && prevProps.workspace.lineName === nextProps.workspace.lineName;
70206
70609
  });
70207
70610
  DesktopWorkspaceRow.displayName = "DesktopWorkspaceRow";
70208
70611
  var LeaderboardDetailView = React144.memo(({
@@ -70301,6 +70704,8 @@ var LeaderboardDetailView = React144.memo(({
70301
70704
  const [monthlyError, setMonthlyError] = React144.useState(null);
70302
70705
  const todayRequestKeyRef = React144.useRef(null);
70303
70706
  const monthlyRequestKeyRef = React144.useRef(null);
70707
+ const monthlyLoadKeyRef = React144.useRef(null);
70708
+ const monthlyLoadPromiseRef = React144.useRef(null);
70304
70709
  const leaderboardUpdateQueuedRef = React144.useRef(false);
70305
70710
  const leaderboardUpdateTimerRef = React144.useRef(null);
70306
70711
  const leaderboardViewTrackedRef = React144.useRef(null);
@@ -70575,6 +70980,9 @@ var LeaderboardDetailView = React144.memo(({
70575
70980
  trend: 0,
70576
70981
  predicted_output: 0,
70577
70982
  efficiency: entry.efficiency || 0,
70983
+ avg_recent_flow: toFiniteNumber(entry.avg_recent_flow),
70984
+ leaderboard_value: toFiniteNumber(entry.leaderboard_value),
70985
+ leaderboard_metric_kind: entry.leaderboard_metric_kind ?? "efficiency",
70578
70986
  action_threshold: entry.total_day_output || 0,
70579
70987
  displayName: entry.workspace_display_name,
70580
70988
  monitoring_mode: entry.monitoring_mode ?? "output"
@@ -70605,7 +71013,10 @@ var LeaderboardDetailView = React144.memo(({
70605
71013
  searchParams.set("monitoring_mode", viewType === "machine" ? "uptime" : "output");
70606
71014
  const data = await fetchBackendJson(
70607
71015
  supabase,
70608
- `/api/dashboard/leaderboard?${searchParams.toString()}`
71016
+ `/api/dashboard/leaderboard?${searchParams.toString()}`,
71017
+ {
71018
+ timeoutMs: params.startDate && params.endDate ? 3e4 : void 0
71019
+ }
70609
71020
  );
70610
71021
  return data.entries || [];
70611
71022
  }, [supabase, entityConfig.companyId, configuredLineIds, viewType]);
@@ -70683,15 +71094,21 @@ var LeaderboardDetailView = React144.memo(({
70683
71094
  setMonthlyLoading(true);
70684
71095
  setMonthlyError(null);
70685
71096
  try {
70686
- const entries = await fetchLeaderboardEntries({
70687
- startDate: normalizedRange.startKey,
70688
- endDate: normalizedRange.endKey,
70689
- shiftId: monthlyShiftId
70690
- });
71097
+ const loadPromise = monthlyLoadPromiseRef.current && monthlyLoadKeyRef.current === requestKey ? monthlyLoadPromiseRef.current : (async () => {
71098
+ const entries = await fetchLeaderboardEntries({
71099
+ startDate: normalizedRange.startKey,
71100
+ endDate: normalizedRange.endKey,
71101
+ shiftId: monthlyShiftId
71102
+ });
71103
+ return mapEntriesToWorkspaces(entries, normalizedRange.endKey, monthlyShiftId);
71104
+ })();
71105
+ monthlyLoadKeyRef.current = requestKey;
71106
+ monthlyLoadPromiseRef.current = loadPromise;
71107
+ const workspaces = await loadPromise;
70691
71108
  if (monthlyRequestKeyRef.current !== requestKey) {
70692
71109
  return;
70693
71110
  }
70694
- setMonthlyEntries(mapEntriesToWorkspaces(entries, normalizedRange.endKey, monthlyShiftId));
71111
+ setMonthlyEntries(workspaces);
70695
71112
  } catch (err) {
70696
71113
  console.error("[LeaderboardDetailView] Error fetching monthly leaderboard:", err);
70697
71114
  if (monthlyRequestKeyRef.current !== requestKey) {
@@ -70700,6 +71117,9 @@ var LeaderboardDetailView = React144.memo(({
70700
71117
  setMonthlyError({ message: err.message, code: err.code || "FETCH_ERROR" });
70701
71118
  setMonthlyEntries([]);
70702
71119
  } finally {
71120
+ if (monthlyLoadKeyRef.current === requestKey && monthlyLoadPromiseRef.current) {
71121
+ monthlyLoadPromiseRef.current = null;
71122
+ }
70703
71123
  if (monthlyRequestKeyRef.current === requestKey) {
70704
71124
  setMonthlyLoading(false);
70705
71125
  }
@@ -70712,6 +71132,46 @@ var LeaderboardDetailView = React144.memo(({
70712
71132
  monthlyShiftId,
70713
71133
  lineKey
70714
71134
  ]);
71135
+ React144.useEffect(() => {
71136
+ if (activeTab !== "today") {
71137
+ return;
71138
+ }
71139
+ const requestKey = `${normalizedRange.startKey}|${normalizedRange.endKey}|${monthlyShiftId}|${lineKey}|${viewType}`;
71140
+ let cancelled = false;
71141
+ const loadPromise = monthlyLoadPromiseRef.current && monthlyLoadKeyRef.current === requestKey ? monthlyLoadPromiseRef.current : (async () => {
71142
+ const entries = await fetchLeaderboardEntries({
71143
+ startDate: normalizedRange.startKey,
71144
+ endDate: normalizedRange.endKey,
71145
+ shiftId: monthlyShiftId
71146
+ });
71147
+ return mapEntriesToWorkspaces(entries, normalizedRange.endKey, monthlyShiftId);
71148
+ })();
71149
+ monthlyLoadKeyRef.current = requestKey;
71150
+ monthlyLoadPromiseRef.current = loadPromise;
71151
+ loadPromise.then((workspaces) => {
71152
+ if (cancelled) {
71153
+ return;
71154
+ }
71155
+ setMonthlyEntries(workspaces);
71156
+ }).catch(() => {
71157
+ }).finally(() => {
71158
+ if (!cancelled && monthlyLoadKeyRef.current === requestKey && monthlyLoadPromiseRef.current === loadPromise) {
71159
+ monthlyLoadPromiseRef.current = null;
71160
+ }
71161
+ });
71162
+ return () => {
71163
+ cancelled = true;
71164
+ };
71165
+ }, [
71166
+ activeTab,
71167
+ fetchLeaderboardEntries,
71168
+ mapEntriesToWorkspaces,
71169
+ normalizedRange.endKey,
71170
+ normalizedRange.startKey,
71171
+ monthlyShiftId,
71172
+ lineKey,
71173
+ viewType
71174
+ ]);
70715
71175
  React144.useEffect(() => {
70716
71176
  if (activeTab === "today") {
70717
71177
  fetchTodayLeaderboard();
@@ -70846,6 +71306,9 @@ var LeaderboardDetailView = React144.memo(({
70846
71306
  total_workspaces: workspacesLengthRef.current,
70847
71307
  // Use ref instead of state to avoid dependency
70848
71308
  metric_context: viewType === "machine" ? "machine" : outputCategory,
71309
+ leaderboard_value: getWorkspaceLeaderboardMetricValue(workspace),
71310
+ leaderboard_metric_kind: workspace.leaderboard_metric_kind ?? "efficiency",
71311
+ avg_recent_flow: workspace.avg_recent_flow ?? null,
70849
71312
  efficiency: workspace.efficiency,
70850
71313
  action_count: workspace.action_count,
70851
71314
  action_threshold: workspace.action_threshold,
@@ -70926,9 +71389,12 @@ var LeaderboardDetailView = React144.memo(({
70926
71389
  if (ratioB === null) return -1;
70927
71390
  return sortAscending ? ratioA - ratioB : ratioB - ratioA;
70928
71391
  }
70929
- const effA = a.efficiency || 0;
70930
- const effB = b.efficiency || 0;
70931
- return sortAscending ? effA - effB : effB - effA;
71392
+ const metricA = getWorkspaceLeaderboardMetricValue(a);
71393
+ const metricB = getWorkspaceLeaderboardMetricValue(b);
71394
+ if (metricA === null && metricB === null) return 0;
71395
+ if (metricA === null) return 1;
71396
+ if (metricB === null) return -1;
71397
+ return sortAscending ? metricA - metricB : metricB - metricA;
70932
71398
  });
70933
71399
  }, [workspaceDisplayData, sortAscending, selectedLineFilter, selectedShiftFilter, activeTab, viewType, isAssemblyMode]);
70934
71400
  const loading = activeTab === "today" ? todayLoading : monthlyLoading;
@@ -70957,6 +71423,8 @@ var LeaderboardDetailView = React144.memo(({
70957
71423
  workspace_count: sortedWorkspaces.length,
70958
71424
  top_workspace_id: topWorkspace?.workspace_uuid ?? null,
70959
71425
  top_workspace_name: topWorkspace?.workspace_name ?? null,
71426
+ top_leaderboard_value: topWorkspace ? getWorkspaceLeaderboardMetricValue(topWorkspace) : null,
71427
+ top_leaderboard_metric_kind: topWorkspace?.leaderboard_metric_kind ?? null,
70960
71428
  top_efficiency: topWorkspace?.efficiency ?? null,
70961
71429
  top_avg_cycle_time: topWorkspace?.avg_cycle_time ?? null,
70962
71430
  top_ideal_cycle_time: topWorkspace?.ideal_cycle_time ?? null,
@@ -76102,7 +76570,11 @@ var WorkspaceDetailView = ({
76102
76570
  workspace,
76103
76571
  legend: efficiencyLegend,
76104
76572
  layout: "stack",
76105
- idleTimeData: idleTimeVlmEnabled ? idleTimeData : void 0
76573
+ idleTimeData: idleTimeVlmEnabled ? idleTimeData : void 0,
76574
+ skuAware: isSkuAware,
76575
+ skuBreakdown: isSkuAware ? realSkuBreakdown : void 0,
76576
+ activeSkuId,
76577
+ liveSkuId: isHistoricView ? null : liveSkuId
76106
76578
  }
76107
76579
  ) : /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx(
76108
76580
  WorkspaceMetricCards,
@@ -76112,7 +76584,8 @@ var WorkspaceDetailView = ({
76112
76584
  className: "flex-1",
76113
76585
  skuAware: isSkuAware,
76114
76586
  skuBreakdown: isSkuAware ? realSkuBreakdown : void 0,
76115
- activeSkuId
76587
+ activeSkuId,
76588
+ liveSkuId: isHistoricView ? null : liveSkuId
76116
76589
  }
76117
76590
  ) })
76118
76591
  ] }),
@@ -76255,7 +76728,11 @@ var WorkspaceDetailView = ({
76255
76728
  legend: efficiencyLegend,
76256
76729
  layout: "grid",
76257
76730
  className: desktopBottomSectionClass,
76258
- idleTimeData: idleTimeVlmEnabled ? idleTimeData : void 0
76731
+ idleTimeData: idleTimeVlmEnabled ? idleTimeData : void 0,
76732
+ skuAware: isSkuAware,
76733
+ skuBreakdown: isSkuAware ? realSkuBreakdown : void 0,
76734
+ activeSkuId,
76735
+ liveSkuId: isHistoricView ? null : liveSkuId
76259
76736
  }
76260
76737
  ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: clsx("flex min-h-0", desktopBottomSectionClass), children: /* @__PURE__ */ jsxRuntime.jsx(
76261
76738
  WorkspaceMetricCards,
@@ -76265,7 +76742,8 @@ var WorkspaceDetailView = ({
76265
76742
  className: "flex-1",
76266
76743
  skuAware: isSkuAware,
76267
76744
  skuBreakdown: isSkuAware ? realSkuBreakdown : void 0,
76268
- activeSkuId
76745
+ activeSkuId,
76746
+ liveSkuId: isHistoricView ? null : liveSkuId
76269
76747
  }
76270
76748
  ) })
76271
76749
  ] })
@@ -85553,6 +86031,7 @@ exports.authRateLimitService = authRateLimitService;
85553
86031
  exports.awardsService = awardsService;
85554
86032
  exports.buildDateKey = buildDateKey;
85555
86033
  exports.buildKPIsFromLineMetricsRow = buildKPIsFromLineMetricsRow;
86034
+ exports.buildKpiLineHierarchy = buildKpiLineHierarchy;
85556
86035
  exports.buildLineSkuBreakdown = buildLineSkuBreakdown;
85557
86036
  exports.buildShiftGroupsKey = buildShiftGroupsKey;
85558
86037
  exports.canRoleAccessDashboardPath = canRoleAccessDashboardPath;