@optifye/dashboard-core 6.12.25 → 6.12.27

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
@@ -22075,14 +22075,87 @@ var toNumber = (value) => {
22075
22075
  }
22076
22076
  return 0;
22077
22077
  };
22078
+ var toOptionalNumber = (value) => {
22079
+ if (typeof value === "number" && Number.isFinite(value)) return value;
22080
+ if (typeof value === "string" && value.trim() !== "") {
22081
+ const parsed = Number(value);
22082
+ return Number.isFinite(parsed) ? parsed : null;
22083
+ }
22084
+ return null;
22085
+ };
22078
22086
  var EFFICIENCY_ON_TRACK_THRESHOLD = 100;
22079
22087
  var isEfficiencyOnTrack = (efficiency) => toNumber(efficiency) >= EFFICIENCY_ON_TRACK_THRESHOLD;
22088
+ var KPI_SIGNAL_LABELS = {
22089
+ stable: "Stable",
22090
+ warning: "Warning",
22091
+ attention: "Attention"
22092
+ };
22093
+ var normalizeLineSignalSource = (source) => source === "recent_flow" ? "recent_flow" : "efficiency";
22094
+ var normalizeLineSignal = (lineSignal, fallbackEfficiency, fallbackWeight) => {
22095
+ if (lineSignal && typeof lineSignal === "object") {
22096
+ const raw = lineSignal;
22097
+ return {
22098
+ source: normalizeLineSignalSource(raw.source),
22099
+ percent: toOptionalNumber(raw.percent),
22100
+ weight: toOptionalNumber(raw.weight),
22101
+ mode: raw.mode === "unavailable" ? "unavailable" : "computed",
22102
+ reason: typeof raw.reason === "string" ? raw.reason : raw.reason ?? null,
22103
+ effectiveEndAt: raw.effective_end_at ?? raw.effectiveEndAt ?? null,
22104
+ computedAt: raw.computed_at ?? raw.computedAt ?? null
22105
+ };
22106
+ }
22107
+ const percent2 = toOptionalNumber(fallbackEfficiency);
22108
+ if (percent2 === null) return null;
22109
+ return {
22110
+ source: "efficiency",
22111
+ percent: percent2,
22112
+ weight: toOptionalNumber(fallbackWeight),
22113
+ mode: "computed",
22114
+ reason: "fallback_efficiency",
22115
+ effectiveEndAt: null,
22116
+ computedAt: null
22117
+ };
22118
+ };
22119
+ var getKpiSignalStatus = (signal, legend = DEFAULT_EFFICIENCY_LEGEND) => {
22120
+ const percent2 = toOptionalNumber(signal?.percent);
22121
+ if (percent2 === null) return null;
22122
+ const color2 = getEfficiencyColor(percent2, legend);
22123
+ if (color2 === "green") return "stable";
22124
+ if (color2 === "yellow") return "warning";
22125
+ return "attention";
22126
+ };
22127
+ var getKpiSignalLabel = (signal, legend = DEFAULT_EFFICIENCY_LEGEND) => {
22128
+ const status = getKpiSignalStatus(signal, legend);
22129
+ return status ? KPI_SIGNAL_LABELS[status] : null;
22130
+ };
22131
+ var aggregateLineSignals = (signals) => {
22132
+ const numericSignals = signals.filter(
22133
+ (signal) => signal !== null && signal !== void 0 && toOptionalNumber(signal.percent) !== null
22134
+ );
22135
+ if (numericSignals.length === 0) return null;
22136
+ const percent2 = numericSignals.reduce(
22137
+ (sum, signal) => sum + (toOptionalNumber(signal.percent) ?? 0),
22138
+ 0
22139
+ ) / numericSignals.length;
22140
+ const signalSources = new Set(numericSignals.map((signal) => signal.source));
22141
+ const source = signalSources.size === 1 ? numericSignals[0].source : "mixed";
22142
+ return {
22143
+ source,
22144
+ percent: percent2,
22145
+ weight: null,
22146
+ mode: "computed",
22147
+ reason: "average",
22148
+ effectiveEndAt: null,
22149
+ computedAt: null
22150
+ };
22151
+ };
22080
22152
  var createDefaultKPIs = () => ({
22081
22153
  underperformingWorkers: { current: 0, total: 0, change: 0 },
22082
22154
  efficiency: { value: 0, change: 0 },
22083
22155
  outputProgress: { current: 0, target: 0, idealOutput: 0, change: 0 },
22084
22156
  avgCycleTime: { value: 0, change: 0 },
22085
- qualityCompliance: { value: 95, change: 0 }
22157
+ qualityCompliance: { value: 95, change: 0 },
22158
+ lineSignal: null
22086
22159
  });
22087
22160
  var buildKPIsFromLineMetricsRow = (row) => {
22088
22161
  if (!row) return createDefaultKPIs();
@@ -22116,15 +22189,28 @@ var buildKPIsFromLineMetricsRow = (row) => {
22116
22189
  qualityCompliance: {
22117
22190
  value: 95,
22118
22191
  change: 0
22119
- }
22192
+ },
22193
+ lineSignal: normalizeLineSignal(row.line_signal, avgEfficiency, idealOutput || lineThreshold)
22120
22194
  };
22121
22195
  };
22122
22196
  var aggregateKPIsFromLineMetricsRows = (rows) => {
22123
22197
  if (!rows || rows.length === 0) return createDefaultKPIs();
22198
+ const lineSignal = aggregateLineSignals(
22199
+ rows.map((row) => normalizeLineSignal(
22200
+ row?.line_signal,
22201
+ row?.avg_efficiency,
22202
+ row?.ideal_output ?? row?.line_threshold
22203
+ ))
22204
+ );
22124
22205
  const eligibleRows = rows.filter(
22125
22206
  (row) => isValidAggregateEfficiency(row?.monitoring_mode ?? row?.monitoringMode, row?.avg_efficiency)
22126
22207
  );
22127
- if (eligibleRows.length === 0) return createDefaultKPIs();
22208
+ if (eligibleRows.length === 0) {
22209
+ return {
22210
+ ...createDefaultKPIs(),
22211
+ lineSignal
22212
+ };
22213
+ }
22128
22214
  const currentOutputSum = eligibleRows.reduce((sum, row) => sum + toNumber(row.current_output), 0);
22129
22215
  const lineThresholdSum = eligibleRows.reduce(
22130
22216
  (sum, row) => sum + (row?.output_target_recalculated !== void 0 && row?.output_target_recalculated !== null ? toNumber(row.output_target_recalculated) : toNumber(row.line_threshold)),
@@ -22171,7 +22257,8 @@ var aggregateKPIsFromLineMetricsRows = (rows) => {
22171
22257
  qualityCompliance: {
22172
22258
  value: 95,
22173
22259
  change: 0
22174
- }
22260
+ },
22261
+ lineSignal
22175
22262
  };
22176
22263
  };
22177
22264
 
@@ -34725,19 +34812,19 @@ var SkuRow = ({ sku, isSelected, isLive, onSelect }) => {
34725
34812
  onClick: () => onSelect?.(isSelected ? null : sku.sku_id),
34726
34813
  "data-testid": `sku-progress-row-${sku.sku_id}`,
34727
34814
  children: [
34728
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline justify-between gap-3", children: [
34729
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 truncate", children: [
34815
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-3", children: [
34816
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-2 flex-1 min-w-0", children: [
34730
34817
  /* @__PURE__ */ jsxRuntime.jsx(
34731
34818
  "span",
34732
34819
  {
34733
- className: "text-sm font-semibold text-gray-800 truncate",
34820
+ className: "text-xs sm:text-sm font-semibold text-gray-800 break-words line-clamp-2",
34734
34821
  title: skuLabel,
34735
34822
  children: skuLabel
34736
34823
  }
34737
34824
  ),
34738
- isLive && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-2 h-2 rounded-full bg-green-500 shrink-0 shadow-[0_0_4px_rgba(34,197,94,0.6)]", title: "Currently running" })
34825
+ isLive && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-2 h-2 rounded-full bg-green-500 shrink-0 shadow-[0_0_4px_rgba(34,197,94,0.6)] mt-1.5", title: "Currently running" })
34739
34826
  ] }),
34740
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-gray-500 font-medium tabular-nums shrink-0", children: [
34827
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-gray-500 font-medium tabular-nums shrink-0 pt-0.5", children: [
34741
34828
  Math.round(current),
34742
34829
  " / ",
34743
34830
  Math.round(target)
@@ -36146,6 +36233,37 @@ var interpretIdleValue = (value) => {
36146
36233
  if (value === "x" || value === null || value === void 0) return "unknown";
36147
36234
  return "unknown";
36148
36235
  };
36236
+ var timeToMinutes = (value) => {
36237
+ const parsed = parseTime(value);
36238
+ if (!parsed) return null;
36239
+ return parsed.hour * 60 + parsed.minute;
36240
+ };
36241
+ var normalizeBreaksOnShiftTimeline2 = (shiftStart, shiftBreaks) => {
36242
+ const shiftStartMinutes = timeToMinutes(shiftStart);
36243
+ if (shiftStartMinutes === null || !Array.isArray(shiftBreaks)) {
36244
+ return [];
36245
+ }
36246
+ return shiftBreaks.flatMap((entry) => {
36247
+ const startRaw = timeToMinutes(entry?.startTime ?? entry?.start);
36248
+ const endRaw = timeToMinutes(entry?.endTime ?? entry?.end);
36249
+ if (startRaw === null || endRaw === null) return [];
36250
+ let start = startRaw;
36251
+ let end = endRaw;
36252
+ if (end <= start) {
36253
+ end += 24 * 60;
36254
+ }
36255
+ if (start < shiftStartMinutes) {
36256
+ start += 24 * 60;
36257
+ end += 24 * 60;
36258
+ }
36259
+ return [{ start, end }];
36260
+ });
36261
+ };
36262
+ var isBreakMinute = (minuteIndex, shiftStartMinutes, normalizedBreaks) => {
36263
+ if (!normalizedBreaks.length) return false;
36264
+ const absoluteMinute = shiftStartMinutes + minuteIndex;
36265
+ return normalizedBreaks.some((entry) => entry.start <= absoluteMinute && absoluteMinute < entry.end);
36266
+ };
36149
36267
  var getShiftDurationMinutes = (shiftStart, shiftEnd) => {
36150
36268
  const start = parseTime(shiftStart);
36151
36269
  const end = parseTime(shiftEnd);
@@ -36156,6 +36274,29 @@ var getShiftDurationMinutes = (shiftStart, shiftEnd) => {
36156
36274
  }
36157
36275
  return duration > 0 ? duration : null;
36158
36276
  };
36277
+ var getBreakExcludedShiftMinutes = ({
36278
+ shiftStart,
36279
+ shiftEnd,
36280
+ elapsedMinutes,
36281
+ shiftBreaks
36282
+ }) => {
36283
+ const shiftMinutes = getShiftDurationMinutes(shiftStart, shiftEnd);
36284
+ if (shiftMinutes === null) return null;
36285
+ const elapsedLimit = Number.isFinite(elapsedMinutes) ? Math.min(Math.max(Math.floor(elapsedMinutes ?? 0), 0), shiftMinutes) : shiftMinutes;
36286
+ if (elapsedLimit <= 0) return 0;
36287
+ const startTime = parseTime(shiftStart);
36288
+ if (!startTime) return elapsedLimit;
36289
+ const shiftStartMinutes = startTime.hour * 60 + startTime.minute;
36290
+ const normalizedBreaks = normalizeBreaksOnShiftTimeline2(shiftStart, shiftBreaks);
36291
+ if (!normalizedBreaks.length) return elapsedLimit;
36292
+ let workingMinutes = 0;
36293
+ for (let minuteIndex = 0; minuteIndex < elapsedLimit; minuteIndex += 1) {
36294
+ if (!isBreakMinute(minuteIndex, shiftStartMinutes, normalizedBreaks)) {
36295
+ workingMinutes += 1;
36296
+ }
36297
+ }
36298
+ return workingMinutes;
36299
+ };
36159
36300
  var getShiftElapsedMinutes = ({
36160
36301
  shiftStart,
36161
36302
  shiftEnd,
@@ -36220,7 +36361,8 @@ var buildUptimeSeries = ({
36220
36361
  shiftEnd,
36221
36362
  shiftDate,
36222
36363
  timezone,
36223
- elapsedMinutes
36364
+ elapsedMinutes,
36365
+ shiftBreaks
36224
36366
  }) => {
36225
36367
  const normalizedIdle = normalizeIdleTimeHourly(idleTimeHourly || {});
36226
36368
  const hasIdleData = Object.keys(normalizedIdle).length > 0;
@@ -36275,6 +36417,8 @@ var buildUptimeSeries = ({
36275
36417
  const points = [];
36276
36418
  let activeMinutes = 0;
36277
36419
  let idleMinutes = 0;
36420
+ const shiftStartMinutes = startTime.hour * 60 + startTime.minute;
36421
+ const normalizedBreaks = normalizeBreaksOnShiftTimeline2(shiftStart, shiftBreaks);
36278
36422
  for (let minuteIndex = 0; minuteIndex < shiftMinutes; minuteIndex += 1) {
36279
36423
  const minuteDate = dateFns.addMinutes(shiftStartDate, minuteIndex);
36280
36424
  const timeLabel = dateFnsTz.formatInTimeZone(minuteDate, timezone, "h:mm a");
@@ -36287,6 +36431,15 @@ var buildUptimeSeries = ({
36287
36431
  });
36288
36432
  continue;
36289
36433
  }
36434
+ if (isBreakMinute(minuteIndex, shiftStartMinutes, normalizedBreaks)) {
36435
+ points.push({
36436
+ minuteIndex,
36437
+ timeLabel,
36438
+ uptime: null,
36439
+ status: "break"
36440
+ });
36441
+ continue;
36442
+ }
36290
36443
  const hourKey = dateFnsTz.formatInTimeZone(minuteDate, timezone, "H");
36291
36444
  const minuteKey = Number.parseInt(dateFnsTz.formatInTimeZone(minuteDate, timezone, "m"), 10);
36292
36445
  const hourBucket = normalizedIdle[hourKey] || [];
@@ -39447,6 +39600,7 @@ var HourlyUptimeChartComponent = ({
39447
39600
  shiftDate,
39448
39601
  timezone,
39449
39602
  elapsedMinutes,
39603
+ shiftBreaks,
39450
39604
  className = ""
39451
39605
  }) => {
39452
39606
  const containerRef = React144__namespace.default.useRef(null);
@@ -39458,8 +39612,9 @@ var HourlyUptimeChartComponent = ({
39458
39612
  shiftEnd,
39459
39613
  shiftDate,
39460
39614
  timezone,
39461
- elapsedMinutes
39462
- }), [idleTimeHourly, shiftStart, shiftEnd, shiftDate, timezone, elapsedMinutes]);
39615
+ elapsedMinutes,
39616
+ shiftBreaks
39617
+ }), [idleTimeHourly, shiftStart, shiftEnd, shiftDate, timezone, elapsedMinutes, shiftBreaks]);
39463
39618
  const hasAggregateData = Boolean(hourlyAggregates && hourlyAggregates.length > 0);
39464
39619
  const shiftStartTime = React144__namespace.default.useMemo(
39465
39620
  () => getTimeFromTimeString(shiftStart),
@@ -45007,7 +45162,7 @@ var FileManagerFilters = ({
45007
45162
  onClick: () => {
45008
45163
  onIdleClipSortChange?.(idleClipSort === "latest" ? "idle_duration_desc" : "latest");
45009
45164
  },
45010
- className: `p-2 rounded-xl transition-all duration-200 ${idleClipSort === "idle_duration_desc" ? "bg-orange-100 text-orange-600 hover:bg-orange-200 shadow-sm" : "bg-slate-100 text-slate-600 hover:bg-slate-200"}`,
45165
+ className: `p-2 rounded-xl transition-all duration-200 ${idleClipSort === "idle_duration_desc" ? "bg-blue-100 text-blue-600 hover:bg-blue-200 shadow-sm" : "bg-slate-100 text-slate-600 hover:bg-slate-200"}`,
45011
45166
  title: idleClipSort === "idle_duration_desc" ? "Sort by newest first" : "Sort by longest idle first",
45012
45167
  "aria-label": idleClipSort === "idle_duration_desc" ? "Sort idle clips by newest first" : "Sort idle clips by longest idle first",
45013
45168
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDownWideNarrow, { className: "h-5 w-5" })
@@ -45074,7 +45229,7 @@ var FileManagerFilters = ({
45074
45229
  }
45075
45230
  )
45076
45231
  ] }),
45077
- activeFilter === "idle_time" && idleClipSort === "idle_duration_desc" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex w-fit items-center gap-2 rounded-full border border-orange-100 bg-orange-50/70 px-2.5 py-1 text-xs text-orange-700 shadow-sm", children: [
45232
+ activeFilter === "idle_time" && idleClipSort === "idle_duration_desc" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex w-fit items-center gap-2 rounded-full border border-blue-100 bg-blue-50/70 px-2.5 py-1 text-xs text-blue-700 shadow-sm", children: [
45078
45233
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDownWideNarrow, { className: "h-3.5 w-3.5" }),
45079
45234
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "Longest idle first" }),
45080
45235
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -45084,7 +45239,7 @@ var FileManagerFilters = ({
45084
45239
  e.stopPropagation();
45085
45240
  onIdleClipSortChange?.("latest");
45086
45241
  },
45087
- className: "rounded-full p-0.5 transition-colors hover:bg-orange-100",
45242
+ className: "rounded-full p-0.5 transition-colors hover:bg-blue-100",
45088
45243
  title: "Clear idle sort",
45089
45244
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-3.5 w-3.5" })
45090
45245
  }
@@ -51651,7 +51806,8 @@ var LineMonthlyPdfGenerator = ({
51651
51806
  };
51652
51807
  var LineWhatsAppShareButton = ({
51653
51808
  lineInfo,
51654
- className
51809
+ className,
51810
+ shiftBreaks = []
51655
51811
  }) => {
51656
51812
  const handleShare = () => {
51657
51813
  trackCoreEvent("Line WhatsApp Share Clicked", {
@@ -51665,7 +51821,8 @@ var LineWhatsAppShareButton = ({
51665
51821
  shiftStart: lineInfo.metrics.shift_start,
51666
51822
  shiftEnd: lineInfo.metrics.shift_end,
51667
51823
  shiftDate: lineInfo.date,
51668
- timezone: "Asia/Kolkata"
51824
+ timezone: "Asia/Kolkata",
51825
+ shiftBreaks
51669
51826
  }) : null;
51670
51827
  const efficiencyValue = Number.isFinite(lineInfo.metrics.avg_efficiency) ? Number(lineInfo.metrics.avg_efficiency) : null;
51671
51828
  const utilization = efficiencyValue !== null ? efficiencyValue : uptimeSeries && uptimeSeries.availableMinutes > 0 ? uptimeSeries.activeMinutes / uptimeSeries.availableMinutes * 100 : 0;
@@ -51834,16 +51991,24 @@ var LinePdfGenerator = ({
51834
51991
  shiftStart,
51835
51992
  shiftEnd,
51836
51993
  shiftDate,
51837
- timezone: effectiveUptimeTimezone
51994
+ timezone: effectiveUptimeTimezone,
51995
+ shiftBreaks
51838
51996
  });
51839
51997
  let activeMinutes = uptimeSeries.activeMinutes;
51840
51998
  let idleMinutes = uptimeSeries.idleMinutes;
51841
51999
  let availableMinutes = uptimeSeries.availableMinutes;
51842
52000
  let hasData = uptimeSeries.hasData;
51843
- if (!hasData) {
52001
+ const hasIdleHourlyPayload = Boolean(
52002
+ workspace.idle_time_hourly && typeof workspace.idle_time_hourly === "object" && Object.keys(workspace.idle_time_hourly).length > 0
52003
+ );
52004
+ if (!hasData && !hasIdleHourlyPayload) {
51844
52005
  const idleTimeValue = workspace.idle_time;
51845
52006
  const hasIdleTimeValue = Number.isFinite(idleTimeValue);
51846
- const fallbackDuration = shiftDurationMinutes;
52007
+ const fallbackDuration = getBreakExcludedShiftMinutes({
52008
+ shiftStart,
52009
+ shiftEnd,
52010
+ shiftBreaks
52011
+ }) ?? shiftDurationMinutes;
51847
52012
  if (hasIdleTimeValue && idleTimeValue > 0 && fallbackDuration > 0) {
51848
52013
  const idleFromAggregate = Math.min(Math.max(Number(idleTimeValue) / 60, 0), fallbackDuration);
51849
52014
  idleMinutes = idleFromAggregate;
@@ -51915,7 +52080,8 @@ var LinePdfGenerator = ({
51915
52080
  shiftStart: lineShiftStart,
51916
52081
  shiftEnd: lineShiftEnd,
51917
52082
  shiftDate: lineInfo.date,
51918
- timezone: effectiveUptimeTimezone
52083
+ timezone: effectiveUptimeTimezone,
52084
+ shiftBreaks
51919
52085
  });
51920
52086
  hourlyData = buildHourlyFromSeries(lineUptimeSeries);
51921
52087
  }
@@ -53854,7 +54020,8 @@ var WorkspaceMonthlyHistory = ({
53854
54020
  };
53855
54021
  var WorkspaceWhatsAppShareButton = ({
53856
54022
  workspace,
53857
- className
54023
+ className,
54024
+ shiftBreaks = []
53858
54025
  }) => {
53859
54026
  const handleShare = () => {
53860
54027
  trackCoreEvent("Workspace WhatsApp Share Clicked", {
@@ -53870,7 +54037,11 @@ var WorkspaceWhatsAppShareButton = ({
53870
54037
  timeZone: "Asia/Kolkata"
53871
54038
  });
53872
54039
  const isUptimeMode = workspace.monitoring_mode === "uptime";
53873
- const shiftMinutes = getShiftDurationMinutes(workspace.shift_start, workspace.shift_end);
54040
+ const shiftMinutes = getBreakExcludedShiftMinutes({
54041
+ shiftStart: workspace.shift_start,
54042
+ shiftEnd: workspace.shift_end,
54043
+ shiftBreaks
54044
+ }) ?? getShiftDurationMinutes(workspace.shift_start, workspace.shift_end);
53874
54045
  const shiftSeconds = shiftMinutes ? shiftMinutes * 60 : 0;
53875
54046
  const idleSeconds = Math.max(workspace.idle_time || 0, 0);
53876
54047
  const clampedIdleSeconds = shiftSeconds > 0 ? Math.min(idleSeconds, shiftSeconds) : idleSeconds;
@@ -53960,7 +54131,11 @@ var WorkspacePdfGenerator = ({
53960
54131
  try {
53961
54132
  const isUptimeMode = workspace.monitoring_mode === "uptime";
53962
54133
  const isAssemblyCycleMode = !isUptimeMode && shouldUseAssemblyCycleTimeLayout(workspace);
53963
- const shiftMinutes = getShiftDurationMinutes(workspace.shift_start, workspace.shift_end);
54134
+ const shiftMinutes = getBreakExcludedShiftMinutes({
54135
+ shiftStart: workspace.shift_start,
54136
+ shiftEnd: workspace.shift_end,
54137
+ shiftBreaks
54138
+ }) ?? getShiftDurationMinutes(workspace.shift_start, workspace.shift_end);
53964
54139
  const shiftSeconds = shiftMinutes ? shiftMinutes * 60 : 0;
53965
54140
  const idleSeconds = Math.max(workspace.idle_time || 0, 0);
53966
54141
  const clampedIdleSeconds = shiftSeconds > 0 ? Math.min(idleSeconds, shiftSeconds) : idleSeconds;
@@ -54130,7 +54305,8 @@ var WorkspacePdfGenerator = ({
54130
54305
  shiftStart: workspace.shift_start,
54131
54306
  shiftEnd: workspace.shift_end,
54132
54307
  shiftDate: workspace.date,
54133
- timezone: reportTimezone
54308
+ timezone: reportTimezone,
54309
+ shiftBreaks
54134
54310
  }) : null;
54135
54311
  const hourlyUptime = uptimeSeries?.points?.length ? Array.from({ length: Math.ceil(uptimeSeries.shiftMinutes / 60) }, (_, index) => {
54136
54312
  const start = index * 60;
@@ -57525,7 +57701,7 @@ var SideNavBar = React144.memo(({
57525
57701
  const role = user?.role_level;
57526
57702
  const roleNavPaths = React144.useMemo(() => getRoleNavPaths(role), [role]);
57527
57703
  const showLiveMonitorLink = roleNavPaths.includes("/live-monitor");
57528
- const rootDashboardSurface = React144.useMemo(() => role === "owner" || role === "plant_head" || role === "optifye" ? "operations_overview" : "monitor", [role]);
57704
+ const rootDashboardSurface = React144.useMemo(() => "monitor", []);
57529
57705
  const getBasePath = React144.useCallback((path) => {
57530
57706
  const firstSegment = path.split("?")[0].split("/").filter(Boolean)[0];
57531
57707
  return firstSegment ? `/${firstSegment}` : "/";
@@ -57576,12 +57752,6 @@ var SideNavBar = React144.memo(({
57576
57752
  dashboard_surface: dashboardSurface
57577
57753
  }
57578
57754
  }), []);
57579
- const handleHomeClick = React144.useCallback(() => {
57580
- navigate("/", {
57581
- trackingEvent: buildDashboardSurfaceTrackingEvent("side_nav", "/", rootDashboardSurface)
57582
- });
57583
- onMobileMenuClose?.();
57584
- }, [navigate, onMobileMenuClose, buildDashboardSurfaceTrackingEvent, rootDashboardSurface]);
57585
57755
  const handleLeaderboardClick = React144.useCallback(() => {
57586
57756
  navigate(`/leaderboard`, {
57587
57757
  trackingEvent: {
@@ -57887,7 +58057,6 @@ var SideNavBar = React144.memo(({
57887
58057
  });
57888
58058
  onMobileMenuClose?.();
57889
58059
  }, [navigate, onMobileMenuClose, buildDashboardSurfaceTrackingEvent, rootDashboardSurface]);
57890
- const homeButtonClasses = React144.useMemo(() => getButtonClasses("/"), [getButtonClasses]);
57891
58060
  const liveButtonClasses = React144.useMemo(() => getButtonClasses("/live-monitor"), [getButtonClasses]);
57892
58061
  const leaderboardButtonClasses = React144.useMemo(() => getButtonClasses("/leaderboard"), [getButtonClasses]);
57893
58062
  const kpisButtonClasses = React144.useMemo(() => getButtonClasses("/kpis"), [getButtonClasses]);
@@ -57918,118 +58087,99 @@ var SideNavBar = React144.memo(({
57918
58087
  children: /* @__PURE__ */ jsxRuntime.jsx(Logo, { className: "w-12 h-12 object-contain cursor-pointer" })
57919
58088
  }
57920
58089
  ) }),
57921
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 w-full py-6 px-4 overflow-y-auto", children: [
57922
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-6", children: [
57923
- canAccessPath("/") && /* @__PURE__ */ jsxRuntime.jsxs(
57924
- "button",
57925
- {
57926
- onClick: handleHomeClick,
57927
- className: homeButtonClasses,
57928
- "aria-label": "Home",
57929
- tabIndex: 0,
57930
- role: "tab",
57931
- "aria-selected": isPathActive("/"),
57932
- children: [
57933
- /* @__PURE__ */ jsxRuntime.jsx(outline.HomeIcon, { className: "w-5 h-5 mb-1" }),
57934
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-[10px] font-medium leading-tight", children: "Home" })
57935
- ]
57936
- }
57937
- ),
57938
- showLiveMonitorLink && canAccessPath("/live-monitor") && /* @__PURE__ */ jsxRuntime.jsxs(
57939
- "button",
57940
- {
57941
- onClick: handleLiveClick,
57942
- className: `${liveButtonClasses} mt-3`,
57943
- "aria-label": "Monitor",
57944
- tabIndex: 0,
57945
- role: "tab",
57946
- "aria-selected": isPathActive("/live-monitor"),
57947
- children: [
57948
- /* @__PURE__ */ jsxRuntime.jsx(outline.VideoCameraIcon, { className: "w-5 h-5 mb-1" }),
57949
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-[10px] font-medium leading-tight", children: "Monitor" })
57950
- ]
57951
- }
57952
- )
57953
- ] }),
57954
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
57955
- canAccessPath("/leaderboard") && /* @__PURE__ */ jsxRuntime.jsxs(
57956
- "button",
57957
- {
57958
- onClick: handleLeaderboardClick,
57959
- className: leaderboardButtonClasses,
57960
- "aria-label": "Leaderboard",
57961
- tabIndex: 0,
57962
- role: "tab",
57963
- "aria-selected": isPathActive("/leaderboard"),
57964
- children: [
57965
- /* @__PURE__ */ jsxRuntime.jsx(outline.TrophyIcon, { className: "w-5 h-5 mb-1" }),
57966
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-[10px] font-medium leading-tight", children: "Leaders" })
57967
- ]
57968
- }
57969
- ),
57970
- canAccessPath("/kpis") && /* @__PURE__ */ jsxRuntime.jsxs(
57971
- "button",
57972
- {
57973
- onClick: handleKPIsClick,
57974
- className: kpisButtonClasses,
57975
- "aria-label": "Lines",
57976
- tabIndex: 0,
57977
- role: "tab",
57978
- "aria-selected": isPathActive("/kpis"),
57979
- children: [
57980
- /* @__PURE__ */ jsxRuntime.jsx(outline.ChartBarIcon, { className: "w-5 h-5 mb-1" }),
57981
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-[10px] font-medium leading-tight", children: "Lines" })
57982
- ]
57983
- }
57984
- ),
57985
- canAccessPath("/improvement-center") && /* @__PURE__ */ jsxRuntime.jsxs(
57986
- "button",
57987
- {
57988
- onClick: handleImprovementClick,
57989
- className: improvementButtonClasses,
57990
- "aria-label": "Improvement Center",
57991
- tabIndex: 0,
57992
- role: "tab",
57993
- "aria-selected": isPathActive("/improvement-center"),
57994
- children: [
57995
- /* @__PURE__ */ jsxRuntime.jsx(outline.LightBulbIcon, { className: "w-5 h-5 mb-1" }),
57996
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-[10px] font-medium leading-tight text-center", children: "Improve" })
57997
- ]
57998
- }
57999
- ),
58000
- showSupervisorManagement,
58001
- skuEnabled && canAccessPath("/skus") && /* @__PURE__ */ jsxRuntime.jsxs(
58002
- "button",
58003
- {
58004
- onClick: handleSKUsClick,
58005
- className: skusButtonClasses,
58006
- "aria-label": "SKU Management",
58007
- tabIndex: 0,
58008
- role: "tab",
58009
- "aria-selected": isPathActive("/skus"),
58010
- children: [
58011
- /* @__PURE__ */ jsxRuntime.jsx(outline.CubeIcon, { className: "w-5 h-5 mb-1" }),
58012
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-[10px] font-medium leading-tight", children: "SKUs" })
58013
- ]
58014
- }
58015
- ),
58016
- canAccessPath("/health") && /* @__PURE__ */ jsxRuntime.jsxs(
58017
- "button",
58018
- {
58019
- onClick: handleHealthClick,
58020
- className: healthButtonClasses,
58021
- "aria-label": "System Health",
58022
- tabIndex: 0,
58023
- role: "tab",
58024
- "aria-selected": isPathActive("/health"),
58025
- children: [
58026
- /* @__PURE__ */ jsxRuntime.jsx(outline.HeartIcon, { className: "w-5 h-5 mb-1" }),
58027
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-[10px] font-medium leading-tight", children: "Health" })
58028
- ]
58029
- }
58030
- )
58031
- ] })
58032
- ] }),
58090
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 w-full py-6 px-4 overflow-y-auto", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
58091
+ showLiveMonitorLink && canAccessPath("/live-monitor") && /* @__PURE__ */ jsxRuntime.jsxs(
58092
+ "button",
58093
+ {
58094
+ onClick: handleLiveClick,
58095
+ className: liveButtonClasses,
58096
+ "aria-label": "Monitor",
58097
+ tabIndex: 0,
58098
+ role: "tab",
58099
+ "aria-selected": isPathActive("/live-monitor"),
58100
+ children: [
58101
+ /* @__PURE__ */ jsxRuntime.jsx(outline.VideoCameraIcon, { className: "w-5 h-5 mb-1" }),
58102
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-[10px] font-medium leading-tight", children: "Monitor" })
58103
+ ]
58104
+ }
58105
+ ),
58106
+ canAccessPath("/leaderboard") && /* @__PURE__ */ jsxRuntime.jsxs(
58107
+ "button",
58108
+ {
58109
+ onClick: handleLeaderboardClick,
58110
+ className: leaderboardButtonClasses,
58111
+ "aria-label": "Leaderboard",
58112
+ tabIndex: 0,
58113
+ role: "tab",
58114
+ "aria-selected": isPathActive("/leaderboard"),
58115
+ children: [
58116
+ /* @__PURE__ */ jsxRuntime.jsx(outline.TrophyIcon, { className: "w-5 h-5 mb-1" }),
58117
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-[10px] font-medium leading-tight", children: "Leaders" })
58118
+ ]
58119
+ }
58120
+ ),
58121
+ canAccessPath("/kpis") && /* @__PURE__ */ jsxRuntime.jsxs(
58122
+ "button",
58123
+ {
58124
+ onClick: handleKPIsClick,
58125
+ className: kpisButtonClasses,
58126
+ "aria-label": "Lines",
58127
+ tabIndex: 0,
58128
+ role: "tab",
58129
+ "aria-selected": isPathActive("/kpis"),
58130
+ children: [
58131
+ /* @__PURE__ */ jsxRuntime.jsx(outline.ChartBarIcon, { className: "w-5 h-5 mb-1" }),
58132
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-[10px] font-medium leading-tight", children: "Lines" })
58133
+ ]
58134
+ }
58135
+ ),
58136
+ canAccessPath("/improvement-center") && /* @__PURE__ */ jsxRuntime.jsxs(
58137
+ "button",
58138
+ {
58139
+ onClick: handleImprovementClick,
58140
+ className: improvementButtonClasses,
58141
+ "aria-label": "Improvement Center",
58142
+ tabIndex: 0,
58143
+ role: "tab",
58144
+ "aria-selected": isPathActive("/improvement-center"),
58145
+ children: [
58146
+ /* @__PURE__ */ jsxRuntime.jsx(outline.LightBulbIcon, { className: "w-5 h-5 mb-1" }),
58147
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-[10px] font-medium leading-tight text-center", children: "Improve" })
58148
+ ]
58149
+ }
58150
+ ),
58151
+ showSupervisorManagement,
58152
+ skuEnabled && canAccessPath("/skus") && /* @__PURE__ */ jsxRuntime.jsxs(
58153
+ "button",
58154
+ {
58155
+ onClick: handleSKUsClick,
58156
+ className: skusButtonClasses,
58157
+ "aria-label": "SKU Management",
58158
+ tabIndex: 0,
58159
+ role: "tab",
58160
+ "aria-selected": isPathActive("/skus"),
58161
+ children: [
58162
+ /* @__PURE__ */ jsxRuntime.jsx(outline.CubeIcon, { className: "w-5 h-5 mb-1" }),
58163
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-[10px] font-medium leading-tight", children: "SKUs" })
58164
+ ]
58165
+ }
58166
+ ),
58167
+ canAccessPath("/health") && /* @__PURE__ */ jsxRuntime.jsxs(
58168
+ "button",
58169
+ {
58170
+ onClick: handleHealthClick,
58171
+ className: healthButtonClasses,
58172
+ "aria-label": "System Health",
58173
+ tabIndex: 0,
58174
+ role: "tab",
58175
+ "aria-selected": isPathActive("/health"),
58176
+ children: [
58177
+ /* @__PURE__ */ jsxRuntime.jsx(outline.HeartIcon, { className: "w-5 h-5 mb-1" }),
58178
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-[10px] font-medium leading-tight", children: "Health" })
58179
+ ]
58180
+ }
58181
+ )
58182
+ ] }) }),
58033
58183
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full py-4 px-4 border-t border-gray-100 flex-shrink-0 flex flex-col gap-2", children: settingsItems.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
58034
58184
  "button",
58035
58185
  {
@@ -58066,23 +58216,11 @@ var SideNavBar = React144.memo(({
58066
58216
  };
58067
58217
  };
58068
58218
  return /* @__PURE__ */ jsxRuntime.jsxs("nav", { className: "px-5 py-6", children: [
58069
- canAccessPath("/") && /* @__PURE__ */ jsxRuntime.jsxs(
58070
- "button",
58071
- {
58072
- onClick: handleMobileNavClick(handleHomeClick),
58073
- className: getMobileButtonClass("/"),
58074
- "aria-label": "Home",
58075
- children: [
58076
- /* @__PURE__ */ jsxRuntime.jsx(outline.HomeIcon, { className: getIconClass("/") }),
58077
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-base font-medium", children: "Home" })
58078
- ]
58079
- }
58080
- ),
58081
58219
  showLiveMonitorLink && canAccessPath("/live-monitor") && /* @__PURE__ */ jsxRuntime.jsxs(
58082
58220
  "button",
58083
58221
  {
58084
58222
  onClick: handleMobileNavClick(handleLiveClick),
58085
- className: `${getMobileButtonClass("/live-monitor")} mt-2`,
58223
+ className: getMobileButtonClass("/live-monitor"),
58086
58224
  "aria-label": "Monitor",
58087
58225
  children: [
58088
58226
  /* @__PURE__ */ jsxRuntime.jsx(outline.VideoCameraIcon, { className: getIconClass("/live-monitor") }),
@@ -67925,6 +68063,7 @@ var UptimeBottomSection = React144.memo(({
67925
68063
  shiftDate,
67926
68064
  timezone,
67927
68065
  elapsedMinutes,
68066
+ shiftBreaks,
67928
68067
  urlDate,
67929
68068
  urlShift,
67930
68069
  navigate
@@ -68012,7 +68151,8 @@ var UptimeBottomSection = React144.memo(({
68012
68151
  shiftEnd,
68013
68152
  shiftDate,
68014
68153
  timezone,
68015
- elapsedMinutes
68154
+ elapsedMinutes,
68155
+ shiftBreaks
68016
68156
  }
68017
68157
  ) })
68018
68158
  ] })
@@ -68330,7 +68470,8 @@ var KPIDetailView = ({
68330
68470
  shiftStart: resolvedShiftStart || void 0,
68331
68471
  shiftEnd: resolvedShiftEnd || void 0,
68332
68472
  shiftDate: metric.date,
68333
- timezone: configuredTimezone
68473
+ timezone: configuredTimezone,
68474
+ shiftBreaks: shiftConfig?.shifts?.find((shift) => shift.shiftId === metric.shift_id)?.breaks || []
68334
68475
  }) : null;
68335
68476
  const idleTimeSeconds = isUptimeMode ? hasBackendUptimeSeconds ? metricIdleTimeSeconds ?? 0 : (uptimeSeries2?.idleMinutes || 0) * 60 : 0;
68336
68477
  const activeTimeSeconds = isUptimeMode ? hasBackendUptimeSeconds ? metricActiveTimeSeconds ?? 0 : (uptimeSeries2?.activeMinutes || 0) * 60 : 0;
@@ -68510,6 +68651,10 @@ var KPIDetailView = ({
68510
68651
  end: chartMetrics.shift_end || fallback.end
68511
68652
  };
68512
68653
  }, [chartMetrics?.shift_start, chartMetrics?.shift_end, chartMetrics?.shift_id, resolveShiftTimes]);
68654
+ const resolvedShiftBreaks = React144.useMemo(
68655
+ () => shiftConfig?.shifts?.find((shift) => shift.shiftId === chartMetrics?.shift_id)?.breaks || [],
68656
+ [shiftConfig?.shifts, chartMetrics?.shift_id]
68657
+ );
68513
68658
  const lineSkuBreakdown = React144.useMemo(
68514
68659
  () => resolvedLineInfo?.metrics.sku_breakdown ?? [],
68515
68660
  [resolvedLineInfo]
@@ -68643,7 +68788,8 @@ var KPIDetailView = ({
68643
68788
  shiftEnd: null,
68644
68789
  shiftDate: null,
68645
68790
  timezone: configuredTimezone,
68646
- elapsedMinutes: null
68791
+ elapsedMinutes: null,
68792
+ shiftBreaks: resolvedShiftBreaks
68647
68793
  });
68648
68794
  }
68649
68795
  return buildUptimeSeries({
@@ -68652,9 +68798,10 @@ var KPIDetailView = ({
68652
68798
  shiftEnd: resolvedShiftTimes.end,
68653
68799
  shiftDate: chartMetrics.date,
68654
68800
  timezone: configuredTimezone,
68655
- elapsedMinutes: elapsedShiftMinutes
68801
+ elapsedMinutes: elapsedShiftMinutes,
68802
+ shiftBreaks: resolvedShiftBreaks
68656
68803
  });
68657
- }, [chartMetrics, resolvedShiftTimes.start, resolvedShiftTimes.end, configuredTimezone, elapsedShiftMinutes]);
68804
+ }, [chartMetrics, resolvedShiftTimes.start, resolvedShiftTimes.end, configuredTimezone, elapsedShiftMinutes, resolvedShiftBreaks]);
68658
68805
  const lineUtilizationFromLine = React144.useMemo(() => {
68659
68806
  const efficiencyValue = Number.isFinite(chartMetrics?.avg_efficiency) ? Number(chartMetrics?.avg_efficiency) : null;
68660
68807
  if (efficiencyValue !== null) return efficiencyValue;
@@ -68681,7 +68828,8 @@ var KPIDetailView = ({
68681
68828
  shiftEnd,
68682
68829
  shiftDate,
68683
68830
  timezone: configuredTimezone,
68684
- elapsedMinutes: workspaceElapsedMinutes
68831
+ elapsedMinutes: workspaceElapsedMinutes,
68832
+ shiftBreaks: resolvedShiftBreaks
68685
68833
  });
68686
68834
  let activeMinutes = uptimeSeries2.activeMinutes;
68687
68835
  let idleMinutes = uptimeSeries2.idleMinutes;
@@ -68693,7 +68841,12 @@ var KPIDetailView = ({
68693
68841
  );
68694
68842
  const idleTimeValue = workspace.idle_time;
68695
68843
  const hasIdleTimeValue = Number.isFinite(idleTimeValue);
68696
- const fallbackDuration = workspaceElapsedMinutes ?? shiftMinutes;
68844
+ const fallbackDuration = getBreakExcludedShiftMinutes({
68845
+ shiftStart,
68846
+ shiftEnd,
68847
+ elapsedMinutes: workspaceElapsedMinutes,
68848
+ shiftBreaks: resolvedShiftBreaks
68849
+ }) ?? workspaceElapsedMinutes ?? shiftMinutes;
68697
68850
  if (!hasIdleHourlyPayload && hasIdleTimeValue && idleTimeValue > 0 && fallbackDuration > 0) {
68698
68851
  const idleSeconds = Number(idleTimeValue);
68699
68852
  const idleFromAggregate = Math.min(Math.max(idleSeconds / 60, 0), fallbackDuration);
@@ -68723,7 +68876,8 @@ var KPIDetailView = ({
68723
68876
  resolvedShiftTimes.end,
68724
68877
  chartMetrics?.date,
68725
68878
  configuredTimezone,
68726
- isCurrentShiftView
68879
+ isCurrentShiftView,
68880
+ resolvedShiftBreaks
68727
68881
  ]);
68728
68882
  const lineUptimeStats = React144.useMemo(() => {
68729
68883
  if (!isUptimeMode) {
@@ -69410,6 +69564,7 @@ var KPIDetailView = ({
69410
69564
  shiftDate: chartMetrics?.date,
69411
69565
  timezone: configuredTimezone,
69412
69566
  elapsedMinutes: elapsedShiftMinutes,
69567
+ shiftBreaks: resolvedShiftBreaks,
69413
69568
  urlDate,
69414
69569
  urlShift,
69415
69570
  navigate
@@ -69524,6 +69679,7 @@ var KPIDetailView = ({
69524
69679
  shiftDate: chartMetrics?.date,
69525
69680
  timezone: configuredTimezone,
69526
69681
  elapsedMinutes: elapsedShiftMinutes,
69682
+ shiftBreaks: resolvedShiftBreaks,
69527
69683
  urlDate,
69528
69684
  urlShift,
69529
69685
  navigate
@@ -69630,6 +69786,19 @@ var formatDateKey2 = (dateKey, timezone, options) => {
69630
69786
  return dateKey;
69631
69787
  }
69632
69788
  };
69789
+ var getFirstName = (displayName) => {
69790
+ const trimmedName = displayName.trim();
69791
+ if (!trimmedName) return "";
69792
+ return trimmedName.split(/\s+/)[0];
69793
+ };
69794
+ var formatSupervisorFirstNames = (supervisors, fallbackSupervisorNames) => {
69795
+ const firstNames = supervisors.map((supervisor) => getFirstName(supervisor.displayName)).filter((name) => name.length > 0);
69796
+ if (firstNames.length > 0) {
69797
+ return Array.from(new Set(firstNames)).join(", ");
69798
+ }
69799
+ const fallbackFirstNames = fallbackSupervisorNames.flatMap((names) => names.split(",")).map(getFirstName).filter((name) => name.length > 0);
69800
+ return fallbackFirstNames.length > 0 ? Array.from(new Set(fallbackFirstNames)).join(", ") : "Unassigned";
69801
+ };
69633
69802
  var LeaderboardCountdown = ({ targetDate, format: format10, finishedLabel = "Finished", placeholder = "--", onFinished }) => {
69634
69803
  const [time2, setTime] = React144.useState("");
69635
69804
  const hasFinishedRef = React144.useRef(false);
@@ -69755,7 +69924,7 @@ var LinesLeaderboard = ({
69755
69924
  });
69756
69925
  const supervisors = Array.from(supervisorByUserId.values());
69757
69926
  const primarySupervisor = supervisors[0];
69758
- const supervisorName = supervisors.length > 1 ? `${supervisors.length} supervisors` : primarySupervisor?.displayName || fallbackSupervisorNames[0] || "Unassigned";
69927
+ const supervisorName = formatSupervisorFirstNames(supervisors, fallbackSupervisorNames);
69759
69928
  const supervisorImage = primarySupervisor?.profilePhotoUrl || null;
69760
69929
  return {
69761
69930
  ...row,
@@ -70015,21 +70184,49 @@ var LinesLeaderboard = ({
70015
70184
  ] }) }) }) })
70016
70185
  ] });
70017
70186
  };
70187
+ var SIGNAL_PILL_STYLES = {
70188
+ stable: {
70189
+ container: "bg-emerald-100 text-emerald-700 border border-emerald-200",
70190
+ dot: "bg-emerald-500"
70191
+ },
70192
+ warning: {
70193
+ container: "bg-amber-100 text-amber-700 border border-amber-200",
70194
+ dot: "bg-amber-500"
70195
+ },
70196
+ attention: {
70197
+ container: "bg-red-100 text-red-700 border border-red-200",
70198
+ dot: "bg-red-500"
70199
+ }
70200
+ };
70201
+ var KpiSignalPill = ({ signal, efficiencyLegend }) => {
70202
+ const status = getKpiSignalStatus(signal, efficiencyLegend);
70203
+ const label = getKpiSignalLabel(signal, efficiencyLegend);
70204
+ if (!status || !label) return null;
70205
+ const styles2 = SIGNAL_PILL_STYLES[status];
70206
+ return /* @__PURE__ */ jsxRuntime.jsxs(
70207
+ "div",
70208
+ {
70209
+ 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 ${styles2.container}`,
70210
+ style: { minWidth: "fit-content" },
70211
+ children: [
70212
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-2 h-2 rounded-full ${styles2.dot} animate-pulse` }),
70213
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: label })
70214
+ ]
70215
+ }
70216
+ );
70217
+ };
70018
70218
  var LineCard = ({
70019
70219
  line,
70020
70220
  kpis,
70021
70221
  isLoading,
70022
70222
  error,
70023
70223
  onClick,
70224
+ efficiencyLegend,
70024
70225
  supervisorEnabled = false,
70025
70226
  supervisorName,
70026
70227
  supervisors
70027
70228
  }) => {
70028
70229
  const isUptimeLine = (line.monitoring_mode ?? "output") === "uptime";
70029
- const isOnTrack = React144__namespace.default.useMemo(() => {
70030
- if (!kpis) return null;
70031
- return isEfficiencyOnTrack(kpis.efficiency.value);
70032
- }, [kpis]);
70033
70230
  return /* @__PURE__ */ jsxRuntime.jsxs(
70034
70231
  motion.div,
70035
70232
  {
@@ -70101,10 +70298,7 @@ var LineCard = ({
70101
70298
  ] })
70102
70299
  ] })
70103
70300
  ] }),
70104
- !isUptimeLine && 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: [
70105
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-2 h-2 rounded-full ${isOnTrack ? "bg-emerald-500" : "bg-red-500"} animate-pulse` }),
70106
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: isOnTrack ? "On Track" : "Behind" })
70107
- ] })
70301
+ !isUptimeLine && kpis && /* @__PURE__ */ jsxRuntime.jsx(KpiSignalPill, { signal: kpis.lineSignal, efficiencyLegend })
70108
70302
  ] }) }),
70109
70303
  isLoading && !kpis && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
70110
70304
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "animate-pulse", children: [
@@ -70172,12 +70366,9 @@ var KpiGroupCard = ({
70172
70366
  isLoading,
70173
70367
  error,
70174
70368
  isUptimeMode,
70369
+ efficiencyLegend,
70175
70370
  onClick
70176
70371
  }) => {
70177
- const isOnTrack = React144__namespace.default.useMemo(() => {
70178
- if (!kpis) return null;
70179
- return isEfficiencyOnTrack(kpis.efficiency.value);
70180
- }, [kpis]);
70181
70372
  const outputTarget = Number(kpis?.outputProgress?.target ?? 0);
70182
70373
  const outputCurrent = Number(kpis?.outputProgress?.current ?? 0);
70183
70374
  const progressPercent = outputTarget > 0 ? Math.min(outputCurrent / outputTarget * 100, 100) : 0;
@@ -70201,10 +70392,7 @@ var KpiGroupCard = ({
70201
70392
  ),
70202
70393
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-xs font-medium text-gray-500", children: subtitle })
70203
70394
  ] }),
70204
- !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: [
70205
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-2 h-2 rounded-full ${isOnTrack ? "bg-emerald-500" : "bg-red-500"} animate-pulse` }),
70206
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: isOnTrack ? "On Track" : "Behind" })
70207
- ] })
70395
+ !isUptimeMode && kpis && /* @__PURE__ */ jsxRuntime.jsx(KpiSignalPill, { signal: kpis.lineSignal, efficiencyLegend })
70208
70396
  ] }) }),
70209
70397
  isLoading && !kpis && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
70210
70398
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "animate-pulse", children: [
@@ -70521,7 +70709,8 @@ var KPIsOverviewView = ({
70521
70709
  const {
70522
70710
  lineMetrics,
70523
70711
  isLoading: metricsLoading,
70524
- error: metricsError
70712
+ error: metricsError,
70713
+ efficiencyLegend
70525
70714
  } = useDashboardMetrics({
70526
70715
  lineId: factoryViewId,
70527
70716
  userAccessibleLineIds: metricsLineIds
@@ -70865,7 +71054,11 @@ var KPIsOverviewView = ({
70865
71054
  trackProps.output_current = kpis.outputProgress?.current;
70866
71055
  trackProps.output_target = kpis.outputProgress?.target;
70867
71056
  trackProps.underperforming_workers = kpis.underperformingWorkers?.current;
70868
- trackProps.status = isEfficiencyOnTrack(kpis.efficiency?.value) ? "On Track" : "Behind";
71057
+ const signalLabel = getKpiSignalLabel(kpis.lineSignal, efficiencyLegend);
71058
+ if (signalLabel) {
71059
+ trackProps.status = signalLabel;
71060
+ trackProps.signal_source = kpis.lineSignal?.source ?? null;
71061
+ }
70869
71062
  }
70870
71063
  trackCoreEvent("Line Card Clicked", trackProps);
70871
71064
  if (activeTab === "leaderboard" && timeRange === "today" && isHistoricalLeaderboardDaily) {
@@ -70951,6 +71144,7 @@ var KPIsOverviewView = ({
70951
71144
  isLoading: metricsLoading,
70952
71145
  error: metricsError,
70953
71146
  onClick: (kpis) => handleLineClick(line, kpis),
71147
+ efficiencyLegend,
70954
71148
  supervisorEnabled,
70955
71149
  supervisorName: supervisorNamesByLineId.get(line.id) || null,
70956
71150
  supervisors: supervisorsByLineId?.get(line.id)
@@ -70972,6 +71166,7 @@ var KPIsOverviewView = ({
70972
71166
  isLoading: metricsLoading,
70973
71167
  error: metricsError,
70974
71168
  isUptimeMode: viewType === "machine",
71169
+ efficiencyLegend,
70975
71170
  onClick
70976
71171
  },
70977
71172
  key
@@ -77009,6 +77204,10 @@ var WorkspaceDetailView = ({
77009
77204
  timezone
77010
77205
  });
77011
77206
  }, [isCurrentShiftView, workspace?.shift_start, workspace?.shift_end, idleClipDate, timezone]);
77207
+ const workspaceShiftBreaks = React144.useMemo(
77208
+ () => shiftConfig?.shifts?.find((shift2) => shift2.shiftId === workspace?.shift_id)?.breaks || [],
77209
+ [shiftConfig?.shifts, workspace?.shift_id]
77210
+ );
77012
77211
  const uptimeSeries = React144.useMemo(
77013
77212
  () => buildUptimeSeries({
77014
77213
  idleTimeHourly: workspace?.idle_time_hourly,
@@ -77016,17 +77215,26 @@ var WorkspaceDetailView = ({
77016
77215
  shiftEnd: workspace?.shift_end,
77017
77216
  shiftDate: idleClipDate,
77018
77217
  timezone,
77019
- elapsedMinutes: elapsedShiftMinutes
77218
+ elapsedMinutes: elapsedShiftMinutes,
77219
+ shiftBreaks: workspaceShiftBreaks
77020
77220
  }),
77021
- [workspace?.idle_time_hourly, workspace?.shift_start, workspace?.shift_end, idleClipDate, timezone, elapsedShiftMinutes]
77221
+ [workspace?.idle_time_hourly, workspace?.shift_start, workspace?.shift_end, idleClipDate, timezone, elapsedShiftMinutes, workspaceShiftBreaks]
77022
77222
  );
77023
77223
  const uptimePieData = React144.useMemo(() => {
77024
77224
  if (!isUptimeMode) return [];
77025
77225
  let activeMinutes = uptimeSeries.activeMinutes;
77026
77226
  let idleMinutes = uptimeSeries.idleMinutes;
77027
77227
  let totalMinutes = uptimeSeries.availableMinutes;
77028
- const fallbackDuration = elapsedShiftMinutes ?? shiftDurationMinutes;
77029
- if (!uptimeSeries.hasData && fallbackDuration !== null && fallbackDuration !== void 0) {
77228
+ const fallbackDuration = getBreakExcludedShiftMinutes({
77229
+ shiftStart: workspace?.shift_start,
77230
+ shiftEnd: workspace?.shift_end,
77231
+ elapsedMinutes: elapsedShiftMinutes,
77232
+ shiftBreaks: workspaceShiftBreaks
77233
+ }) ?? elapsedShiftMinutes ?? shiftDurationMinutes;
77234
+ const hasIdleHourlyPayload = Boolean(
77235
+ workspace?.idle_time_hourly && typeof workspace.idle_time_hourly === "object" && Object.keys(workspace.idle_time_hourly).length > 0
77236
+ );
77237
+ if (!uptimeSeries.hasData && !hasIdleHourlyPayload && fallbackDuration !== null && fallbackDuration !== void 0) {
77030
77238
  const idleSeconds = Number(workspace?.idle_time || 0);
77031
77239
  const idleFromAggregate = Math.min(Math.max(Math.round(idleSeconds / 60), 0), fallbackDuration);
77032
77240
  const activeFromAggregate = Math.max(fallbackDuration - idleFromAggregate, 0);
@@ -77039,7 +77247,7 @@ var WorkspaceDetailView = ({
77039
77247
  { name: "Productive", value: activeMinutes },
77040
77248
  { name: "Idle", value: idleMinutes }
77041
77249
  ];
77042
- }, [isUptimeMode, uptimeSeries, shiftDurationMinutes, elapsedShiftMinutes, workspace?.idle_time]);
77250
+ }, [isUptimeMode, uptimeSeries, shiftDurationMinutes, elapsedShiftMinutes, workspace?.idle_time, workspace?.shift_start, workspace?.shift_end, workspaceShiftBreaks]);
77043
77251
  const overviewTabLabel = isUptimeMode ? "Utilization" : "Efficiency";
77044
77252
  const idleClipFetchEnabled = Boolean(
77045
77253
  workspaceId && idleClipDate && idleClipShiftId !== void 0 && activeTab === "overview" && idleTimeVlmEnabled && isOutputLayout
@@ -77518,7 +77726,8 @@ var WorkspaceDetailView = ({
77518
77726
  shiftEnd: workspace.shift_end,
77519
77727
  shiftDate: idleClipDate,
77520
77728
  timezone,
77521
- elapsedMinutes: elapsedShiftMinutes
77729
+ elapsedMinutes: elapsedShiftMinutes,
77730
+ shiftBreaks: workspaceShiftBreaks
77522
77731
  }
77523
77732
  ) : isAssemblyCycleLayout ? shouldShowCycleTimeUnavailableState ? cycleTimeUnavailableView : shouldShowCycleTimeChart ? /* @__PURE__ */ jsxRuntime.jsx(
77524
77733
  CycleTimeOverTimeChart,
@@ -77671,7 +77880,8 @@ var WorkspaceDetailView = ({
77671
77880
  shiftEnd: workspace.shift_end,
77672
77881
  shiftDate: idleClipDate,
77673
77882
  timezone,
77674
- elapsedMinutes: elapsedShiftMinutes
77883
+ elapsedMinutes: elapsedShiftMinutes,
77884
+ shiftBreaks: workspaceShiftBreaks
77675
77885
  }
77676
77886
  ) : isAssemblyCycleLayout ? shouldShowCycleTimeUnavailableState ? cycleTimeUnavailableView : shouldShowCycleTimeChart ? /* @__PURE__ */ jsxRuntime.jsx(
77677
77887
  CycleTimeOverTimeChart,
@@ -87143,6 +87353,7 @@ exports.KPIDetailView = KPIDetailView_default;
87143
87353
  exports.KPIGrid = KPIGrid;
87144
87354
  exports.KPIHeader = KPIHeader;
87145
87355
  exports.KPISection = KPISection;
87356
+ exports.KPI_SIGNAL_LABELS = KPI_SIGNAL_LABELS;
87146
87357
  exports.KPIsOverviewView = KPIsOverviewView_default;
87147
87358
  exports.LINE_1_UUID = LINE_1_UUID;
87148
87359
  exports.LINE_2_UUID = LINE_2_UUID;
@@ -87276,6 +87487,7 @@ exports.WorkspaceWhatsAppShareButton = WorkspaceWhatsAppShareButton;
87276
87487
  exports.actionService = actionService;
87277
87488
  exports.addSentryBreadcrumb = addSentryBreadcrumb;
87278
87489
  exports.aggregateKPIsFromLineMetricsRows = aggregateKPIsFromLineMetricsRows;
87490
+ exports.aggregateLineSignals = aggregateLineSignals;
87279
87491
  exports.alertsService = alertsService;
87280
87492
  exports.apiUtils = apiUtils;
87281
87493
  exports.areAllLinesOnSameShift = areAllLinesOnSameShift;
@@ -87380,6 +87592,8 @@ exports.getDefaultCameraStreamUrl = getDefaultCameraStreamUrl;
87380
87592
  exports.getDefaultLineId = getDefaultLineId;
87381
87593
  exports.getDefaultTabForWorkspace = getDefaultTabForWorkspace;
87382
87594
  exports.getInitials = getInitials;
87595
+ exports.getKpiSignalLabel = getKpiSignalLabel;
87596
+ exports.getKpiSignalStatus = getKpiSignalStatus;
87383
87597
  exports.getLineDisplayName = getLineDisplayName;
87384
87598
  exports.getManufacturingInsights = getManufacturingInsights;
87385
87599
  exports.getMetricsTablePrefix = getMetricsTablePrefix;