@optifye/dashboard-core 6.12.49 → 6.12.50

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
@@ -13820,12 +13820,22 @@ var stripSeconds = (timeStr) => {
13820
13820
  if (!timeStr) return timeStr;
13821
13821
  return timeStr.substring(0, 5);
13822
13822
  };
13823
+ var normalizeOffDays = (value) => {
13824
+ if (!value) return [];
13825
+ if (Array.isArray(value)) {
13826
+ return value.map((day) => String(day || "").trim().toLowerCase()).filter(Boolean);
13827
+ }
13828
+ if (Array.isArray(value.off_days)) return normalizeOffDays(value.off_days);
13829
+ if (Array.isArray(value.offDays)) return normalizeOffDays(value.offDays);
13830
+ return [];
13831
+ };
13823
13832
  var buildShiftConfigFromOperatingHoursRows = (rows, fallback) => {
13824
13833
  const mapped = (rows || []).map((row) => ({
13825
13834
  shiftId: row.shift_id,
13826
13835
  shiftName: row.shift_name || `Shift ${row.shift_id}`,
13827
13836
  startTime: stripSeconds(row.start_time),
13828
13837
  endTime: stripSeconds(row.end_time),
13838
+ offDays: normalizeOffDays(row.off_days),
13829
13839
  breaks: (() => {
13830
13840
  const raw = Array.isArray(row.breaks) ? row.breaks : Array.isArray(row.breaks?.breaks) ? row.breaks.breaks : [];
13831
13841
  return raw.map((b) => ({
@@ -13885,7 +13895,7 @@ var fetchAndStoreShiftConfig = async (supabase, lineId, fallback) => {
13885
13895
  if (existing) return existing;
13886
13896
  const promise = (async () => {
13887
13897
  try {
13888
- const { data, error } = await supabase.from("line_operating_hours").select("line_id, shift_id, shift_name, start_time, end_time, breaks, timezone").eq("line_id", lineId);
13898
+ const { data, error } = await supabase.from("line_operating_hours").select("line_id, shift_id, shift_name, start_time, end_time, breaks, timezone, off_days").eq("line_id", lineId);
13889
13899
  if (error) {
13890
13900
  throw new Error(`Failed to fetch shift config: ${error.message}`);
13891
13901
  }
@@ -14578,7 +14588,7 @@ var useMultiLineShiftConfigs = (lineIds, fallbackConfig) => {
14578
14588
  setError(null);
14579
14589
  }
14580
14590
  console.log(`[useMultiLineShiftConfigs] Fetching shift configs for ${missingLineIds.length} lines`);
14581
- const { data, error: fetchError } = await supabase.from("line_operating_hours").select("line_id, shift_id, shift_name, start_time, end_time, breaks, timezone").in("line_id", missingLineIds);
14591
+ const { data, error: fetchError } = await supabase.from("line_operating_hours").select("line_id, shift_id, shift_name, start_time, end_time, breaks, timezone, off_days").in("line_id", missingLineIds);
14582
14592
  if (fetchError) {
14583
14593
  console.error("[useMultiLineShiftConfigs] Error fetching shift configs:", fetchError);
14584
14594
  throw new Error(`Failed to fetch shift configs: ${fetchError.message}`);
@@ -37808,7 +37818,12 @@ var HourlyOutputChartComponent = ({
37808
37818
  }, [idleBarState.visible, idleBarState.key, idleBarState.shouldAnimate]);
37809
37819
  const maxDataValue = Math.max(...data, 0);
37810
37820
  const numericChartTargets = chartData.map((d) => d.target).filter((target) => target !== null && Number.isFinite(target));
37811
- const maxTargetValue = Math.max(...numericChartTargets, pphThreshold, 0);
37821
+ const hasAuthoritativeNumericTargets = hasHourlyTargetOutputProp && numericChartTargets.length > 0;
37822
+ const maxTargetValue = Math.max(
37823
+ ...numericChartTargets,
37824
+ hasAuthoritativeNumericTargets ? 0 : pphThreshold,
37825
+ 0
37826
+ );
37812
37827
  const maxYValue = Math.max(
37813
37828
  Math.ceil(maxTargetValue * 1.5),
37814
37829
  Math.ceil(maxDataValue * 1.15)
@@ -37963,14 +37978,9 @@ var HourlyOutputChartComponent = ({
37963
37978
  return /* @__PURE__ */ jsxRuntime.jsx("g", { children: lines });
37964
37979
  }, [hourlyTargetSegments, targetTimelineSegments, SHIFT_DURATION, pphThreshold, targetLineEndOffset, hasHourlyTargetOutputProp]);
37965
37980
  const renderLegend = () => {
37966
- const uniqueTargets = [...new Set(
37967
- chartData.map((d) => d.target).filter((target) => target !== null && Number.isFinite(target)).map((target) => Math.round(target))
37968
- )].sort((a, b) => a - b);
37969
- const unitLabel = hasHourlyTargetOutputProp ? "units" : "units/hr";
37970
- const targetText = uniqueTargets.length === 0 ? `Target` : uniqueTargets.length === 1 ? `Target: ${uniqueTargets[0]} ${unitLabel}` : `Target: ${uniqueTargets[0]} - ${uniqueTargets[uniqueTargets.length - 1]} ${unitLabel}`;
37971
37981
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center text-sm text-gray-600 bg-white py-1", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 border border-gray-100 rounded-full px-3 py-1", children: [
37972
37982
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-8 flex items-center", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full border-t-2 border-[#E34329] border-dashed" }) }),
37973
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: targetText })
37983
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Target" })
37974
37984
  ] }) });
37975
37985
  };
37976
37986
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -52488,6 +52498,106 @@ var getOrdinal = (n) => {
52488
52498
  const v = n % 100;
52489
52499
  return n + (suffix[(v - 20) % 10] || suffix[v] || suffix[0]);
52490
52500
  };
52501
+ var resolveDailyTargetOutput = (shiftData) => {
52502
+ if (!shiftData || shiftData.hasData === false) return 0;
52503
+ const target = Number(shiftData?.targetOutput || shiftData?.idealOutput || 0);
52504
+ return Number.isFinite(target) && target > 0 ? target : 0;
52505
+ };
52506
+ var getUniqueRoundedTargets = (data) => Array.from(new Set(
52507
+ data.map((entry) => Number(entry.targetOutput || 0)).filter((target) => Number.isFinite(target) && target > 0).map((target) => Math.round(target))
52508
+ )).sort((a, b) => a - b);
52509
+ var formatDailyTargetLegend = (targets) => {
52510
+ if (targets.length === 0) return "";
52511
+ if (targets.length === 1) return `Target: ${targets[0].toLocaleString()} units/day`;
52512
+ return `Target: ${targets[0].toLocaleString()} - ${targets[targets.length - 1].toLocaleString()} units/day`;
52513
+ };
52514
+ var WEEKDAY_NAMES = ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"];
52515
+ var getShiftOffDays = (shiftConfig, selectedShiftId) => {
52516
+ const shift = shiftConfig?.shifts?.find((candidate) => candidate.shiftId === selectedShiftId);
52517
+ const raw = shift?.offDays || shift?.off_days || [];
52518
+ return Array.isArray(raw) ? raw.map((day) => String(day || "").trim().toLowerCase()).filter(Boolean) : [];
52519
+ };
52520
+ var isScheduledOffDay = (dateKey, shiftConfig, selectedShiftId) => {
52521
+ const offDays = getShiftOffDays(shiftConfig, selectedShiftId);
52522
+ if (offDays.length === 0) return false;
52523
+ const date = parseDateKeyToDate(dateKey);
52524
+ return offDays.includes(WEEKDAY_NAMES[date.getDay()]);
52525
+ };
52526
+ var renderDailyOutputCapsules = (props, data) => {
52527
+ const { offset, yAxisMap } = props;
52528
+ if (!offset || !yAxisMap || data.length === 0) return null;
52529
+ const { left, width } = offset;
52530
+ const yAxis = yAxisMap.default || yAxisMap[0];
52531
+ if (!Number.isFinite(left) || !Number.isFinite(width) || width <= 0 || !yAxis?.scale) {
52532
+ return null;
52533
+ }
52534
+ const slotWidth = width / data.length;
52535
+ const capsuleWidth = Math.max(10, Math.min(28, slotWidth * 0.44));
52536
+ const radius = capsuleWidth / 2;
52537
+ const baseY = yAxis.scale(0);
52538
+ const capsules = [];
52539
+ data.forEach((entry, index) => {
52540
+ const target = Number(entry.targetOutput || 0);
52541
+ const output = Number(entry.output || 0);
52542
+ if ((!Number.isFinite(target) || target <= 0) && (!Number.isFinite(output) || output <= 0)) return;
52543
+ const x = left + index * slotWidth + (slotWidth - capsuleWidth) / 2;
52544
+ const targetTop = target > 0 ? yAxis.scale(target) : yAxis.scale(output);
52545
+ const capsuleTop = Math.min(targetTop, baseY - 4);
52546
+ const capsuleHeight = Math.max(baseY - capsuleTop, 4);
52547
+ const fillValue = target > 0 ? Math.min(Math.max(output, 0), target) : Math.max(output, 0);
52548
+ const fillTop = fillValue > 0 ? Math.max(yAxis.scale(fillValue), capsuleTop) : baseY;
52549
+ const fillHeight = Math.max(baseY - fillTop, 0);
52550
+ const fillColor = target > 0 ? output >= target ? "#00AB45" : "#E34329" : entry.color || "#6b7280";
52551
+ const trackFill = output >= target ? "#f0fdf4" : "#fff5f3";
52552
+ const trackStroke = output >= target ? "#00AB45" : "#E34329";
52553
+ const clipId = `line-daily-output-capsule-${index}`;
52554
+ capsules.push(
52555
+ /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
52556
+ /* @__PURE__ */ jsxRuntime.jsx("defs", { children: /* @__PURE__ */ jsxRuntime.jsx("clipPath", { id: clipId, children: /* @__PURE__ */ jsxRuntime.jsx(
52557
+ "rect",
52558
+ {
52559
+ x,
52560
+ y: capsuleTop,
52561
+ width: capsuleWidth,
52562
+ height: capsuleHeight,
52563
+ rx: radius,
52564
+ ry: radius
52565
+ }
52566
+ ) }) }),
52567
+ target > 0 && /* @__PURE__ */ jsxRuntime.jsx(
52568
+ "rect",
52569
+ {
52570
+ "data-testid": "daily-target-capsule-track",
52571
+ x,
52572
+ y: capsuleTop,
52573
+ width: capsuleWidth,
52574
+ height: capsuleHeight,
52575
+ rx: radius,
52576
+ ry: radius,
52577
+ fill: trackFill,
52578
+ stroke: trackStroke,
52579
+ strokeWidth: 1.5
52580
+ }
52581
+ ),
52582
+ /* @__PURE__ */ jsxRuntime.jsx(
52583
+ "rect",
52584
+ {
52585
+ "data-testid": "daily-output-capsule-fill",
52586
+ x,
52587
+ y: fillTop,
52588
+ width: capsuleWidth,
52589
+ height: fillHeight,
52590
+ rx: fillHeight >= capsuleHeight ? radius : 0,
52591
+ ry: fillHeight >= capsuleHeight ? radius : 0,
52592
+ fill: fillColor,
52593
+ clipPath: target > 0 ? `url(#${clipId})` : void 0
52594
+ }
52595
+ )
52596
+ ] }, `daily-output-capsule-${index}`)
52597
+ );
52598
+ });
52599
+ return capsules.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("g", { children: capsules }) : null;
52600
+ };
52491
52601
  var CustomTooltip2 = ({ active, payload, label, isUptimeMode }) => {
52492
52602
  if (!active || !payload || payload.length === 0) return null;
52493
52603
  if (isUptimeMode) {
@@ -52571,6 +52681,7 @@ var LineMonthlyHistory = ({
52571
52681
  legend,
52572
52682
  monitoringMode,
52573
52683
  lineAssembly = false,
52684
+ shiftConfig,
52574
52685
  underperformingWorkspaces = {},
52575
52686
  lineId,
52576
52687
  selectedShiftId = 0,
@@ -52588,6 +52699,13 @@ var LineMonthlyHistory = ({
52588
52699
  const { isIdleTimeVlmEnabled } = useIdleTimeVlmConfig();
52589
52700
  const idleTimeVlmEnabled = isIdleTimeVlmEnabled(lineId);
52590
52701
  const isUptimeMode = monitoringMode === "uptime";
52702
+ const [isMobile, setIsMobile] = React125.useState(false);
52703
+ React125.useEffect(() => {
52704
+ const checkMobile = () => setIsMobile(window.innerWidth < 640);
52705
+ checkMobile();
52706
+ window.addEventListener("resize", checkMobile);
52707
+ return () => window.removeEventListener("resize", checkMobile);
52708
+ }, []);
52591
52709
  const chartKey = React125.useMemo(() => `${lineId}-${month}-${year}-${selectedShiftId}-${rangeStart}-${rangeEnd}`, [lineId, month, year, selectedShiftId, rangeStart, rangeEnd]);
52592
52710
  const monthBounds = React125.useMemo(() => getMonthKeyBounds(year, month), [year, month]);
52593
52711
  const normalizedRange = React125.useMemo(() => {
@@ -52749,29 +52867,21 @@ var LineMonthlyHistory = ({
52749
52867
  });
52750
52868
  }
52751
52869
  const yAxisMax2 = maxHours > 0 ? 100 : 1;
52752
- return { data: dailyData2, maxOutput: 0, lastSetTarget: 0, yAxisMax: yAxisMax2 };
52870
+ return { data: dailyData2, maxOutput: 0, targetValues: [], yAxisMax: yAxisMax2, targetLegend: "" };
52753
52871
  }
52754
52872
  const dailyData = [];
52755
52873
  let maxOutput = 0;
52756
- let lastSetTarget = 0;
52757
- for (let i = rangeDateKeys.length - 1; i >= 0; i--) {
52758
- const dayKey = rangeDateKeys[i];
52759
- const dayData = analysisMonthlyDataByKey.get(dayKey);
52760
- const shiftData = dayData ? getShiftData2(dayData, selectedShiftId) : null;
52761
- const idealOutput = shiftData ? shiftData.idealOutput || 0 : 0;
52762
- if (idealOutput > 0) {
52763
- lastSetTarget = idealOutput;
52764
- break;
52765
- }
52766
- }
52874
+ let maxTarget = 0;
52767
52875
  for (const dayKey of rangeDateKeys) {
52768
52876
  const day = Number(dayKey.slice(-2));
52769
52877
  const dayData = analysisMonthlyDataByKey.get(dayKey);
52878
+ const isOffDay = isScheduledOffDay(dayKey, shiftConfig, selectedShiftId);
52770
52879
  const shiftData = dayData ? getShiftData2(dayData, selectedShiftId) : null;
52771
- const output = shiftData && hasRealData(shiftData) ? shiftData.output || 0 : 0;
52772
- const idealOutput = shiftData ? shiftData.idealOutput || 0 : 0;
52880
+ const output = !isOffDay && shiftData && hasRealData(shiftData) ? shiftData.output || 0 : 0;
52881
+ const targetOutput = isOffDay ? 0 : resolveDailyTargetOutput(shiftData);
52773
52882
  if (output > maxOutput) maxOutput = output;
52774
- const color2 = output >= lastSetTarget ? "#00AB45" : "#E34329";
52883
+ if (targetOutput > maxTarget) maxTarget = targetOutput;
52884
+ const color2 = targetOutput > 0 && output >= targetOutput ? "#00AB45" : "#E34329";
52775
52885
  dailyData.push({
52776
52886
  hour: getOrdinal(day),
52777
52887
  // Using ordinal format (1st, 2nd, 3rd, etc.)
@@ -52779,17 +52889,25 @@ var LineMonthlyHistory = ({
52779
52889
  output,
52780
52890
  originalOutput: output,
52781
52891
  // For label display
52782
- idealOutput,
52892
+ idealOutput: targetOutput,
52893
+ targetOutput,
52783
52894
  color: color2
52784
52895
  });
52785
52896
  }
52786
- const calculatedMax = Math.max(maxOutput, lastSetTarget);
52897
+ const calculatedMax = Math.max(maxOutput, maxTarget);
52787
52898
  const yAxisMax = calculatedMax > 0 ? calculatedMax * 1.1 : 100;
52788
- return { data: dailyData, maxOutput, lastSetTarget, yAxisMax };
52789
- }, [analysisMonthlyDataByKey, normalizedRange.endKey, normalizedRange.startKey, selectedShiftId, isUptimeMode, timezone]);
52899
+ const targetValues = getUniqueRoundedTargets(dailyData);
52900
+ return {
52901
+ data: dailyData,
52902
+ maxOutput,
52903
+ targetValues,
52904
+ yAxisMax,
52905
+ targetLegend: formatDailyTargetLegend(targetValues)
52906
+ };
52907
+ }, [analysisMonthlyDataByKey, normalizedRange.endKey, normalizedRange.startKey, selectedShiftId, isUptimeMode, timezone, shiftConfig]);
52790
52908
  const yAxisTicks = React125.useMemo(() => {
52791
52909
  const max = chartData.yAxisMax;
52792
- const target = chartData.lastSetTarget;
52910
+ const targets = chartData.targetValues || [];
52793
52911
  if (!max || max <= 0) return void 0;
52794
52912
  const desiredIntervals = 4;
52795
52913
  const roughStep = max / desiredIntervals;
@@ -52803,11 +52921,18 @@ var LineMonthlyHistory = ({
52803
52921
  for (let v = 0; v <= max; v += step) {
52804
52922
  ticks.push(Math.round(v));
52805
52923
  }
52806
- if (target > 0) {
52807
- ticks.push(Math.round(target));
52808
- }
52924
+ targets.forEach((target) => ticks.push(Math.round(target)));
52809
52925
  return Array.from(new Set(ticks)).filter((v) => v >= 0 && v <= max).sort((a, b) => a - b);
52810
- }, [chartData.yAxisMax, chartData.lastSetTarget]);
52926
+ }, [chartData.yAxisMax, chartData.targetValues]);
52927
+ const visibleYAxisTicks = React125.useMemo(() => {
52928
+ if (!isMobile || isUptimeMode || !yAxisTicks || yAxisTicks.length <= 3) return yAxisTicks;
52929
+ const importantTicks = /* @__PURE__ */ new Set([
52930
+ 0,
52931
+ Math.round(chartData.yAxisMax),
52932
+ ...chartData.targetValues || []
52933
+ ]);
52934
+ return yAxisTicks.filter((tick) => importantTicks.has(Math.round(tick))).sort((a, b) => a - b).slice(-3);
52935
+ }, [chartData.targetValues, chartData.yAxisMax, isMobile, isUptimeMode, yAxisTicks]);
52811
52936
  const pieChartData = React125.useMemo(() => {
52812
52937
  if (!isUptimeMode) return [];
52813
52938
  const validShifts = (analysisMonthlyData || []).map((day) => getShiftData2(day, selectedShiftId)).filter(
@@ -53126,36 +53251,35 @@ var LineMonthlyHistory = ({
53126
53251
  ] }),
53127
53252
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-lg sm:rounded-xl shadow-sm border border-gray-100 p-2 sm:p-3 lg:p-4", children: [
53128
53253
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-xs sm:text-sm font-bold text-gray-700 mb-1 sm:mb-2 text-left", children: isUptimeMode ? "Daily Utilization" : "Daily Output" }),
53129
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[160px] sm:h-[180px] lg:h-[220px]", children: /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(
53254
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[140px] sm:h-[180px] lg:h-[220px]", children: /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(
53130
53255
  recharts.BarChart,
53131
53256
  {
53132
53257
  data: chartData.data,
53133
- margin: { top: 20, right: 10, bottom: 40, left: 10 },
53258
+ margin: isMobile ? { top: 8, right: 4, bottom: 28, left: 0 } : { top: 20, right: 10, bottom: 40, left: 10 },
53134
53259
  children: [
53135
53260
  /* @__PURE__ */ jsxRuntime.jsx(recharts.CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#f3f4f6" }),
53136
53261
  /* @__PURE__ */ jsxRuntime.jsx(
53137
53262
  recharts.XAxis,
53138
53263
  {
53139
53264
  dataKey: "hour",
53140
- tick: { fontSize: 10, fill: "#6b7280" },
53141
- interval: 0,
53265
+ tick: { fontSize: isMobile ? 9 : 10, fill: "#6b7280" },
53266
+ interval: isMobile ? 3 : 0,
53142
53267
  angle: -45,
53143
53268
  textAnchor: "end",
53144
- height: 60
53269
+ height: isMobile ? 42 : 60
53145
53270
  }
53146
53271
  ),
53147
53272
  /* @__PURE__ */ jsxRuntime.jsx(
53148
53273
  recharts.YAxis,
53149
53274
  {
53150
53275
  domain: [0, chartData.yAxisMax],
53151
- width: 40,
53152
- ticks: isUptimeMode ? [0, 25, 50, 75, 100] : yAxisTicks,
53276
+ width: isMobile ? 34 : 40,
53277
+ ticks: isUptimeMode ? [0, 25, 50, 75, 100] : visibleYAxisTicks,
53153
53278
  tickFormatter: isUptimeMode ? (value) => `${value}%` : void 0,
53154
53279
  tick: isUptimeMode ? void 0 : (props) => {
53155
53280
  const { x, y, payload } = props;
53156
53281
  const value = Math.round(payload.value);
53157
- const targetValue = Math.round(chartData.lastSetTarget);
53158
- const isTarget = value === targetValue && targetValue > 0;
53282
+ const isTarget = (chartData.targetValues || []).includes(value);
53159
53283
  return /* @__PURE__ */ jsxRuntime.jsx(
53160
53284
  "text",
53161
53285
  {
@@ -53178,15 +53302,7 @@ var LineMonthlyHistory = ({
53178
53302
  content: (props) => /* @__PURE__ */ jsxRuntime.jsx(CustomTooltip2, { ...props, isUptimeMode })
53179
53303
  }
53180
53304
  ),
53181
- !isUptimeMode && chartData.lastSetTarget > 0 && /* @__PURE__ */ jsxRuntime.jsx(
53182
- recharts.ReferenceLine,
53183
- {
53184
- y: chartData.lastSetTarget,
53185
- stroke: "#E34329",
53186
- strokeDasharray: "5 5",
53187
- strokeWidth: 2
53188
- }
53189
- ),
53305
+ !isUptimeMode && /* @__PURE__ */ jsxRuntime.jsx(recharts.Customized, { component: (props) => renderDailyOutputCapsules(props, chartData.data) }),
53190
53306
  isUptimeMode ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
53191
53307
  /* @__PURE__ */ jsxRuntime.jsx(
53192
53308
  recharts.Bar,
@@ -53219,6 +53335,8 @@ var LineMonthlyHistory = ({
53219
53335
  {
53220
53336
  dataKey: "output",
53221
53337
  radius: [4, 4, 0, 0],
53338
+ fill: "transparent",
53339
+ opacity: 0,
53222
53340
  isAnimationActive: true,
53223
53341
  animationBegin: 0,
53224
53342
  animationDuration: 1e3,
@@ -53237,13 +53355,9 @@ var LineMonthlyHistory = ({
53237
53355
  },
53238
53356
  chartKey
53239
53357
  ) }) }),
53240
- !isUptimeMode && chartData.lastSetTarget > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center text-sm text-gray-600 bg-white py-1 pt-2", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 border border-gray-100 rounded-full px-3 py-1", children: [
53241
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-8 flex items-center", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full border-t-2 border-[#E34329] border-dashed" }) }),
53242
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
53243
- "Target: ",
53244
- Math.round(chartData.lastSetTarget),
53245
- " units/day"
53246
- ] })
53358
+ !isUptimeMode && chartData.targetValues.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center text-sm text-gray-600 bg-white py-1 pt-2", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 border border-gray-100 rounded-full px-3 py-1", children: [
53359
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative h-5 w-3 rounded-full border border-[#E34329] bg-[#fff5f3] overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-x-0 bottom-0 h-1/2 bg-[#E34329]" }) }),
53360
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: chartData.targetLegend })
53247
53361
  ] }) })
53248
53362
  ] })
53249
53363
  ] })
@@ -55166,6 +55280,106 @@ var formatCycleSeconds = (value) => {
55166
55280
  if (!Number.isFinite(value)) return "0.0s";
55167
55281
  return `${value.toFixed(1)}s`;
55168
55282
  };
55283
+ var resolveDailyTargetOutput2 = (shiftData) => {
55284
+ if (!shiftData || shiftData.hasData === false) return 0;
55285
+ const target = Number(shiftData?.targetOutput || shiftData?.idealOutput || 0);
55286
+ return Number.isFinite(target) && target > 0 ? target : 0;
55287
+ };
55288
+ var getUniqueRoundedTargets2 = (data) => Array.from(new Set(
55289
+ data.map((entry) => Number(entry.targetOutput || 0)).filter((target) => Number.isFinite(target) && target > 0).map((target) => Math.round(target))
55290
+ )).sort((a, b) => a - b);
55291
+ var formatDailyTargetLegend2 = (targets) => {
55292
+ if (targets.length === 0) return "";
55293
+ if (targets.length === 1) return `Target: ${targets[0].toLocaleString()} units/day`;
55294
+ return `Target: ${targets[0].toLocaleString()} - ${targets[targets.length - 1].toLocaleString()} units/day`;
55295
+ };
55296
+ var WEEKDAY_NAMES2 = ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"];
55297
+ var getShiftOffDays2 = (shiftConfig, selectedShiftId) => {
55298
+ const shift = shiftConfig?.shifts?.find((candidate) => candidate.shiftId === selectedShiftId);
55299
+ const raw = shift?.offDays || shift?.off_days || [];
55300
+ return Array.isArray(raw) ? raw.map((day) => String(day || "").trim().toLowerCase()).filter(Boolean) : [];
55301
+ };
55302
+ var isScheduledOffDay2 = (dateKey, shiftConfig, selectedShiftId) => {
55303
+ const offDays = getShiftOffDays2(shiftConfig, selectedShiftId);
55304
+ if (offDays.length === 0) return false;
55305
+ const date = parseDateKeyToDate(dateKey);
55306
+ return offDays.includes(WEEKDAY_NAMES2[date.getDay()]);
55307
+ };
55308
+ var renderDailyOutputCapsules2 = (props, data) => {
55309
+ const { offset, yAxisMap } = props;
55310
+ if (!offset || !yAxisMap || data.length === 0) return null;
55311
+ const { left, width } = offset;
55312
+ const yAxis = yAxisMap.default || yAxisMap[0];
55313
+ if (!Number.isFinite(left) || !Number.isFinite(width) || width <= 0 || !yAxis?.scale) {
55314
+ return null;
55315
+ }
55316
+ const slotWidth = width / data.length;
55317
+ const capsuleWidth = Math.max(10, Math.min(28, slotWidth * 0.44));
55318
+ const radius = capsuleWidth / 2;
55319
+ const baseY = yAxis.scale(0);
55320
+ const capsules = [];
55321
+ data.forEach((entry, index) => {
55322
+ const target = Number(entry.targetOutput || 0);
55323
+ const output = Number(entry.output || 0);
55324
+ if ((!Number.isFinite(target) || target <= 0) && (!Number.isFinite(output) || output <= 0)) return;
55325
+ const x = left + index * slotWidth + (slotWidth - capsuleWidth) / 2;
55326
+ const targetTop = target > 0 ? yAxis.scale(target) : yAxis.scale(output);
55327
+ const capsuleTop = Math.min(targetTop, baseY - 4);
55328
+ const capsuleHeight = Math.max(baseY - capsuleTop, 4);
55329
+ const fillValue = target > 0 ? Math.min(Math.max(output, 0), target) : Math.max(output, 0);
55330
+ const fillTop = fillValue > 0 ? Math.max(yAxis.scale(fillValue), capsuleTop) : baseY;
55331
+ const fillHeight = Math.max(baseY - fillTop, 0);
55332
+ const fillColor = target > 0 ? output >= target ? "#00AB45" : "#E34329" : entry.color || "#6b7280";
55333
+ const trackFill = output >= target ? "#f0fdf4" : "#fff5f3";
55334
+ const trackStroke = output >= target ? "#00AB45" : "#E34329";
55335
+ const clipId = `workspace-daily-output-capsule-${index}`;
55336
+ capsules.push(
55337
+ /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
55338
+ /* @__PURE__ */ jsxRuntime.jsx("defs", { children: /* @__PURE__ */ jsxRuntime.jsx("clipPath", { id: clipId, children: /* @__PURE__ */ jsxRuntime.jsx(
55339
+ "rect",
55340
+ {
55341
+ x,
55342
+ y: capsuleTop,
55343
+ width: capsuleWidth,
55344
+ height: capsuleHeight,
55345
+ rx: radius,
55346
+ ry: radius
55347
+ }
55348
+ ) }) }),
55349
+ target > 0 && /* @__PURE__ */ jsxRuntime.jsx(
55350
+ "rect",
55351
+ {
55352
+ "data-testid": "daily-target-capsule-track",
55353
+ x,
55354
+ y: capsuleTop,
55355
+ width: capsuleWidth,
55356
+ height: capsuleHeight,
55357
+ rx: radius,
55358
+ ry: radius,
55359
+ fill: trackFill,
55360
+ stroke: trackStroke,
55361
+ strokeWidth: 1.5
55362
+ }
55363
+ ),
55364
+ /* @__PURE__ */ jsxRuntime.jsx(
55365
+ "rect",
55366
+ {
55367
+ "data-testid": "daily-output-capsule-fill",
55368
+ x,
55369
+ y: fillTop,
55370
+ width: capsuleWidth,
55371
+ height: fillHeight,
55372
+ rx: fillHeight >= capsuleHeight ? radius : 0,
55373
+ ry: fillHeight >= capsuleHeight ? radius : 0,
55374
+ fill: fillColor,
55375
+ clipPath: target > 0 ? `url(#${clipId})` : void 0
55376
+ }
55377
+ )
55378
+ ] }, `daily-output-capsule-${index}`)
55379
+ );
55380
+ });
55381
+ return capsules.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("g", { children: capsules }) : null;
55382
+ };
55169
55383
  var CustomTooltip3 = ({ active, payload, label, isUptimeMode }) => {
55170
55384
  if (!active || !payload || payload.length === 0) return null;
55171
55385
  if (isUptimeMode) {
@@ -55345,28 +55559,20 @@ var WorkspaceMonthlyHistory = ({
55345
55559
  });
55346
55560
  }
55347
55561
  const yAxisMax2 = maxHours > 0 ? maxHours * 1.1 : 1;
55348
- return { data: dailyData, yAxisMax: yAxisMax2, lastSetTarget: 0 };
55562
+ return { data: dailyData, yAxisMax: yAxisMax2, targetValues: [], targetLegend: "" };
55349
55563
  }
55350
55564
  let maxOutput = 0;
55351
- let lastSetTarget = 0;
55352
- for (let i = rangeDateKeys.length - 1; i >= 0; i--) {
55353
- const dateKey = rangeDateKeys[i];
55354
- const dayData = analysisMonthlyDataByKey.get(dateKey);
55355
- const shiftData = dayData ? getShiftData(dayData, selectedShiftId) : null;
55356
- const idealOutput = shiftData ? shiftData.idealOutput : 0;
55357
- if (idealOutput > 0) {
55358
- lastSetTarget = idealOutput;
55359
- break;
55360
- }
55361
- }
55565
+ let maxTarget = 0;
55362
55566
  for (const dateKey of rangeDateKeys) {
55363
55567
  const dayData = analysisMonthlyDataByKey.get(dateKey);
55364
55568
  const dayNumber = Number(dateKey.slice(-2));
55569
+ const isOffDay = isScheduledOffDay2(dateKey, shiftConfig, selectedShiftId);
55365
55570
  const shiftData = dayData ? getShiftData(dayData, selectedShiftId) : null;
55366
- const output = shiftData && hasRealData(shiftData) ? shiftData.output : 0;
55367
- const idealOutput = shiftData ? shiftData.idealOutput : 0;
55571
+ const output = !isOffDay && shiftData && hasRealData(shiftData) ? shiftData.output : 0;
55572
+ const targetOutput = isOffDay ? 0 : resolveDailyTargetOutput2(shiftData);
55368
55573
  if (output > maxOutput) maxOutput = output;
55369
- const color2 = output >= lastSetTarget ? "#00AB45" : "#E34329";
55574
+ if (targetOutput > maxTarget) maxTarget = targetOutput;
55575
+ const color2 = targetOutput > 0 && output >= targetOutput ? "#00AB45" : "#E34329";
55370
55576
  dailyData.push({
55371
55577
  hour: getOrdinal2(dayNumber),
55372
55578
  // Using ordinal format (1st, 2nd, 3rd, etc.)
@@ -55374,21 +55580,29 @@ var WorkspaceMonthlyHistory = ({
55374
55580
  output,
55375
55581
  originalOutput: output,
55376
55582
  // For label display
55377
- idealOutput,
55583
+ idealOutput: targetOutput,
55584
+ targetOutput,
55378
55585
  efficiency: shiftData && hasRealData(shiftData) ? shiftData.efficiency : 0,
55379
55586
  color: color2,
55380
55587
  idleMinutes: 0
55381
55588
  // Not used but keeps structure consistent
55382
55589
  });
55383
55590
  }
55384
- const calculatedMax = Math.max(maxOutput, lastSetTarget);
55591
+ const calculatedMax = Math.max(maxOutput, maxTarget);
55385
55592
  const yAxisMax = calculatedMax > 0 ? calculatedMax * 1.1 : 100;
55386
- return { data: dailyData, maxOutput, lastSetTarget, yAxisMax };
55387
- }, [analysisMonthlyDataByKey, rangeDateKeys, selectedShiftId, isUptimeMode, shiftWorkSeconds]);
55593
+ const targetValues = getUniqueRoundedTargets2(dailyData);
55594
+ return {
55595
+ data: dailyData,
55596
+ maxOutput,
55597
+ targetValues,
55598
+ yAxisMax,
55599
+ targetLegend: formatDailyTargetLegend2(targetValues)
55600
+ };
55601
+ }, [analysisMonthlyDataByKey, rangeDateKeys, selectedShiftId, isUptimeMode, shiftConfig, shiftWorkSeconds]);
55388
55602
  const yAxisTicks = React125.useMemo(() => {
55389
55603
  if (isUptimeMode) return void 0;
55390
55604
  const max = chartData.yAxisMax;
55391
- const target = chartData.lastSetTarget;
55605
+ const targets = chartData.targetValues || [];
55392
55606
  if (!max || max <= 0) return void 0;
55393
55607
  const desiredIntervals = 4;
55394
55608
  const roughStep = max / desiredIntervals;
@@ -55402,7 +55616,7 @@ var WorkspaceMonthlyHistory = ({
55402
55616
  for (let v = 0; v <= max; v += step) {
55403
55617
  ticks.push(Math.round(v));
55404
55618
  }
55405
- if (target > 0) {
55619
+ targets.forEach((target) => {
55406
55620
  const roundedTarget = Math.round(target);
55407
55621
  if (!ticks.includes(roundedTarget)) {
55408
55622
  let nearestIndex = -1;
@@ -55421,9 +55635,18 @@ var WorkspaceMonthlyHistory = ({
55421
55635
  ticks.push(roundedTarget);
55422
55636
  }
55423
55637
  }
55424
- }
55638
+ });
55425
55639
  return ticks.filter((v) => v >= 0 && v <= max * 1.05).sort((a, b) => a - b);
55426
- }, [chartData.yAxisMax, chartData.lastSetTarget, isUptimeMode]);
55640
+ }, [chartData.yAxisMax, chartData.targetValues, isUptimeMode]);
55641
+ const visibleYAxisTicks = React125.useMemo(() => {
55642
+ if (!isMobile || isUptimeMode || !yAxisTicks || yAxisTicks.length <= 3) return yAxisTicks;
55643
+ const importantTicks = /* @__PURE__ */ new Set([
55644
+ 0,
55645
+ Math.round(chartData.yAxisMax),
55646
+ ...chartData.targetValues || []
55647
+ ]);
55648
+ return yAxisTicks.filter((tick) => importantTicks.has(Math.round(tick))).sort((a, b) => a - b).slice(-3);
55649
+ }, [chartData.targetValues, chartData.yAxisMax, isMobile, isUptimeMode, yAxisTicks]);
55427
55650
  const pieChartData = React125.useMemo(() => {
55428
55651
  const aggregateMode = isUptimeMode ? "uptime" : "output";
55429
55652
  const validShifts = analysisMonthlyData.map((d) => getShiftData(d, selectedShiftId)).filter(
@@ -55813,22 +56036,22 @@ var WorkspaceMonthlyHistory = ({
55813
56036
  ] }),
55814
56037
  (!isAssemblyWorkspace || isUptimeMode) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-lg shadow-sm border border-gray-100 p-4 flex-1", children: [
55815
56038
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-bold text-gray-700 mb-3 text-left", children: isUptimeMode ? "Daily Utilization" : "Daily Output" }),
55816
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { height: "220px" }, children: /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(
56039
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { height: isMobile ? "150px" : "220px" }, children: /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(
55817
56040
  recharts.BarChart,
55818
56041
  {
55819
56042
  data: chartData.data,
55820
- margin: { top: 20, right: 10, bottom: 40, left: 10 },
56043
+ margin: isMobile ? { top: 8, right: 4, bottom: 28, left: 0 } : { top: 20, right: 10, bottom: 40, left: 10 },
55821
56044
  children: [
55822
56045
  /* @__PURE__ */ jsxRuntime.jsx(recharts.CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#f3f4f6" }),
55823
56046
  /* @__PURE__ */ jsxRuntime.jsx(
55824
56047
  recharts.XAxis,
55825
56048
  {
55826
56049
  dataKey: "hour",
55827
- tick: { fontSize: 10, fill: "#6b7280" },
55828
- interval: isMobile ? "preserveStartEnd" : 0,
56050
+ tick: { fontSize: isMobile ? 9 : 10, fill: "#6b7280" },
56051
+ interval: isMobile ? 3 : 0,
55829
56052
  angle: -45,
55830
56053
  textAnchor: "end",
55831
- height: 60
56054
+ height: isMobile ? 42 : 60
55832
56055
  }
55833
56056
  ),
55834
56057
  isUptimeMode ? /* @__PURE__ */ jsxRuntime.jsx(
@@ -55843,13 +56066,12 @@ var WorkspaceMonthlyHistory = ({
55843
56066
  recharts.YAxis,
55844
56067
  {
55845
56068
  domain: [0, chartData.yAxisMax],
55846
- width: 40,
55847
- ticks: yAxisTicks,
56069
+ width: isMobile ? 34 : 40,
56070
+ ticks: visibleYAxisTicks,
55848
56071
  tick: (props) => {
55849
56072
  const { x, y, payload } = props;
55850
56073
  const value = Math.round(payload.value);
55851
- const targetValue = Math.round(chartData.lastSetTarget);
55852
- const isTarget = value === targetValue && targetValue > 0;
56074
+ const isTarget = (chartData.targetValues || []).includes(value);
55853
56075
  return /* @__PURE__ */ jsxRuntime.jsx(
55854
56076
  "text",
55855
56077
  {
@@ -55872,15 +56094,7 @@ var WorkspaceMonthlyHistory = ({
55872
56094
  content: (props) => /* @__PURE__ */ jsxRuntime.jsx(CustomTooltip3, { ...props, isUptimeMode })
55873
56095
  }
55874
56096
  ),
55875
- !isUptimeMode && chartData.lastSetTarget > 0 && /* @__PURE__ */ jsxRuntime.jsx(
55876
- recharts.ReferenceLine,
55877
- {
55878
- y: chartData.lastSetTarget,
55879
- stroke: "#E34329",
55880
- strokeDasharray: "5 5",
55881
- strokeWidth: 2
55882
- }
55883
- ),
56097
+ !isUptimeMode && /* @__PURE__ */ jsxRuntime.jsx(recharts.Customized, { component: (props) => renderDailyOutputCapsules2(props, chartData.data) }),
55884
56098
  isUptimeMode ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
55885
56099
  /* @__PURE__ */ jsxRuntime.jsx(
55886
56100
  recharts.Bar,
@@ -55913,6 +56127,8 @@ var WorkspaceMonthlyHistory = ({
55913
56127
  {
55914
56128
  dataKey: "output",
55915
56129
  radius: [4, 4, 0, 0],
56130
+ fill: "transparent",
56131
+ opacity: 0,
55916
56132
  isAnimationActive: true,
55917
56133
  animationBegin: 0,
55918
56134
  animationDuration: 1e3,
@@ -55956,13 +56172,9 @@ var WorkspaceMonthlyHistory = ({
55956
56172
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-2.5 h-2.5 rounded-full", style: { backgroundColor: "#e5e7eb" } }),
55957
56173
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-gray-600", children: "Idle" })
55958
56174
  ] })
55959
- ] }) : chartData.lastSetTarget > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
55960
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-12 h-0.5 border-t-2 border-dashed", style: { borderColor: "#E34329" } }),
55961
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-gray-600", children: [
55962
- "Target: ",
55963
- Math.round(chartData.lastSetTarget),
55964
- " units/day"
55965
- ] })
56175
+ ] }) : chartData.targetValues.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
56176
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative h-5 w-3 rounded-full border border-[#E34329] bg-[#fff5f3] overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-x-0 bottom-0 h-1/2 bg-[#E34329]" }) }),
56177
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-gray-600", children: chartData.targetLegend })
55966
56178
  ] }) })
55967
56179
  ] })
55968
56180
  ] })
@@ -67592,6 +67804,17 @@ var setSessionSeenValue = (key) => {
67592
67804
  };
67593
67805
  var buildAllGreenCelebrationSeenKey = (identity) => `${ALL_GREEN_CELEBRATION_SEEN_PREFIX}${identity}`;
67594
67806
  var buildAllGreenMilestoneSeenKey = (identity, milestoneSeconds) => `${ALL_GREEN_MILESTONE_SEEN_PREFIX}${identity}:${milestoneSeconds}`;
67807
+ var LINE_SELECTOR_INDICATOR_VERSION = "incident_exclamation_v1";
67808
+ var LineSelectorIncidentIcon = ({ lineId }) => /* @__PURE__ */ jsxRuntime.jsx(
67809
+ "span",
67810
+ {
67811
+ "data-testid": `line-selector-incident-icon-${lineId}`,
67812
+ "aria-label": "Line needs attention",
67813
+ role: "img",
67814
+ className: "inline-flex h-5 w-5 flex-shrink-0 items-center justify-center rounded-full border border-rose-200 bg-white text-[13px] font-semibold leading-none text-rose-600 shadow-[0_1px_2px_rgba(15,23,42,0.06)]",
67815
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", className: "-mt-px", children: "!" })
67816
+ }
67817
+ );
67595
67818
  var LoadingPageCmp = LoadingPage_default;
67596
67819
  var LoadingOverlayCmp = LoadingOverlay_default;
67597
67820
  function HomeView({
@@ -67931,25 +68154,49 @@ function HomeView({
67931
68154
  const currentIsCurrentScopeResolved = isBootstrapMonitorMode ? bootstrapMonitor.isCurrentScopeResolved : legacyIsCurrentScopeResolved;
67932
68155
  const currentMetricsError = isBootstrapMonitorMode ? bootstrapMonitor.error : legacyMetricsError;
67933
68156
  const currentRefetchMetrics = isBootstrapMonitorMode ? bootstrapMonitor.refetch : refetchLegacyMetrics;
67934
- const lineSelectorSignalStatusByLine = React125.useMemo(() => {
67935
- const statusByLine = /* @__PURE__ */ new Map();
68157
+ const lineSelectorIndicatorByLine = React125.useMemo(() => {
68158
+ const indicatorByLine = /* @__PURE__ */ new Map();
67936
68159
  const legend = currentEfficiencyLegend || DEFAULT_EFFICIENCY_LEGEND;
67937
68160
  const addRows = (rows) => {
67938
68161
  (rows || []).forEach((row) => {
67939
68162
  const lineId = typeof row?.line_id === "string" ? row.line_id : "";
67940
- if (!lineId || statusByLine.has(lineId)) {
68163
+ if (!lineId) {
68164
+ return;
68165
+ }
68166
+ if (row?.red_flow_incident?.active === true) {
68167
+ indicatorByLine.set(lineId, "incident");
68168
+ return;
68169
+ }
68170
+ if (indicatorByLine.get(lineId) === "incident") {
67941
68171
  return;
67942
68172
  }
67943
68173
  const status = getKpiSignalStatus(row?.line_signal, legend);
67944
- if (status) {
67945
- statusByLine.set(lineId, status);
68174
+ if (status === "attention") {
68175
+ indicatorByLine.set(lineId, "red");
67946
68176
  }
67947
68177
  });
67948
68178
  };
67949
68179
  addRows(currentSelectorLineMetrics);
67950
68180
  addRows(currentLineMetrics);
67951
- return statusByLine;
68181
+ return indicatorByLine;
67952
68182
  }, [currentEfficiencyLegend, currentLineMetrics, currentSelectorLineMetrics]);
68183
+ const lineSelectorIndicatorStats = React125.useMemo(() => {
68184
+ let incidentLineCount = 0;
68185
+ let redFlowLineCount = 0;
68186
+ visibleLineIds.forEach((lineId) => {
68187
+ const indicator = lineSelectorIndicatorByLine.get(lineId);
68188
+ if (indicator === "incident") {
68189
+ incidentLineCount += 1;
68190
+ } else if (indicator === "red") {
68191
+ redFlowLineCount += 1;
68192
+ }
68193
+ });
68194
+ return {
68195
+ incidentLineCount,
68196
+ redFlowLineCount,
68197
+ hasAnyIncident: incidentLineCount > 0
68198
+ };
68199
+ }, [lineSelectorIndicatorByLine, visibleLineIds]);
67953
68200
  const metricsDisplayNames = React125.useMemo(() => {
67954
68201
  const nextDisplayNames = {};
67955
68202
  currentWorkspaceMetrics.forEach((workspace) => {
@@ -68806,9 +69053,12 @@ function HomeView({
68806
69053
  new_line_ids: normalizedLineIds,
68807
69054
  selected_line_count: normalizedLineIds.length,
68808
69055
  selection_mode: isAllLinesSelection(normalizedLineIds) ? "all" : normalizedLineIds.length === 1 ? "single" : "custom",
69056
+ incident_line_count: lineSelectorIndicatorStats.incidentLineCount,
69057
+ red_flow_line_count: lineSelectorIndicatorStats.redFlowLineCount,
69058
+ selector_indicator_version: LINE_SELECTOR_INDICATOR_VERSION,
68809
69059
  line_name: getLineSelectionLabel(normalizedLineIds)
68810
69060
  });
68811
- }, [factoryViewId, getLineSelectionLabel, getTrackedLineScope, selectedLineIds, selectedLineIdsKey, visibleLineIds]);
69061
+ }, [factoryViewId, getLineSelectionLabel, getTrackedLineScope, lineSelectorIndicatorStats, selectedLineIds, selectedLineIdsKey, visibleLineIds]);
68812
69062
  React125.useCallback(() => {
68813
69063
  updateSelectedLineIds(visibleLineIds);
68814
69064
  }, [updateSelectedLineIds, visibleLineIds]);
@@ -68866,7 +69116,10 @@ function HomeView({
68866
69116
  current_display_mode_label: getHomeDisplayModeLabel(displayMode),
68867
69117
  selected_line_ids: selectedLineIds,
68868
69118
  selected_line_count: selectedLineIds.length,
68869
- is_all_lines: isAllLinesSelection(selectedLineIds)
69119
+ is_all_lines: isAllLinesSelection(selectedLineIds),
69120
+ incident_line_count: lineSelectorIndicatorStats.incidentLineCount,
69121
+ red_flow_line_count: lineSelectorIndicatorStats.redFlowLineCount,
69122
+ selector_indicator_version: LINE_SELECTOR_INDICATOR_VERSION
68870
69123
  });
68871
69124
  }
68872
69125
  },
@@ -68907,8 +69160,8 @@ function HomeView({
68907
69160
  ] }),
68908
69161
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-h-56 space-y-0.5 overflow-y-auto pr-1", children: visibleLineIds.map((lineId) => {
68909
69162
  const isChecked = pendingSelectedLineIds.includes(lineId);
68910
- const signalStatus = lineSelectorSignalStatusByLine.get(lineId);
68911
- const signalDotClass = signalStatus === "stable" ? "bg-green-500" : signalStatus === "warning" ? "bg-yellow-400" : signalStatus === "attention" ? "bg-red-500" : "";
69163
+ const selectorIndicator = lineSelectorIndicatorByLine.get(lineId);
69164
+ const lineLabel = mergedLineNames[lineId] || `Line ${lineId.substring(0, 4)}`;
68912
69165
  return /* @__PURE__ */ jsxRuntime.jsxs(
68913
69166
  "label",
68914
69167
  {
@@ -68918,6 +69171,7 @@ function HomeView({
68918
69171
  "input",
68919
69172
  {
68920
69173
  type: "checkbox",
69174
+ "aria-label": lineLabel,
68921
69175
  className: "h-4 w-4 rounded border-slate-300 text-blue-600 focus:ring-blue-500",
68922
69176
  checked: isChecked,
68923
69177
  onChange: () => {
@@ -68933,12 +69187,12 @@ function HomeView({
68933
69187
  }
68934
69188
  }
68935
69189
  ),
68936
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate", children: mergedLineNames[lineId] || `Line ${lineId.substring(0, 4)}` }),
68937
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-2.5 w-2.5 flex-shrink-0 items-center justify-center", children: signalStatus ? /* @__PURE__ */ jsxRuntime.jsx(
69190
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate", children: lineLabel }),
69191
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-5 w-5 flex-shrink-0 items-center justify-center", children: selectorIndicator === "incident" ? /* @__PURE__ */ jsxRuntime.jsx(LineSelectorIncidentIcon, { lineId }) : selectorIndicator === "red" && !lineSelectorIndicatorStats.hasAnyIncident ? /* @__PURE__ */ jsxRuntime.jsx(
68938
69192
  "span",
68939
69193
  {
68940
69194
  "data-testid": `line-selector-signal-dot-${lineId}`,
68941
- className: `h-2 w-2 rounded-full ${signalDotClass}`,
69195
+ className: "h-2 w-2 rounded-full bg-red-500",
68942
69196
  "aria-hidden": "true"
68943
69197
  }
68944
69198
  ) : null })
@@ -68970,7 +69224,8 @@ function HomeView({
68970
69224
  mergedLineNames,
68971
69225
  selectedLineIds,
68972
69226
  pendingSelectedLineIds,
68973
- lineSelectorSignalStatusByLine,
69227
+ lineSelectorIndicatorByLine,
69228
+ lineSelectorIndicatorStats,
68974
69229
  displayMode,
68975
69230
  slideshowActiveLineId,
68976
69231
  visibleLineIds,