@optifye/dashboard-core 6.12.6 → 6.12.8

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,21 @@ 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.leaderboard_metric_kind === "recent_flow_shift_average") {
70474
+ return toFiniteNumber(workspace.leaderboard_value) ?? toFiniteNumber(workspace.avg_recent_flow);
70475
+ }
70476
+ return toFiniteNumber(workspace.leaderboard_value) ?? toFiniteNumber(workspace.efficiency);
70477
+ };
70478
+ var getWorkspaceDisplayedMetricValue = (workspace) => getWorkspaceLeaderboardMetricValue(workspace);
70479
+ var getWorkspaceLeaderboardMetricLabel = (workspace, defaultLabel) => workspace.leaderboard_metric_kind === "recent_flow_shift_average" ? "Avg Flow" : defaultLabel;
70480
+ var renderWorkspaceLeaderboardMetric = (workspace) => {
70481
+ const displayedMetricValue = getWorkspaceDisplayedMetricValue(workspace);
70482
+ if (displayedMetricValue === null) {
70483
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "tabular-nums", children: "--" });
70484
+ }
70485
+ return /* @__PURE__ */ jsxRuntime.jsx(AnimatedEfficiency, { value: displayedMetricValue });
70486
+ };
70090
70487
  var HeaderRibbon = React144.memo(({
70091
70488
  currentDate,
70092
70489
  currentMobileDate,
@@ -70137,8 +70534,7 @@ var MobileWorkspaceCard = React144.memo(({
70137
70534
  isClickable,
70138
70535
  onWorkspaceClick,
70139
70536
  getMedalIcon,
70140
- metricLabel,
70141
- isAssemblyMode
70537
+ metricLabel
70142
70538
  }) => /* @__PURE__ */ jsxRuntime.jsx(
70143
70539
  motion.div,
70144
70540
  {
@@ -70165,13 +70561,13 @@ var MobileWorkspaceCard = React144.memo(({
70165
70561
  ] })
70166
70562
  ] }),
70167
70563
  /* @__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 })
70564
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-bold text-gray-900 text-lg flex justify-end", children: renderWorkspaceLeaderboardMetric(workspace) }),
70565
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-gray-500", children: getWorkspaceLeaderboardMetricLabel(workspace, metricLabel) })
70170
70566
  ] })
70171
70567
  ] })
70172
70568
  }
70173
70569
  ), (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;
70570
+ return prevProps.metricLabel === nextProps.metricLabel && 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.avg_recent_flow === nextProps.workspace.avg_recent_flow && 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
70571
  });
70176
70572
  MobileWorkspaceCard.displayName = "MobileWorkspaceCard";
70177
70573
  var DesktopWorkspaceRow = React144.memo(({
@@ -70180,8 +70576,7 @@ var DesktopWorkspaceRow = React144.memo(({
70180
70576
  rowClass,
70181
70577
  isClickable,
70182
70578
  onWorkspaceClick,
70183
- getMedalIcon,
70184
- isAssemblyMode
70579
+ getMedalIcon
70185
70580
  }) => /* @__PURE__ */ jsxRuntime.jsxs(
70186
70581
  motion.tr,
70187
70582
  {
@@ -70198,11 +70593,11 @@ var DesktopWorkspaceRow = React144.memo(({
70198
70593
  ] }) }),
70199
70594
  /* @__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
70595
  /* @__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 }) })
70596
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-3 py-2.5 sm:p-4 text-sm sm:text-base font-medium whitespace-nowrap", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col", children: renderWorkspaceLeaderboardMetric(workspace) }) })
70202
70597
  ]
70203
70598
  }
70204
70599
  ), (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;
70600
+ return prevProps.index === nextProps.index && prevProps.rowClass === nextProps.rowClass && prevProps.isClickable === nextProps.isClickable && prevProps.workspace.workspace_uuid === nextProps.workspace.workspace_uuid && prevProps.workspace.leaderboard_value === nextProps.workspace.leaderboard_value && prevProps.workspace.avg_recent_flow === nextProps.workspace.avg_recent_flow && 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
70601
  });
70207
70602
  DesktopWorkspaceRow.displayName = "DesktopWorkspaceRow";
70208
70603
  var LeaderboardDetailView = React144.memo(({
@@ -70301,6 +70696,8 @@ var LeaderboardDetailView = React144.memo(({
70301
70696
  const [monthlyError, setMonthlyError] = React144.useState(null);
70302
70697
  const todayRequestKeyRef = React144.useRef(null);
70303
70698
  const monthlyRequestKeyRef = React144.useRef(null);
70699
+ const monthlyLoadKeyRef = React144.useRef(null);
70700
+ const monthlyLoadPromiseRef = React144.useRef(null);
70304
70701
  const leaderboardUpdateQueuedRef = React144.useRef(false);
70305
70702
  const leaderboardUpdateTimerRef = React144.useRef(null);
70306
70703
  const leaderboardViewTrackedRef = React144.useRef(null);
@@ -70575,6 +70972,9 @@ var LeaderboardDetailView = React144.memo(({
70575
70972
  trend: 0,
70576
70973
  predicted_output: 0,
70577
70974
  efficiency: entry.efficiency || 0,
70975
+ avg_recent_flow: toFiniteNumber(entry.avg_recent_flow),
70976
+ leaderboard_value: toFiniteNumber(entry.leaderboard_value),
70977
+ leaderboard_metric_kind: entry.leaderboard_metric_kind ?? "efficiency",
70578
70978
  action_threshold: entry.total_day_output || 0,
70579
70979
  displayName: entry.workspace_display_name,
70580
70980
  monitoring_mode: entry.monitoring_mode ?? "output"
@@ -70605,7 +71005,10 @@ var LeaderboardDetailView = React144.memo(({
70605
71005
  searchParams.set("monitoring_mode", viewType === "machine" ? "uptime" : "output");
70606
71006
  const data = await fetchBackendJson(
70607
71007
  supabase,
70608
- `/api/dashboard/leaderboard?${searchParams.toString()}`
71008
+ `/api/dashboard/leaderboard?${searchParams.toString()}`,
71009
+ {
71010
+ timeoutMs: params.startDate && params.endDate ? 3e4 : void 0
71011
+ }
70609
71012
  );
70610
71013
  return data.entries || [];
70611
71014
  }, [supabase, entityConfig.companyId, configuredLineIds, viewType]);
@@ -70683,15 +71086,21 @@ var LeaderboardDetailView = React144.memo(({
70683
71086
  setMonthlyLoading(true);
70684
71087
  setMonthlyError(null);
70685
71088
  try {
70686
- const entries = await fetchLeaderboardEntries({
70687
- startDate: normalizedRange.startKey,
70688
- endDate: normalizedRange.endKey,
70689
- shiftId: monthlyShiftId
70690
- });
71089
+ const loadPromise = monthlyLoadPromiseRef.current && monthlyLoadKeyRef.current === requestKey ? monthlyLoadPromiseRef.current : (async () => {
71090
+ const entries = await fetchLeaderboardEntries({
71091
+ startDate: normalizedRange.startKey,
71092
+ endDate: normalizedRange.endKey,
71093
+ shiftId: monthlyShiftId
71094
+ });
71095
+ return mapEntriesToWorkspaces(entries, normalizedRange.endKey, monthlyShiftId);
71096
+ })();
71097
+ monthlyLoadKeyRef.current = requestKey;
71098
+ monthlyLoadPromiseRef.current = loadPromise;
71099
+ const workspaces = await loadPromise;
70691
71100
  if (monthlyRequestKeyRef.current !== requestKey) {
70692
71101
  return;
70693
71102
  }
70694
- setMonthlyEntries(mapEntriesToWorkspaces(entries, normalizedRange.endKey, monthlyShiftId));
71103
+ setMonthlyEntries(workspaces);
70695
71104
  } catch (err) {
70696
71105
  console.error("[LeaderboardDetailView] Error fetching monthly leaderboard:", err);
70697
71106
  if (monthlyRequestKeyRef.current !== requestKey) {
@@ -70700,6 +71109,9 @@ var LeaderboardDetailView = React144.memo(({
70700
71109
  setMonthlyError({ message: err.message, code: err.code || "FETCH_ERROR" });
70701
71110
  setMonthlyEntries([]);
70702
71111
  } finally {
71112
+ if (monthlyLoadKeyRef.current === requestKey && monthlyLoadPromiseRef.current) {
71113
+ monthlyLoadPromiseRef.current = null;
71114
+ }
70703
71115
  if (monthlyRequestKeyRef.current === requestKey) {
70704
71116
  setMonthlyLoading(false);
70705
71117
  }
@@ -70712,6 +71124,46 @@ var LeaderboardDetailView = React144.memo(({
70712
71124
  monthlyShiftId,
70713
71125
  lineKey
70714
71126
  ]);
71127
+ React144.useEffect(() => {
71128
+ if (activeTab !== "today") {
71129
+ return;
71130
+ }
71131
+ const requestKey = `${normalizedRange.startKey}|${normalizedRange.endKey}|${monthlyShiftId}|${lineKey}|${viewType}`;
71132
+ let cancelled = false;
71133
+ const loadPromise = monthlyLoadPromiseRef.current && monthlyLoadKeyRef.current === requestKey ? monthlyLoadPromiseRef.current : (async () => {
71134
+ const entries = await fetchLeaderboardEntries({
71135
+ startDate: normalizedRange.startKey,
71136
+ endDate: normalizedRange.endKey,
71137
+ shiftId: monthlyShiftId
71138
+ });
71139
+ return mapEntriesToWorkspaces(entries, normalizedRange.endKey, monthlyShiftId);
71140
+ })();
71141
+ monthlyLoadKeyRef.current = requestKey;
71142
+ monthlyLoadPromiseRef.current = loadPromise;
71143
+ loadPromise.then((workspaces) => {
71144
+ if (cancelled) {
71145
+ return;
71146
+ }
71147
+ setMonthlyEntries(workspaces);
71148
+ }).catch(() => {
71149
+ }).finally(() => {
71150
+ if (!cancelled && monthlyLoadKeyRef.current === requestKey && monthlyLoadPromiseRef.current === loadPromise) {
71151
+ monthlyLoadPromiseRef.current = null;
71152
+ }
71153
+ });
71154
+ return () => {
71155
+ cancelled = true;
71156
+ };
71157
+ }, [
71158
+ activeTab,
71159
+ fetchLeaderboardEntries,
71160
+ mapEntriesToWorkspaces,
71161
+ normalizedRange.endKey,
71162
+ normalizedRange.startKey,
71163
+ monthlyShiftId,
71164
+ lineKey,
71165
+ viewType
71166
+ ]);
70715
71167
  React144.useEffect(() => {
70716
71168
  if (activeTab === "today") {
70717
71169
  fetchTodayLeaderboard();
@@ -70846,6 +71298,9 @@ var LeaderboardDetailView = React144.memo(({
70846
71298
  total_workspaces: workspacesLengthRef.current,
70847
71299
  // Use ref instead of state to avoid dependency
70848
71300
  metric_context: viewType === "machine" ? "machine" : outputCategory,
71301
+ leaderboard_value: getWorkspaceLeaderboardMetricValue(workspace),
71302
+ leaderboard_metric_kind: workspace.leaderboard_metric_kind ?? "efficiency",
71303
+ avg_recent_flow: workspace.avg_recent_flow ?? null,
70849
71304
  efficiency: workspace.efficiency,
70850
71305
  action_count: workspace.action_count,
70851
71306
  action_threshold: workspace.action_threshold,
@@ -70918,17 +71373,12 @@ var LeaderboardDetailView = React144.memo(({
70918
71373
  filtered = filtered.filter((ws) => ws.shift_id?.toString() === selectedShiftFilter);
70919
71374
  }
70920
71375
  return filtered.sort((a, b) => {
70921
- if (isAssemblyMode) {
70922
- const ratioA = getCycleRatio(a);
70923
- const ratioB = getCycleRatio(b);
70924
- if (ratioA === null && ratioB === null) return 0;
70925
- if (ratioA === null) return 1;
70926
- if (ratioB === null) return -1;
70927
- return sortAscending ? ratioA - ratioB : ratioB - ratioA;
70928
- }
70929
- const effA = a.efficiency || 0;
70930
- const effB = b.efficiency || 0;
70931
- return sortAscending ? effA - effB : effB - effA;
71376
+ const metricA = getWorkspaceLeaderboardMetricValue(a);
71377
+ const metricB = getWorkspaceLeaderboardMetricValue(b);
71378
+ if (metricA === null && metricB === null) return 0;
71379
+ if (metricA === null) return 1;
71380
+ if (metricB === null) return -1;
71381
+ return sortAscending ? metricA - metricB : metricB - metricA;
70932
71382
  });
70933
71383
  }, [workspaceDisplayData, sortAscending, selectedLineFilter, selectedShiftFilter, activeTab, viewType, isAssemblyMode]);
70934
71384
  const loading = activeTab === "today" ? todayLoading : monthlyLoading;
@@ -70957,6 +71407,8 @@ var LeaderboardDetailView = React144.memo(({
70957
71407
  workspace_count: sortedWorkspaces.length,
70958
71408
  top_workspace_id: topWorkspace?.workspace_uuid ?? null,
70959
71409
  top_workspace_name: topWorkspace?.workspace_name ?? null,
71410
+ top_leaderboard_value: topWorkspace ? getWorkspaceLeaderboardMetricValue(topWorkspace) : null,
71411
+ top_leaderboard_metric_kind: topWorkspace?.leaderboard_metric_kind ?? null,
70960
71412
  top_efficiency: topWorkspace?.efficiency ?? null,
70961
71413
  top_avg_cycle_time: topWorkspace?.avg_cycle_time ?? null,
70962
71414
  top_ideal_cycle_time: topWorkspace?.ideal_cycle_time ?? null,
@@ -70990,7 +71442,13 @@ var LeaderboardDetailView = React144.memo(({
70990
71442
  error.message
70991
71443
  ] }) });
70992
71444
  }
70993
- const metricLabel = viewType === "machine" ? "Utilization" : isAssemblyMode ? "Cycle Time" : "Efficiency";
71445
+ const hasRecentFlowLeaderboardRows = sortedWorkspaces.some(
71446
+ (workspace) => workspace.leaderboard_metric_kind === "recent_flow_shift_average"
71447
+ );
71448
+ const hasEfficiencyLeaderboardRows = sortedWorkspaces.some(
71449
+ (workspace) => workspace.leaderboard_metric_kind !== "recent_flow_shift_average"
71450
+ );
71451
+ const metricLabel = viewType === "machine" ? "Utilization" : hasRecentFlowLeaderboardRows && !hasEfficiencyLeaderboardRows ? "Avg Flow" : hasRecentFlowLeaderboardRows && hasEfficiencyLeaderboardRows ? "Performance" : "Efficiency";
70994
71452
  const descendingSortLabel = "Highest to Lowest";
70995
71453
  const ascendingSortLabel = "Lowest to Highest";
70996
71454
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `min-h-screen bg-slate-50 flex flex-col ${className}`, style: { willChange: "contents" }, children: [
@@ -71254,8 +71712,7 @@ var LeaderboardDetailView = React144.memo(({
71254
71712
  isClickable: canOpenWorkspace(ws.line_id),
71255
71713
  onWorkspaceClick: stableHandleWorkspaceClick,
71256
71714
  getMedalIcon: stableGetMedalIcon,
71257
- metricLabel,
71258
- isAssemblyMode
71715
+ metricLabel
71259
71716
  },
71260
71717
  ws.workspace_uuid
71261
71718
  );
@@ -71279,8 +71736,7 @@ var LeaderboardDetailView = React144.memo(({
71279
71736
  rowClass,
71280
71737
  isClickable: canOpenWorkspace(ws.line_id),
71281
71738
  onWorkspaceClick: stableHandleWorkspaceClick,
71282
- getMedalIcon: stableGetMedalIcon,
71283
- isAssemblyMode
71739
+ getMedalIcon: stableGetMedalIcon
71284
71740
  },
71285
71741
  ws.workspace_uuid
71286
71742
  );
@@ -76102,7 +76558,11 @@ var WorkspaceDetailView = ({
76102
76558
  workspace,
76103
76559
  legend: efficiencyLegend,
76104
76560
  layout: "stack",
76105
- idleTimeData: idleTimeVlmEnabled ? idleTimeData : void 0
76561
+ idleTimeData: idleTimeVlmEnabled ? idleTimeData : void 0,
76562
+ skuAware: isSkuAware,
76563
+ skuBreakdown: isSkuAware ? realSkuBreakdown : void 0,
76564
+ activeSkuId,
76565
+ liveSkuId: isHistoricView ? null : liveSkuId
76106
76566
  }
76107
76567
  ) : /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx(
76108
76568
  WorkspaceMetricCards,
@@ -76112,7 +76572,8 @@ var WorkspaceDetailView = ({
76112
76572
  className: "flex-1",
76113
76573
  skuAware: isSkuAware,
76114
76574
  skuBreakdown: isSkuAware ? realSkuBreakdown : void 0,
76115
- activeSkuId
76575
+ activeSkuId,
76576
+ liveSkuId: isHistoricView ? null : liveSkuId
76116
76577
  }
76117
76578
  ) })
76118
76579
  ] }),
@@ -76255,7 +76716,11 @@ var WorkspaceDetailView = ({
76255
76716
  legend: efficiencyLegend,
76256
76717
  layout: "grid",
76257
76718
  className: desktopBottomSectionClass,
76258
- idleTimeData: idleTimeVlmEnabled ? idleTimeData : void 0
76719
+ idleTimeData: idleTimeVlmEnabled ? idleTimeData : void 0,
76720
+ skuAware: isSkuAware,
76721
+ skuBreakdown: isSkuAware ? realSkuBreakdown : void 0,
76722
+ activeSkuId,
76723
+ liveSkuId: isHistoricView ? null : liveSkuId
76259
76724
  }
76260
76725
  ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: clsx("flex min-h-0", desktopBottomSectionClass), children: /* @__PURE__ */ jsxRuntime.jsx(
76261
76726
  WorkspaceMetricCards,
@@ -76265,7 +76730,8 @@ var WorkspaceDetailView = ({
76265
76730
  className: "flex-1",
76266
76731
  skuAware: isSkuAware,
76267
76732
  skuBreakdown: isSkuAware ? realSkuBreakdown : void 0,
76268
- activeSkuId
76733
+ activeSkuId,
76734
+ liveSkuId: isHistoricView ? null : liveSkuId
76269
76735
  }
76270
76736
  ) })
76271
76737
  ] })
@@ -85553,6 +86019,7 @@ exports.authRateLimitService = authRateLimitService;
85553
86019
  exports.awardsService = awardsService;
85554
86020
  exports.buildDateKey = buildDateKey;
85555
86021
  exports.buildKPIsFromLineMetricsRow = buildKPIsFromLineMetricsRow;
86022
+ exports.buildKpiLineHierarchy = buildKpiLineHierarchy;
85556
86023
  exports.buildLineSkuBreakdown = buildLineSkuBreakdown;
85557
86024
  exports.buildShiftGroupsKey = buildShiftGroupsKey;
85558
86025
  exports.canRoleAccessDashboardPath = canRoleAccessDashboardPath;