@optifye/dashboard-core 6.11.37 → 6.11.39

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
@@ -3423,6 +3423,12 @@ var isFullMonthRange = (range, year, monthIndex) => {
3423
3423
  const bounds = getMonthKeyBounds(year, monthIndex);
3424
3424
  return range.startKey === bounds.startKey && range.endKey === bounds.endKey;
3425
3425
  };
3426
+ var getMonthlyTrendComparisonLabel = (range, year, monthIndex) => {
3427
+ if (isFullMonthRange(range, year, monthIndex)) {
3428
+ return "last month";
3429
+ }
3430
+ return range.startKey === range.endKey ? "previous day" : "previous range";
3431
+ };
3426
3432
  var getMonthWeekRanges = (year, monthIndex, timezone, maxKey) => {
3427
3433
  const totalDays = new Date(year, monthIndex + 1, 0).getDate();
3428
3434
  const ranges = [];
@@ -12290,6 +12296,7 @@ var toWorkspaceDetailedMetrics = ({
12290
12296
  return {
12291
12297
  workspace_id: data.workspace_id,
12292
12298
  workspace_name: data.workspace_name,
12299
+ workspace_display_name: typeof data.workspace_display_name === "string" ? data.workspace_display_name : typeof data.display_name === "string" ? data.display_name : null,
12293
12300
  line_id: data.line_id,
12294
12301
  line_name: data.line_name || "",
12295
12302
  line_assembly_enabled: data.line_assembly_enabled === true || data.assembly_enabled === true,
@@ -18438,13 +18445,27 @@ var useMonthlyTrend = (params) => {
18438
18445
  if (typeof params.shiftId === "number") {
18439
18446
  searchParams.append("shift_id", params.shiftId.toString());
18440
18447
  }
18448
+ if (params.startDate && params.endDate) {
18449
+ searchParams.append("start_date", params.startDate);
18450
+ searchParams.append("end_date", params.endDate);
18451
+ }
18441
18452
  if (params.entityType === "line") {
18442
18453
  searchParams.append("line_id", params.entityId);
18443
18454
  } else {
18444
18455
  searchParams.append("workspace_id", params.entityId);
18445
18456
  }
18446
18457
  return searchParams.toString();
18447
- }, [params.entityId, params.entityType, params.month, params.year, params.shiftId, entityConfig?.companyId]);
18458
+ }, [
18459
+ params.companyId,
18460
+ params.endDate,
18461
+ params.entityId,
18462
+ params.entityType,
18463
+ params.month,
18464
+ params.shiftId,
18465
+ params.startDate,
18466
+ params.year,
18467
+ entityConfig?.companyId
18468
+ ]);
18448
18469
  React143.useEffect(() => {
18449
18470
  let isMounted = true;
18450
18471
  if (!queryString || !supabase) {
@@ -47290,6 +47311,10 @@ var LineMonthlyHistory = ({
47290
47311
  const efficiencyImproved = efficiencyDelta >= 0;
47291
47312
  const EfficiencyTrendIcon = efficiencyImproved ? lucideReact.ArrowUp : lucideReact.ArrowDown;
47292
47313
  const efficiencyTrendText = `${Math.abs(efficiencyDelta).toFixed(1)}%`;
47314
+ const trendComparisonLabel = React143.useMemo(
47315
+ () => getMonthlyTrendComparisonLabel(normalizedRange, year, month),
47316
+ [month, normalizedRange, year]
47317
+ );
47293
47318
  const outputDelta = trendSummary?.avg_daily_output?.delta_pp ?? 0;
47294
47319
  const outputImproved = outputDelta >= 0;
47295
47320
  const OutputTrendIcon = outputImproved ? lucideReact.ArrowUp : lucideReact.ArrowDown;
@@ -47540,10 +47565,7 @@ var LineMonthlyHistory = ({
47540
47565
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-base font-bold text-gray-900 whitespace-nowrap", children: uptimeSummary?.avgDailyStoppages ?? 0 }),
47541
47566
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-1 ${stoppagesImproved ? "bg-emerald-50 text-emerald-600" : "bg-red-50 text-red-600"} px-1.5 py-0.5 rounded-full text-[10px] font-medium whitespace-nowrap flex-shrink-0`, children: [
47542
47567
  /* @__PURE__ */ jsxRuntime.jsx(StoppagesTrendIcon, { className: "w-3 h-3" }),
47543
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
47544
- stoppagesTrendText,
47545
- " vs last month"
47546
- ] })
47568
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: `${stoppagesTrendText} vs ${trendComparisonLabel}` })
47547
47569
  ] })
47548
47570
  ] })
47549
47571
  ] }),
@@ -47553,10 +47575,7 @@ var LineMonthlyHistory = ({
47553
47575
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-base font-bold text-gray-900 whitespace-nowrap", children: formatIdleTime(uptimeSummary?.avgIdleTime ?? 0) }),
47554
47576
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-1 ${idleImproved ? "bg-emerald-50 text-emerald-600" : "bg-red-50 text-red-600"} px-1.5 py-0.5 rounded-full text-[10px] font-medium whitespace-nowrap flex-shrink-0`, children: [
47555
47577
  /* @__PURE__ */ jsxRuntime.jsx(IdleTrendIcon, { className: "w-3 h-3" }),
47556
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
47557
- idleTrendText,
47558
- " vs last month"
47559
- ] })
47578
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: `${idleTrendText} vs ${trendComparisonLabel}` })
47560
47579
  ] })
47561
47580
  ] })
47562
47581
  ] })
@@ -47570,10 +47589,7 @@ var LineMonthlyHistory = ({
47570
47589
  ] }),
47571
47590
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-1 ${efficiencyImproved ? "bg-emerald-50 text-emerald-600" : "bg-red-50 text-red-600"} px-1.5 py-0.5 rounded-full text-[10px] font-medium whitespace-nowrap flex-shrink-0`, children: [
47572
47591
  /* @__PURE__ */ jsxRuntime.jsx(EfficiencyTrendIcon, { className: "w-3 h-3" }),
47573
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
47574
- efficiencyTrendText,
47575
- " vs last month"
47576
- ] })
47592
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: `${efficiencyTrendText} vs ${trendComparisonLabel}` })
47577
47593
  ] })
47578
47594
  ] })
47579
47595
  ] }),
@@ -47583,10 +47599,7 @@ var LineMonthlyHistory = ({
47583
47599
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xl font-bold text-gray-900", children: Math.round(avgOutput).toLocaleString() }),
47584
47600
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-1 ${outputImproved ? "bg-emerald-50 text-emerald-600" : "bg-red-50 text-red-600"} px-1.5 py-0.5 rounded-full text-[10px] font-medium whitespace-nowrap flex-shrink-0`, children: [
47585
47601
  /* @__PURE__ */ jsxRuntime.jsx(OutputTrendIcon, { className: "w-3 h-3" }),
47586
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
47587
- outputTrendText,
47588
- " vs last month"
47589
- ] })
47602
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: `${outputTrendText} vs ${trendComparisonLabel}` })
47590
47603
  ] })
47591
47604
  ] })
47592
47605
  ] })
@@ -47597,10 +47610,7 @@ var LineMonthlyHistory = ({
47597
47610
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-xs sm:text-sm font-bold text-gray-700 text-left", children: "Utilization" }),
47598
47611
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-1 ${utilizationImproved ? "bg-emerald-50 text-emerald-600" : "bg-red-50 text-red-600"} px-1.5 py-0.5 rounded-full text-[10px] font-medium whitespace-nowrap flex-shrink-0`, children: [
47599
47612
  /* @__PURE__ */ jsxRuntime.jsx(UtilizationTrendIcon, { className: "w-3 h-3" }),
47600
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
47601
- utilizationTrendText,
47602
- " vs last month"
47603
- ] })
47613
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: `${utilizationTrendText} vs ${trendComparisonLabel}` })
47604
47614
  ] })
47605
47615
  ] }),
47606
47616
  pieChartData.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full h-[140px] sm:h-[160px] flex items-center overflow-hidden", children: [
@@ -47952,19 +47962,33 @@ var LineMonthlyPdfGenerator = ({
47952
47962
  is_full_month: fullRange
47953
47963
  });
47954
47964
  const doc = new jsPDF.jsPDF();
47955
- doc.setFontSize(14);
47956
- doc.setFont("helvetica", "bold");
47957
- doc.setTextColor(50, 50, 50);
47958
- doc.text("OPTIFYE.AI", 20, 15);
47959
- doc.setFontSize(11);
47960
- doc.setFont("helvetica", "normal");
47961
- doc.setTextColor(80, 80, 80);
47962
- const reportText = isUptimeMode ? "MONTHLY UTILIZATION REPORT" : "MONTHLY PERFORMANCE REPORT";
47963
- const reportTextWidth = doc.getStringUnitWidth(reportText) * 11 / doc.internal.scaleFactor;
47964
- doc.text(reportText, doc.internal.pageSize.width - 20 - reportTextWidth, 15);
47965
- doc.setDrawColor(200, 200, 200);
47966
- doc.setLineWidth(0.5);
47967
- doc.line(20, 20, 190, 20);
47965
+ const pageHeight = typeof doc.internal.pageSize.height === "number" ? Number(doc.internal.pageSize.height) : 297;
47966
+ const footerY = pageHeight - 17;
47967
+ const maxContentY = footerY - 10;
47968
+ const generatedText = `Generated on ${(/* @__PURE__ */ new Date()).toLocaleString("en-IN", { timeZone: "Asia/Kolkata" })}`;
47969
+ const dailySectionTitle = isUptimeMode ? "Daily Utilization Summary" : "Daily Performance Summary";
47970
+ const drawPageChrome = () => {
47971
+ doc.setFontSize(14);
47972
+ doc.setFont("helvetica", "bold");
47973
+ doc.setTextColor(50, 50, 50);
47974
+ doc.text("OPTIFYE.AI", 20, 15);
47975
+ doc.setFontSize(11);
47976
+ doc.setFont("helvetica", "normal");
47977
+ doc.setTextColor(80, 80, 80);
47978
+ const reportText = isUptimeMode ? "MONTHLY UTILIZATION REPORT" : "MONTHLY PERFORMANCE REPORT";
47979
+ const reportTextWidth = doc.getStringUnitWidth(reportText) * 11 / doc.internal.scaleFactor;
47980
+ doc.text(reportText, doc.internal.pageSize.width - 20 - reportTextWidth, 15);
47981
+ doc.setDrawColor(200, 200, 200);
47982
+ doc.setLineWidth(0.5);
47983
+ doc.line(20, 20, 190, 20);
47984
+ };
47985
+ const drawFooter = () => {
47986
+ doc.setFontSize(9);
47987
+ doc.setTextColor(130, 130, 130);
47988
+ doc.text(generatedText, 20, footerY);
47989
+ doc.setTextColor(0, 0, 0);
47990
+ };
47991
+ drawPageChrome();
47968
47992
  doc.setFillColor(250, 250, 250);
47969
47993
  doc.roundedRect(15, 25, 180, 55, 3, 3, "F");
47970
47994
  doc.setFontSize(32);
@@ -47999,6 +48023,10 @@ var LineMonthlyPdfGenerator = ({
47999
48023
  if (shift.hasData !== void 0) return shift.hasData;
48000
48024
  return shift.total_workspaces > 0 || shift.avg_efficiency > 0 || shift.underperforming_workspaces > 0 || (shift.available_time_seconds ?? 0) > 0 || (shift.idle_time_seconds ?? 0) > 0 || (shift.output ?? 0) > 0;
48001
48025
  };
48026
+ const dailyEntries = validDays.map((dayData) => {
48027
+ const shift = getLineShiftData2(dayData, selectedShiftId);
48028
+ return { dayData, shift };
48029
+ }).filter(({ shift }) => hasShiftData(shift)).sort((left, right) => new Date(right.dayData.date).getTime() - new Date(left.dayData.date).getTime());
48002
48030
  const getUptimeTotals2 = (shift, hasData) => {
48003
48031
  if (!hasData || !shift) {
48004
48032
  return { availableSeconds: 0, productiveSeconds: 0, idleSeconds: 0 };
@@ -48125,46 +48153,45 @@ var LineMonthlyPdfGenerator = ({
48125
48153
  }
48126
48154
  const dailySeparatorY = isUptimeMode ? 180 : 165;
48127
48155
  const dailySectionStartY = isUptimeMode ? 185 : 170;
48128
- const dailyTitleY = isUptimeMode ? 195 : 180;
48129
- const dailyHeaderY = isUptimeMode ? 200 : 185;
48130
- const dailyHeaderTextY = isUptimeMode ? 205 : 190;
48131
- const dailyHeaderLineY = isUptimeMode ? 208 : 193;
48132
48156
  const dailyContentStartY = isUptimeMode ? 215 : 200;
48133
- const dailyMaxY = isUptimeMode ? 260 : 245;
48134
48157
  doc.setDrawColor(180, 180, 180);
48135
48158
  doc.setLineWidth(0.8);
48136
48159
  doc.line(20, dailySeparatorY, 190, dailySeparatorY);
48137
- doc.setFillColor(245, 245, 245);
48138
- doc.roundedRect(15, dailySectionStartY, 180, 85, 3, 3, "F");
48139
- doc.setFontSize(18);
48140
- doc.setFont("helvetica", "bold");
48141
- doc.setTextColor(40, 40, 40);
48142
- doc.text(isUptimeMode ? "Daily Utilization Summary" : "Daily Performance Summary", 20, dailyTitleY);
48143
- doc.setTextColor(0, 0, 0);
48144
- if (validDays.length > 0) {
48160
+ const renderDailyTablePage = (startIndex, sectionY, title) => {
48161
+ const rowHeight = 8;
48162
+ const tableHeaderY = sectionY + 15;
48163
+ const headerTextY = tableHeaderY + 5;
48164
+ const firstRowY = headerTextY + 10;
48165
+ const rowsPerPage = Math.max(1, Math.floor((maxContentY - firstRowY) / rowHeight));
48166
+ const pageEntries = dailyEntries.slice(startIndex, startIndex + rowsPerPage);
48167
+ const endY = firstRowY + pageEntries.length * rowHeight;
48168
+ const sectionHeight = Math.max(40, endY - sectionY + 5);
48169
+ doc.setFillColor(245, 245, 245);
48170
+ doc.roundedRect(15, sectionY, 180, sectionHeight, 3, 3, "F");
48171
+ doc.setFontSize(18);
48172
+ doc.setFont("helvetica", "bold");
48173
+ doc.setTextColor(40, 40, 40);
48174
+ doc.text(title, 20, sectionY + 10);
48175
+ doc.setTextColor(0, 0, 0);
48145
48176
  doc.setFontSize(10);
48146
48177
  doc.setFont("helvetica", "bold");
48147
48178
  doc.setFillColor(240, 240, 240);
48148
- doc.roundedRect(20, dailyHeaderY, 170, 7, 1, 1, "F");
48149
- doc.text("Date", 25, dailyHeaderTextY);
48179
+ doc.roundedRect(20, tableHeaderY, 170, 7, 1, 1, "F");
48180
+ doc.text("Date", 25, headerTextY);
48150
48181
  if (isUptimeMode) {
48151
- doc.text("Utilization", 95, dailyHeaderTextY);
48182
+ doc.text("Utilization", 95, headerTextY);
48152
48183
  } else {
48153
- doc.text("Actual", 60, dailyHeaderTextY);
48154
- doc.text("Standard", 95, dailyHeaderTextY);
48155
- doc.text("Efficiency", 135, dailyHeaderTextY);
48156
- doc.text("Status", 170, dailyHeaderTextY);
48184
+ doc.text("Actual", 60, headerTextY);
48185
+ doc.text("Standard", 95, headerTextY);
48186
+ doc.text("Efficiency", 135, headerTextY);
48187
+ doc.text("Status", 170, headerTextY);
48157
48188
  }
48158
48189
  doc.setLineWidth(0.2);
48159
48190
  doc.setDrawColor(220, 220, 220);
48160
- doc.line(20, dailyHeaderLineY, 190, dailyHeaderLineY);
48191
+ doc.line(20, headerTextY + 3, 190, headerTextY + 3);
48161
48192
  doc.setFont("helvetica", "normal");
48162
- let yPos = dailyContentStartY;
48163
- const recentDays = validDays.slice(-10).reverse();
48164
- recentDays.forEach((dayData, index) => {
48165
- if (yPos > dailyMaxY) return;
48166
- const shift = getLineShiftData2(dayData, selectedShiftId);
48167
- if (!hasShiftData(shift)) return;
48193
+ let yPos = firstRowY;
48194
+ pageEntries.forEach(({ dayData, shift }, index) => {
48168
48195
  if (isUptimeMode) {
48169
48196
  doc.setDrawColor(200, 200, 200);
48170
48197
  doc.setLineWidth(0.1);
@@ -48205,12 +48232,34 @@ var LineMonthlyPdfGenerator = ({
48205
48232
  }
48206
48233
  doc.setTextColor(0, 0, 0);
48207
48234
  }
48208
- yPos += 8;
48235
+ yPos += rowHeight;
48209
48236
  });
48210
48237
  if (!isUptimeMode) {
48211
48238
  doc.setLineWidth(0.2);
48212
48239
  doc.setDrawColor(220, 220, 220);
48213
- doc.roundedRect(20, dailyHeaderY, 170, yPos - dailyHeaderY - 3, 1, 1, "S");
48240
+ doc.roundedRect(20, tableHeaderY, 170, yPos - tableHeaderY - 3, 1, 1, "S");
48241
+ }
48242
+ return startIndex + pageEntries.length;
48243
+ };
48244
+ if (dailyEntries.length > 0) {
48245
+ let renderedEntries = renderDailyTablePage(0, dailySectionStartY, dailySectionTitle);
48246
+ while (renderedEntries < dailyEntries.length) {
48247
+ drawFooter();
48248
+ doc.addPage();
48249
+ drawPageChrome();
48250
+ doc.setFontSize(12);
48251
+ doc.setFont("helvetica", "bold");
48252
+ doc.setTextColor(40, 40, 40);
48253
+ doc.text(lineName || "Line", 20, 32);
48254
+ doc.setFont("helvetica", "normal");
48255
+ doc.setTextColor(90, 90, 90);
48256
+ doc.text(`${monthName} \u2022 ${shiftType} \u2022 ${reportStartStr} - ${reportEndStr}`, 20, 40);
48257
+ doc.setTextColor(0, 0, 0);
48258
+ renderedEntries = renderDailyTablePage(
48259
+ renderedEntries,
48260
+ 48,
48261
+ `${dailySectionTitle} (cont.)`
48262
+ );
48214
48263
  }
48215
48264
  } else {
48216
48265
  doc.setFontSize(12);
@@ -48223,20 +48272,9 @@ var LineMonthlyPdfGenerator = ({
48223
48272
  const isCycleTimeWorkspace = (workspace) => workspace.metric_mode === "cycle_time" || lineAssembly && (workspace.avg_cycle_time !== void 0 || workspace.ideal_cycle_time !== void 0 || workspace.cycle_ratio !== void 0);
48224
48273
  const showCycleTimePoorestPerformers = !isUptimeMode && poorestWorkspaces.some(isCycleTimeWorkspace);
48225
48274
  if (poorestWorkspaces && poorestWorkspaces.length > 0) {
48275
+ drawFooter();
48226
48276
  doc.addPage();
48227
- doc.setFontSize(14);
48228
- doc.setFont("helvetica", "bold");
48229
- doc.setTextColor(50, 50, 50);
48230
- doc.text("OPTIFYE.AI", 20, 15);
48231
- doc.setFontSize(11);
48232
- doc.setFont("helvetica", "normal");
48233
- doc.setTextColor(80, 80, 80);
48234
- const reportText2 = isUptimeMode ? "MONTHLY UTILIZATION REPORT" : "MONTHLY PERFORMANCE REPORT";
48235
- const reportTextWidth2 = doc.getStringUnitWidth(reportText2) * 11 / doc.internal.scaleFactor;
48236
- doc.text(reportText2, doc.internal.pageSize.width - 20 - reportTextWidth2, 15);
48237
- doc.setDrawColor(200, 200, 200);
48238
- doc.setLineWidth(0.5);
48239
- doc.line(20, 20, 190, 20);
48277
+ drawPageChrome();
48240
48278
  doc.setFontSize(18);
48241
48279
  doc.setFont("helvetica", "bold");
48242
48280
  doc.setTextColor(40, 40, 40);
@@ -48302,10 +48340,7 @@ var LineMonthlyPdfGenerator = ({
48302
48340
  doc.setDrawColor(220, 220, 220);
48303
48341
  doc.roundedRect(20, 45, 170, yPos2 - 45 - 5, 1, 1, "S");
48304
48342
  }
48305
- doc.setFontSize(9);
48306
- doc.setTextColor(130, 130, 130);
48307
- const generatedText = `Generated on ${(/* @__PURE__ */ new Date()).toLocaleString("en-IN", { timeZone: "Asia/Kolkata" })}`;
48308
- doc.text(generatedText, 20, 280);
48343
+ drawFooter();
48309
48344
  const fileName = `${lineName || "Line"}_${monthName.replace(" ", "_")}_${shiftType.replace(" ", "_")}.pdf`;
48310
48345
  doc.save(fileName);
48311
48346
  } catch (error) {
@@ -49875,6 +49910,10 @@ var formatHours = (value) => {
49875
49910
  if (Math.abs(rounded) < 0.05) return "0h";
49876
49911
  return Number.isInteger(rounded) ? `${rounded}h` : `${rounded.toFixed(1)}h`;
49877
49912
  };
49913
+ var formatCycleSeconds = (value) => {
49914
+ if (!Number.isFinite(value)) return "0.0s";
49915
+ return `${value.toFixed(1)}s`;
49916
+ };
49878
49917
  var CustomTooltip3 = ({ active, payload, label, isUptimeMode }) => {
49879
49918
  if (!active || !payload || payload.length === 0) return null;
49880
49919
  if (isUptimeMode) {
@@ -50185,6 +50224,10 @@ var WorkspaceMonthlyHistory = ({
50185
50224
  avgIdleTime: Math.round(totalIdleTime / filteredShifts.length)
50186
50225
  };
50187
50226
  }, [analysisMonthlyData, selectedShiftId, isUptimeMode, shiftWorkSeconds]);
50227
+ const trendComparisonLabel = React143.useMemo(
50228
+ () => getMonthlyTrendComparisonLabel(normalizedRange, year, month),
50229
+ [month, normalizedRange, year]
50230
+ );
50188
50231
  const efficiencyDelta = trendSummary?.avg_efficiency?.delta_pp ?? 0;
50189
50232
  const efficiencyImproved = efficiencyDelta >= 0;
50190
50233
  const assemblyRangeCycleTime = React143.useMemo(() => {
@@ -50200,17 +50243,17 @@ var WorkspaceMonthlyHistory = ({
50200
50243
  const cycleWorsened = cycleDelta > 0;
50201
50244
  const utilizationDelta = efficiencyDelta;
50202
50245
  const utilizationImproved = utilizationDelta >= 0;
50203
- const utilizationTrendText = `${Math.abs(utilizationDelta).toFixed(1)}% vs last month`;
50246
+ const utilizationTrendText = `${Math.abs(utilizationDelta).toFixed(1)}%`;
50204
50247
  const idleDeltaRaw = trendSummary?.avg_idle_time?.delta_seconds ?? 0;
50205
50248
  const idlePrev = trendSummary?.avg_idle_time?.previous ?? 0;
50206
50249
  const idleDelta = idlePrev ? idleDeltaRaw / idlePrev * 100 : 0;
50207
50250
  const idleImproved = idleDelta <= 0;
50208
- const idleTrendText = `${Math.abs(idleDelta).toFixed(1)}% vs last month`;
50251
+ const idleTrendText = `${Math.abs(idleDelta).toFixed(1)}%`;
50209
50252
  const stoppagesDeltaRaw = trendSummary?.avg_daily_stoppages?.delta_count ?? 0;
50210
50253
  const stoppagesPrev = trendSummary?.avg_daily_stoppages?.previous ?? 0;
50211
50254
  const stoppagesDelta = stoppagesPrev ? stoppagesDeltaRaw / stoppagesPrev * 100 : 0;
50212
50255
  const stoppagesImproved = stoppagesDelta <= 0;
50213
- const stoppagesTrendText = `${Math.abs(stoppagesDelta).toFixed(1)}% vs last month`;
50256
+ const stoppagesTrendText = `${Math.abs(stoppagesDelta).toFixed(1)}%`;
50214
50257
  const calendarData = React143.useMemo(() => {
50215
50258
  const startOfMonth2 = new Date(year, month, 1);
50216
50259
  const endOfMonth2 = new Date(year, month + 1, 0);
@@ -50371,20 +50414,17 @@ var WorkspaceMonthlyHistory = ({
50371
50414
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-2xl font-bold text-gray-900", children: formatIdleTime(metrics2?.avgIdleTime ?? 0) }),
50372
50415
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-1 ${idleImproved ? "bg-emerald-50 text-emerald-600" : "bg-red-50 text-red-600"} px-2 py-0.5 rounded-full text-[10px] font-medium whitespace-nowrap flex-shrink-0`, children: [
50373
50416
  idleImproved ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDown, { className: "w-3 h-3" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "w-3 h-3" }),
50374
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: idleTrendText })
50417
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: `${idleTrendText} vs ${trendComparisonLabel}` })
50375
50418
  ] })
50376
50419
  ] })
50377
50420
  ] }),
50378
50421
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-lg shadow-sm border border-gray-100 p-4 flex flex-col justify-between", children: [
50379
50422
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-semibold text-gray-600 mb-1", children: "Avg Cycle Time" }),
50380
50423
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 flex-nowrap", children: [
50381
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-2xl font-bold text-gray-900", children: [
50382
- assemblyRangeCycleTime,
50383
- "s"
50384
- ] }),
50424
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-2xl font-bold text-gray-900", children: formatCycleSeconds(assemblyRangeCycleTime) }),
50385
50425
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-1 ${cycleWorsened ? "bg-red-50 text-red-600" : "bg-emerald-50 text-emerald-600"} px-2 py-0.5 rounded-full text-[10px] font-medium whitespace-nowrap flex-shrink-0`, children: [
50386
50426
  cycleWorsened ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "w-3 h-3" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDown, { className: "w-3 h-3" }),
50387
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: `${Math.abs(cycleDelta).toFixed(1)}% vs last month` })
50427
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: `${Math.abs(cycleDelta).toFixed(1)}% vs ${trendComparisonLabel}` })
50388
50428
  ] })
50389
50429
  ] })
50390
50430
  ] })
@@ -50395,10 +50435,10 @@ var WorkspaceMonthlyHistory = ({
50395
50435
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-2xl font-bold text-gray-900", children: isUptimeMode ? `${metrics2?.avgUtilization ?? 0}%` : `${metrics2?.avgEfficiency ?? 0}%` }),
50396
50436
  isUptimeMode ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-1 ${utilizationImproved ? "bg-emerald-50 text-emerald-600" : "bg-red-50 text-red-600"} px-2 py-0.5 rounded-full text-[10px] font-medium whitespace-nowrap flex-shrink-0`, children: [
50397
50437
  utilizationImproved ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "w-3 h-3" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDown, { className: "w-3 h-3" }),
50398
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: utilizationTrendText })
50438
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: `${utilizationTrendText} vs ${trendComparisonLabel}` })
50399
50439
  ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-1 ${efficiencyImproved ? "bg-emerald-50 text-emerald-600" : "bg-red-50 text-red-600"} px-2 py-0.5 rounded-full text-[10px] font-medium whitespace-nowrap flex-shrink-0`, children: [
50400
50440
  efficiencyImproved ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "w-3 h-3" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDown, { className: "w-3 h-3" }),
50401
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: `${Math.abs(efficiencyDelta).toFixed(1)}% vs last month` })
50441
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: `${Math.abs(efficiencyDelta).toFixed(1)}% vs ${trendComparisonLabel}` })
50402
50442
  ] })
50403
50443
  ] })
50404
50444
  ] }),
@@ -50408,7 +50448,7 @@ var WorkspaceMonthlyHistory = ({
50408
50448
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-2xl font-bold text-gray-900", children: formatIdleTime(metrics2?.avgIdleTime ?? 0) }),
50409
50449
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-1 ${idleImproved ? "bg-emerald-50 text-emerald-600" : "bg-red-50 text-red-600"} px-2 py-0.5 rounded-full text-[10px] font-medium whitespace-nowrap flex-shrink-0`, children: [
50410
50450
  idleImproved ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDown, { className: "w-3 h-3" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "w-3 h-3" }),
50411
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: idleTrendText })
50451
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: `${idleTrendText} vs ${trendComparisonLabel}` })
50412
50452
  ] })
50413
50453
  ] })
50414
50454
  ] }),
@@ -50421,10 +50461,10 @@ var WorkspaceMonthlyHistory = ({
50421
50461
  ] }),
50422
50462
  isUptimeMode ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-1 ${stoppagesImproved ? "bg-emerald-50 text-emerald-600" : "bg-red-50 text-red-600"} px-2 py-0.5 rounded-full text-[10px] font-medium whitespace-nowrap flex-shrink-0`, children: [
50423
50463
  stoppagesImproved ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDown, { className: "w-3 h-3" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "w-3 h-3" }),
50424
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: stoppagesTrendText })
50464
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: `${stoppagesTrendText} vs ${trendComparisonLabel}` })
50425
50465
  ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-1 ${cycleWorsened ? "bg-red-50 text-red-600" : "bg-emerald-50 text-emerald-600"} px-2 py-0.5 rounded-full text-[10px] font-medium whitespace-nowrap flex-shrink-0`, children: [
50426
50466
  cycleWorsened ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "w-3 h-3" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDown, { className: "w-3 h-3" }),
50427
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: `${Math.abs(cycleDelta).toFixed(1)}% vs last month` })
50467
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: `${Math.abs(cycleDelta).toFixed(1)}% vs ${trendComparisonLabel}` })
50428
50468
  ] })
50429
50469
  ] })
50430
50470
  ] })
@@ -51136,6 +51176,7 @@ var getShiftDisplayName2 = (shiftId, availableShifts) => {
51136
51176
  var WorkspaceMonthlyPdfGenerator = ({
51137
51177
  workspaceId,
51138
51178
  workspaceName,
51179
+ lineName,
51139
51180
  monthlyData,
51140
51181
  analysisData,
51141
51182
  selectedMonth,
@@ -51201,9 +51242,17 @@ var WorkspaceMonthlyPdfGenerator = ({
51201
51242
  year: "numeric",
51202
51243
  timeZone: "Asia/Kolkata"
51203
51244
  });
51245
+ const doc = new jsPDF.jsPDF();
51246
+ const pageHeight = typeof doc.internal.pageSize.height === "number" ? Number(doc.internal.pageSize.height) : 297;
51247
+ const footerY = pageHeight - 17;
51248
+ const maxContentY = footerY - 10;
51249
+ const generatedText = `Generated on ${(/* @__PURE__ */ new Date()).toLocaleString("en-IN", { timeZone: "Asia/Kolkata" })}`;
51250
+ const resolvedLineName = lineName?.trim() || "Line";
51251
+ const dailySectionTitle = isUptimeMode ? "Daily Utilization Summary" : "Daily Performance Summary";
51204
51252
  trackCoreEvent("Workspace Monthly PDF Export Clicked", {
51205
51253
  workspace_id: workspaceId,
51206
51254
  workspace_name: workspaceName,
51255
+ line_name: resolvedLineName,
51207
51256
  month: selectedMonth,
51208
51257
  year: selectedYear,
51209
51258
  shift_id: selectedShiftId,
@@ -51211,26 +51260,34 @@ var WorkspaceMonthlyPdfGenerator = ({
51211
51260
  range_end: normalizedRange.endKey,
51212
51261
  is_full_month: fullRange
51213
51262
  });
51214
- const doc = new jsPDF.jsPDF();
51215
- doc.setFontSize(14);
51216
- doc.setFont("helvetica", "bold");
51217
- doc.setTextColor(50, 50, 50);
51218
- doc.text("OPTIFYE.AI", 20, 15);
51219
- doc.setFontSize(11);
51220
- doc.setFont("helvetica", "normal");
51221
- doc.setTextColor(80, 80, 80);
51222
- const reportText = isUptimeMode ? "MONTHLY UTILIZATION REPORT" : "MONTHLY PERFORMANCE REPORT";
51223
- const reportTextWidth = doc.getStringUnitWidth(reportText) * 11 / doc.internal.scaleFactor;
51224
- doc.text(reportText, doc.internal.pageSize.width - 20 - reportTextWidth, 15);
51225
- doc.setDrawColor(200, 200, 200);
51226
- doc.setLineWidth(0.5);
51227
- doc.line(20, 20, 190, 20);
51263
+ const drawPageChrome = () => {
51264
+ doc.setFontSize(14);
51265
+ doc.setFont("helvetica", "bold");
51266
+ doc.setTextColor(50, 50, 50);
51267
+ doc.text("OPTIFYE.AI", 20, 15);
51268
+ doc.setFontSize(11);
51269
+ doc.setFont("helvetica", "normal");
51270
+ doc.setTextColor(80, 80, 80);
51271
+ const reportText = isUptimeMode ? "MONTHLY UTILIZATION REPORT" : "MONTHLY PERFORMANCE REPORT";
51272
+ const reportTextWidth = doc.getStringUnitWidth(reportText) * 11 / doc.internal.scaleFactor;
51273
+ doc.text(reportText, doc.internal.pageSize.width - 20 - reportTextWidth, 15);
51274
+ doc.setDrawColor(200, 200, 200);
51275
+ doc.setLineWidth(0.5);
51276
+ doc.line(20, 20, 190, 20);
51277
+ };
51278
+ const drawFooter = () => {
51279
+ doc.setFontSize(9);
51280
+ doc.setTextColor(130, 130, 130);
51281
+ doc.text(generatedText, 20, footerY);
51282
+ doc.setTextColor(0, 0, 0);
51283
+ };
51284
+ drawPageChrome();
51228
51285
  doc.setFillColor(250, 250, 250);
51229
51286
  doc.roundedRect(15, 25, 180, 55, 3, 3, "F");
51230
51287
  doc.setFontSize(32);
51231
51288
  doc.setFont("helvetica", "bold");
51232
51289
  doc.setTextColor(0, 0, 0);
51233
- doc.text("Line 1", 20, 40);
51290
+ doc.text(resolvedLineName, 20, 40);
51234
51291
  doc.setFontSize(22);
51235
51292
  doc.setFont("helvetica", "normal");
51236
51293
  doc.setTextColor(40, 40, 40);
@@ -51259,6 +51316,10 @@ var WorkspaceMonthlyPdfGenerator = ({
51259
51316
  return date.getMonth() === selectedMonth && date.getFullYear() === selectedYear;
51260
51317
  });
51261
51318
  const validShifts = validDays.map((day) => getShiftData(day, selectedShiftId)).filter(hasShiftData);
51319
+ const dailyEntries = validDays.map((dayData) => {
51320
+ const shift = getShiftData(dayData, selectedShiftId);
51321
+ return { dayData, shift };
51322
+ }).filter(({ shift }) => hasShiftData(shift)).sort((left, right) => new Date(right.dayData.date).getTime() - new Date(left.dayData.date).getTime());
51262
51323
  const filteredShifts = isUptimeMode ? validShifts : validShifts.filter((shift) => (shift.efficiency ?? 0) >= 5);
51263
51324
  const monthlyMetrics = filteredShifts.length > 0 ? isUptimeMode ? (() => {
51264
51325
  const totalIdleTime = filteredShifts.reduce((sum, shift) => sum + shift.idleTime, 0);
@@ -51385,21 +51446,27 @@ var WorkspaceMonthlyPdfGenerator = ({
51385
51446
  doc.setLineWidth(0.8);
51386
51447
  const separatorY = isAssemblyWorkspaceAndNotUptime ? 150 : 180;
51387
51448
  doc.line(20, separatorY, 190, separatorY);
51388
- doc.setFillColor(245, 245, 245);
51389
51449
  const dailySectionY = isAssemblyWorkspaceAndNotUptime ? 155 : 185;
51390
- doc.roundedRect(15, dailySectionY, 180, 85, 3, 3, "F");
51391
- doc.setFontSize(18);
51392
- doc.setFont("helvetica", "bold");
51393
- doc.setTextColor(40, 40, 40);
51394
- doc.text(isUptimeMode ? "Daily Utilization Summary" : "Daily Performance Summary", 20, dailySectionY + 10);
51395
- doc.setTextColor(0, 0, 0);
51396
- if (validDays.length > 0) {
51450
+ const renderDailyTablePage = (startIndex, sectionY, title) => {
51451
+ const rowHeight = 8;
51452
+ const tableHeaderY = sectionY + 15;
51453
+ const textY = tableHeaderY + 5;
51454
+ const firstRowY = textY + 10;
51455
+ const rowsPerPage = Math.max(1, Math.floor((maxContentY - firstRowY) / rowHeight));
51456
+ const pageEntries = dailyEntries.slice(startIndex, startIndex + rowsPerPage);
51457
+ const endY = firstRowY + pageEntries.length * rowHeight;
51458
+ const sectionHeight = Math.max(40, endY - sectionY + 5);
51459
+ doc.setFillColor(245, 245, 245);
51460
+ doc.roundedRect(15, sectionY, 180, sectionHeight, 3, 3, "F");
51461
+ doc.setFontSize(18);
51462
+ doc.setFont("helvetica", "bold");
51463
+ doc.setTextColor(40, 40, 40);
51464
+ doc.text(title, 20, sectionY + 10);
51465
+ doc.setTextColor(0, 0, 0);
51397
51466
  doc.setFontSize(10);
51398
51467
  doc.setFont("helvetica", "bold");
51399
51468
  doc.setFillColor(240, 240, 240);
51400
- const tableHeaderY = dailySectionY + 15;
51401
51469
  doc.roundedRect(20, tableHeaderY, 170, 7, 1, 1, "F");
51402
- const textY = tableHeaderY + 5;
51403
51470
  doc.text("Date", 25, textY);
51404
51471
  doc.text(isUptimeMode ? "Productive" : isAssemblyWorkspace ? "Actual Cycle Time" : "Actual", 60, textY);
51405
51472
  doc.text(isUptimeMode ? "Idle" : isAssemblyWorkspace ? "Standard Cycle Time" : "Standard", 95, textY);
@@ -51409,12 +51476,8 @@ var WorkspaceMonthlyPdfGenerator = ({
51409
51476
  doc.setDrawColor(220, 220, 220);
51410
51477
  doc.line(20, textY + 3, 190, textY + 3);
51411
51478
  doc.setFont("helvetica", "normal");
51412
- let yPos = textY + 10;
51413
- const recentDays = validDays.slice(-10).reverse();
51414
- recentDays.forEach((dayData, index) => {
51415
- if (yPos > 260) return;
51416
- const shift = getShiftData(dayData, selectedShiftId);
51417
- if (!hasShiftData(shift)) return;
51479
+ let yPos = firstRowY;
51480
+ pageEntries.forEach(({ dayData, shift }, index) => {
51418
51481
  if (index % 2 === 0) {
51419
51482
  doc.setFillColor(252, 252, 252);
51420
51483
  doc.roundedRect(20, yPos - 4, 170, 7, 1, 1, "F");
@@ -51447,11 +51510,35 @@ var WorkspaceMonthlyPdfGenerator = ({
51447
51510
  doc.text(`${shift.efficiency.toFixed(1)}%`, 135, yPos);
51448
51511
  drawStatusMark(doc, 171, yPos - 0.3, shift.efficiency >= effectiveLegend.green_min);
51449
51512
  }
51450
- yPos += 8;
51513
+ yPos += rowHeight;
51451
51514
  });
51452
51515
  doc.setLineWidth(0.2);
51453
51516
  doc.setDrawColor(220, 220, 220);
51454
51517
  doc.roundedRect(20, tableHeaderY, 170, yPos - tableHeaderY - 3, 1, 1, "S");
51518
+ return startIndex + pageEntries.length;
51519
+ };
51520
+ if (dailyEntries.length > 0) {
51521
+ let renderedEntries = renderDailyTablePage(0, dailySectionY, dailySectionTitle);
51522
+ while (renderedEntries < dailyEntries.length) {
51523
+ drawFooter();
51524
+ doc.addPage();
51525
+ drawPageChrome();
51526
+ doc.setFontSize(12);
51527
+ doc.setFont("helvetica", "bold");
51528
+ doc.setTextColor(40, 40, 40);
51529
+ doc.text(resolvedLineName, 20, 32);
51530
+ doc.setFont("helvetica", "normal");
51531
+ doc.text(getWorkspaceDisplayName(workspaceName), 20, 40);
51532
+ doc.setFontSize(10);
51533
+ doc.setTextColor(90, 90, 90);
51534
+ doc.text(`${monthName} \u2022 ${shiftType} \u2022 ${reportStartStr} - ${reportEndStr}`, 20, 48);
51535
+ doc.setTextColor(0, 0, 0);
51536
+ renderedEntries = renderDailyTablePage(
51537
+ renderedEntries,
51538
+ 56,
51539
+ `${dailySectionTitle} (cont.)`
51540
+ );
51541
+ }
51455
51542
  } else {
51456
51543
  doc.setFontSize(12);
51457
51544
  doc.setFont("helvetica", "normal");
@@ -51459,10 +51546,7 @@ var WorkspaceMonthlyPdfGenerator = ({
51459
51546
  doc.text("No daily data available for this month", 25, dailySectionY + 30);
51460
51547
  doc.setTextColor(0, 0, 0);
51461
51548
  }
51462
- doc.setFontSize(9);
51463
- doc.setTextColor(130, 130, 130);
51464
- const generatedText = `Generated on ${(/* @__PURE__ */ new Date()).toLocaleString("en-IN", { timeZone: "Asia/Kolkata" })}`;
51465
- doc.text(generatedText, 20, 280);
51549
+ drawFooter();
51466
51550
  const fileName = `${getWorkspaceDisplayName(workspaceName)}_${monthName.replace(" ", "_")}_${shiftType.replace(" ", "_")}.pdf`;
51467
51551
  doc.save(fileName);
51468
51552
  } catch (error) {
@@ -63611,7 +63695,9 @@ var KPIDetailView = ({
63611
63695
  month: currentMonth,
63612
63696
  year: currentYear,
63613
63697
  shiftId: selectedShiftId,
63614
- companyId: resolvedCompanyId
63698
+ companyId: resolvedCompanyId,
63699
+ startDate: isFullRange ? void 0 : rangeStart,
63700
+ endDate: isFullRange ? void 0 : rangeEnd
63615
63701
  });
63616
63702
  const configuredTimezone = timezone || dashboardConfig.dateTimeConfig?.defaultTimezone || "UTC";
63617
63703
  React143.useMemo(() => getCurrentTimeInZone(configuredTimezone), [configuredTimezone]);
@@ -71094,7 +71180,9 @@ var WorkspaceDetailView = ({
71094
71180
  month: selectedMonth,
71095
71181
  year: selectedYear,
71096
71182
  shiftId: selectedShift,
71097
- companyId: dashboardConfig?.entityConfig?.companyId
71183
+ companyId: dashboardConfig?.entityConfig?.companyId,
71184
+ startDate: isFullRange ? void 0 : rangeStart,
71185
+ endDate: isFullRange ? void 0 : rangeEnd
71098
71186
  });
71099
71187
  const {
71100
71188
  isFastSlowClipFiltersEnabled,
@@ -71361,6 +71449,7 @@ var WorkspaceDetailView = ({
71361
71449
  line_name: "",
71362
71450
  line_assembly_enabled: cachedOverviewMetrics.assembly_enabled === true,
71363
71451
  workspace_name: cachedOverviewMetrics.workspace_name,
71452
+ workspace_display_name: cachedOverviewMetrics.displayName ?? null,
71364
71453
  workspace_id: cachedOverviewMetrics.workspace_uuid || "",
71365
71454
  company_id: cachedOverviewMetrics.company_id,
71366
71455
  company_name: "",
@@ -71675,7 +71764,17 @@ var WorkspaceDetailView = ({
71675
71764
  const analysisMonthlyData = React143.useMemo(() => {
71676
71765
  return filterDataByDateKeyRange(monthlyData, range);
71677
71766
  }, [monthlyData, range]);
71678
- const formattedWorkspaceName = displayName || formatWorkspaceName3(workspace?.workspace_name || "", resolvedLineId);
71767
+ const formattedWorkspaceName = displayName || workspace?.workspace_display_name || formatWorkspaceName3(workspace?.workspace_name || "", resolvedLineId);
71768
+ const resolvedLineName = React143.useMemo(() => {
71769
+ const workspaceLineName = workspace?.line_name?.trim();
71770
+ if (workspaceLineName) {
71771
+ return workspaceLineName;
71772
+ }
71773
+ if (resolvedLineId && dashboardConfig?.entityConfig) {
71774
+ return getLineDisplayName(dashboardConfig.entityConfig, resolvedLineId);
71775
+ }
71776
+ return "Line";
71777
+ }, [dashboardConfig?.entityConfig, resolvedLineId, workspace?.line_name]);
71679
71778
  const workspaceCycleTimeEligibility = workspace ? {
71680
71779
  line_assembly_enabled: workspace.line_assembly_enabled,
71681
71780
  action_family: workspace.action_family,
@@ -72220,6 +72319,7 @@ var WorkspaceDetailView = ({
72220
72319
  {
72221
72320
  workspaceId,
72222
72321
  workspaceName: formattedWorkspaceName,
72322
+ lineName: resolvedLineName,
72223
72323
  monthlyData,
72224
72324
  analysisData: analysisMonthlyData,
72225
72325
  selectedMonth,
@@ -78909,6 +79009,12 @@ var buildDeltaBadge = (delta, options) => {
78909
79009
  text: `${options.formatter(delta)} vs ${options.comparisonLabel}`
78910
79010
  };
78911
79011
  };
79012
+ var parseSpecificShiftId = (shiftMode) => {
79013
+ const rawMode = String(shiftMode || "").trim().toLowerCase();
79014
+ if (!rawMode.startsWith("shift:")) return null;
79015
+ const parsed = Number(rawMode.split(":", 2)[1]);
79016
+ return Number.isFinite(parsed) ? parsed : null;
79017
+ };
78912
79018
  var normalizeShiftLabel = (shiftName, shiftMode) => {
78913
79019
  if (shiftMode === "all") {
78914
79020
  return "All Shifts";
@@ -78920,6 +79026,10 @@ var normalizeShiftLabel = (shiftName, shiftMode) => {
78920
79026
  if (normalizedName === "night") return "Night Shift";
78921
79027
  return /shift/i.test(trimmedName) ? trimmedName : `${trimmedName} Shift`;
78922
79028
  }
79029
+ const specificShiftId = parseSpecificShiftId(shiftMode);
79030
+ if (specificShiftId !== null) {
79031
+ return `Shift ${specificShiftId}`;
79032
+ }
78923
79033
  if (shiftMode === "night") return "Night Shift";
78924
79034
  return "Day Shift";
78925
79035
  };
@@ -78992,6 +79102,7 @@ var OperationsOverviewHeader = React143__namespace.default.memo(({
78992
79102
  dateRange,
78993
79103
  displayDateRange,
78994
79104
  trendMode,
79105
+ shiftFilterOptions,
78995
79106
  isLiveScope,
78996
79107
  liveShiftName,
78997
79108
  lineOptions,
@@ -79245,7 +79356,7 @@ var OperationsOverviewHeader = React143__namespace.default.memo(({
79245
79356
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
79246
79357
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
79247
79358
  /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-xs font-medium text-gray-500 uppercase tracking-wide ml-1", children: "Shift" }),
79248
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative", children: /* @__PURE__ */ jsxRuntime.jsxs(
79359
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative", children: /* @__PURE__ */ jsxRuntime.jsx(
79249
79360
  "select",
79250
79361
  {
79251
79362
  value: trendMode,
@@ -79257,11 +79368,7 @@ var OperationsOverviewHeader = React143__namespace.default.memo(({
79257
79368
  backgroundRepeat: "no-repeat",
79258
79369
  backgroundSize: "1.2em 1.2em"
79259
79370
  },
79260
- children: [
79261
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "all", children: "All Shifts" }),
79262
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "day", children: "Day Shift" }),
79263
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "night", children: "Night Shift" })
79264
- ]
79371
+ children: shiftFilterOptions.map((option) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: option.value, children: option.label }, option.value))
79265
79372
  }
79266
79373
  ) })
79267
79374
  ] }),
@@ -80331,6 +80438,24 @@ var normalizeShiftId = (value) => {
80331
80438
  }
80332
80439
  return null;
80333
80440
  };
80441
+ var buildSpecificShiftMode = (value) => {
80442
+ const normalizedShiftId = normalizeShiftId(value);
80443
+ return normalizedShiftId === null ? null : `shift:${normalizedShiftId}`;
80444
+ };
80445
+ var parseSpecificShiftMode = (value) => {
80446
+ const rawValue = String(value || "").trim().toLowerCase();
80447
+ if (!rawValue.startsWith("shift:")) return null;
80448
+ const parsed = Number(rawValue.split(":", 2)[1]);
80449
+ return Number.isFinite(parsed) ? parsed : null;
80450
+ };
80451
+ var formatShiftOptionLabel = (shiftName, shiftId) => {
80452
+ const trimmedName = shiftName?.trim();
80453
+ if (trimmedName) {
80454
+ return /shift/i.test(trimmedName) ? trimmedName : `${trimmedName} Shift`;
80455
+ }
80456
+ const normalizedShiftId = normalizeShiftId(shiftId);
80457
+ return normalizedShiftId === null ? "Unknown Shift" : `Shift ${normalizedShiftId}`;
80458
+ };
80334
80459
  var classifyShiftBucket = ({
80335
80460
  shiftName,
80336
80461
  shiftId,
@@ -80417,6 +80542,26 @@ var normalizeShiftWindowMinutes = (startTime, endTime) => {
80417
80542
  }
80418
80543
  return { startMinutes, endMinutes };
80419
80544
  };
80545
+ var getOperationalSortStartMinutes = (startTime, endTime) => {
80546
+ const normalizedWindow = normalizeShiftWindowMinutes(startTime, endTime);
80547
+ if (!normalizedWindow) return null;
80548
+ return normalizedWindow.startMinutes < 6 * 60 ? normalizedWindow.startMinutes + 24 * 60 : normalizedWindow.startMinutes;
80549
+ };
80550
+ var doesShiftMatchTrendMode = ({
80551
+ trendMode,
80552
+ shiftName,
80553
+ shiftId,
80554
+ startTime,
80555
+ endTime
80556
+ }) => {
80557
+ if (trendMode === "all") return true;
80558
+ const specificShiftId = parseSpecificShiftMode(trendMode);
80559
+ if (specificShiftId !== null) {
80560
+ return normalizeShiftId(shiftId) === specificShiftId;
80561
+ }
80562
+ const bucket = classifyShiftBucket({ shiftName, shiftId, startTime, endTime });
80563
+ return bucket === trendMode;
80564
+ };
80420
80565
  var PlantHeadView = () => {
80421
80566
  const supabase = useSupabase();
80422
80567
  const entityConfig = useEntityConfig();
@@ -80547,6 +80692,45 @@ var PlantHeadView = () => {
80547
80692
  shiftConfigMap,
80548
80693
  isLoading: isShiftConfigLoading
80549
80694
  } = useMultiLineShiftConfigs(scopedLineIds, staticShiftConfig);
80695
+ const shiftFilterOptions = React143__namespace.default.useMemo(() => {
80696
+ const optionsById = /* @__PURE__ */ new Map();
80697
+ scopedLineIds.forEach((lineId) => {
80698
+ const shiftConfig = shiftConfigMap.get(lineId) || staticShiftConfig;
80699
+ getShiftWindowsForConfig(shiftConfig).forEach((shift) => {
80700
+ const shiftId = normalizeShiftId(shift.shiftId);
80701
+ if (shiftId === null) return;
80702
+ const sortKey = getOperationalSortStartMinutes(shift.startTime, shift.endTime) ?? 24 * 60 + shiftId;
80703
+ const existing = optionsById.get(shiftId);
80704
+ const label = formatShiftOptionLabel(shift.shiftName, shiftId);
80705
+ if (!existing) {
80706
+ optionsById.set(shiftId, {
80707
+ value: buildSpecificShiftMode(shiftId),
80708
+ label,
80709
+ shiftId,
80710
+ shiftName: shift.shiftName || null,
80711
+ startTime: shift.startTime || null,
80712
+ endTime: shift.endTime || null,
80713
+ sortKey
80714
+ });
80715
+ return;
80716
+ }
80717
+ if ((!existing.shiftName || existing.shiftName === `Shift ${shiftId}`) && shift.shiftName) {
80718
+ existing.shiftName = shift.shiftName;
80719
+ existing.label = label;
80720
+ }
80721
+ if (sortKey < existing.sortKey) {
80722
+ existing.sortKey = sortKey;
80723
+ existing.startTime = shift.startTime || null;
80724
+ existing.endTime = shift.endTime || null;
80725
+ }
80726
+ });
80727
+ });
80728
+ const dynamicOptions = Array.from(optionsById.values()).sort((a, b) => a.sortKey - b.sortKey || (a.shiftId || 0) - (b.shiftId || 0)).map(({ sortKey, ...option }) => option);
80729
+ return [
80730
+ { value: "all", label: "All Shifts" },
80731
+ ...dynamicOptions
80732
+ ];
80733
+ }, [appTimezone, scopedLineIds, shiftConfigMap, staticShiftConfig]);
80550
80734
  React143__namespace.default.useEffect(() => {
80551
80735
  if (scopedLineIds.length === 0 || isShiftConfigLoading) {
80552
80736
  return;
@@ -80609,18 +80793,20 @@ var PlantHeadView = () => {
80609
80793
  if (!activeShift) {
80610
80794
  return [];
80611
80795
  }
80612
- const trendBucket = classifyShiftBucket({
80796
+ const bucketTrendMode = classifyShiftBucket({
80613
80797
  shiftName: activeShift.shiftName,
80614
80798
  shiftId: activeShift.shiftId,
80615
80799
  startTime: activeShift.startTime,
80616
80800
  endTime: activeShift.endTime
80617
80801
  });
80618
- if (!trendBucket || trendBucket === "all") {
80802
+ const exactTrendMode = buildSpecificShiftMode(activeShift.shiftId);
80803
+ if (!bucketTrendMode && !exactTrendMode) {
80619
80804
  return [];
80620
80805
  }
80621
80806
  return [{
80622
80807
  lineId,
80623
- trendMode: trendBucket,
80808
+ exactTrendMode,
80809
+ bucketTrendMode,
80624
80810
  shiftId: activeShift.shiftId,
80625
80811
  shiftName: activeShift.shiftName || null,
80626
80812
  startTime: activeShift.startTime || null,
@@ -80629,14 +80815,12 @@ var PlantHeadView = () => {
80629
80815
  }];
80630
80816
  });
80631
80817
  }, [appTimezone, scopedLineIds, shiftConfigMap, shiftResolutionNow, staticShiftConfig]);
80632
- const hasActiveDayShiftLine = React143__namespace.default.useMemo(
80633
- () => activeLineShiftStates.some((shift) => shift.trendMode === "day" && shift.date === resolvedOperationalToday),
80634
- [activeLineShiftStates, resolvedOperationalToday]
80635
- );
80636
- const hasActiveNightShiftLine = React143__namespace.default.useMemo(
80637
- () => activeLineShiftStates.some((shift) => shift.trendMode === "night" && shift.date === resolvedOperationalToday),
80638
- [activeLineShiftStates, resolvedOperationalToday]
80639
- );
80818
+ const uniformActiveTrendMode = React143__namespace.default.useMemo(() => {
80819
+ const activeModes = Array.from(new Set(
80820
+ activeLineShiftStates.filter((shift) => shift.date === resolvedOperationalToday).map((shift) => shift.exactTrendMode).filter((mode) => !!mode)
80821
+ ));
80822
+ return activeModes.length === 1 ? activeModes[0] : null;
80823
+ }, [activeLineShiftStates, resolvedOperationalToday]);
80640
80824
  const resolvedTrendMode = isInitialScopeReady ? trendMode : "all";
80641
80825
  const hourlyWindowStartTime = React143__namespace.default.useMemo(() => {
80642
80826
  if (scopedLineIds.length === 0) {
@@ -80647,13 +80831,19 @@ var PlantHeadView = () => {
80647
80831
  scopedLineIds.forEach((lineId) => {
80648
80832
  const shiftConfig = shiftConfigMap.get(lineId) || staticShiftConfig;
80649
80833
  getShiftWindowsForConfig(shiftConfig).forEach((shift) => {
80650
- const bucket = classifyShiftBucket({
80834
+ classifyShiftBucket({
80651
80835
  shiftName: shift.shiftName,
80652
80836
  shiftId: shift.shiftId,
80653
80837
  startTime: shift.startTime,
80654
80838
  endTime: shift.endTime
80655
80839
  });
80656
- if (resolvedTrendMode !== "all" && bucket !== resolvedTrendMode) {
80840
+ if (!doesShiftMatchTrendMode({
80841
+ trendMode: resolvedTrendMode,
80842
+ shiftName: shift.shiftName,
80843
+ shiftId: shift.shiftId,
80844
+ startTime: shift.startTime,
80845
+ endTime: shift.endTime
80846
+ })) {
80657
80847
  return;
80658
80848
  }
80659
80849
  const normalizedWindow = normalizeShiftWindowMinutes(shift.startTime, shift.endTime);
@@ -80727,11 +80917,11 @@ var PlantHeadView = () => {
80727
80917
  endKey: nextStartKey
80728
80918
  };
80729
80919
  });
80730
- setTrendMode("all");
80920
+ setTrendMode(uniformActiveTrendMode || "all");
80731
80921
  setUsesThisWeekComparison(false);
80732
80922
  hasAutoInitializedScopeRef.current = true;
80733
80923
  setIsInitialScopeReady(true);
80734
- }, [fallbackOperationalDate, isShiftScopeResolved, resolvedOperationalToday, scopedLineIds.length]);
80924
+ }, [fallbackOperationalDate, isShiftScopeResolved, resolvedOperationalToday, scopedLineIds.length, uniformActiveTrendMode]);
80735
80925
  const handleDateRangeChange = React143__namespace.default.useCallback((range, meta) => {
80736
80926
  hasUserAdjustedScopeRef.current = true;
80737
80927
  setIsInitialScopeReady(true);
@@ -80818,6 +81008,33 @@ var PlantHeadView = () => {
80818
81008
  () => resolvedTrendMode,
80819
81009
  [resolvedTrendMode]
80820
81010
  );
81011
+ const hasActiveSelectedShiftLine = React143__namespace.default.useMemo(
81012
+ () => activeLineShiftStates.some((shift) => {
81013
+ if (shift.date !== resolvedOperationalToday) return false;
81014
+ if (effectiveTrendMode === "all") return true;
81015
+ const specificShiftId = parseSpecificShiftMode(effectiveTrendMode);
81016
+ if (specificShiftId !== null) {
81017
+ return shift.exactTrendMode === effectiveTrendMode;
81018
+ }
81019
+ return shift.bucketTrendMode === effectiveTrendMode;
81020
+ }),
81021
+ [activeLineShiftStates, effectiveTrendMode, resolvedOperationalToday]
81022
+ );
81023
+ const activeLiveShiftName = React143__namespace.default.useMemo(
81024
+ () => {
81025
+ if (effectiveTrendMode === "all") return null;
81026
+ const matchingShift = activeLineShiftStates.find((shift) => {
81027
+ if (shift.date !== resolvedOperationalToday) return false;
81028
+ const specificShiftId = parseSpecificShiftMode(effectiveTrendMode);
81029
+ if (specificShiftId !== null) {
81030
+ return shift.exactTrendMode === effectiveTrendMode;
81031
+ }
81032
+ return shift.bucketTrendMode === effectiveTrendMode;
81033
+ });
81034
+ return matchingShift?.shiftName || null;
81035
+ },
81036
+ [activeLineShiftStates, effectiveTrendMode, resolvedOperationalToday]
81037
+ );
80821
81038
  const hourlyLabelStartTime = React143__namespace.default.useMemo(() => {
80822
81039
  if (scopedLineIds.length === 0) {
80823
81040
  return null;
@@ -80829,12 +81046,11 @@ var PlantHeadView = () => {
80829
81046
  [effectiveDateRange.endKey, effectiveDateRange.startKey]
80830
81047
  );
80831
81048
  const isLiveScope = React143__namespace.default.useMemo(
80832
- () => isSingleDayScope && effectiveDateRange.startKey === resolvedOperationalToday && (effectiveTrendMode === "all" || effectiveTrendMode === "day" && hasActiveDayShiftLine || effectiveTrendMode === "night" && hasActiveNightShiftLine),
81049
+ () => isSingleDayScope && effectiveDateRange.startKey === resolvedOperationalToday && hasActiveSelectedShiftLine,
80833
81050
  [
80834
81051
  effectiveDateRange.startKey,
80835
81052
  effectiveTrendMode,
80836
- hasActiveDayShiftLine,
80837
- hasActiveNightShiftLine,
81053
+ hasActiveSelectedShiftLine,
80838
81054
  isSingleDayScope,
80839
81055
  resolvedOperationalToday
80840
81056
  ]
@@ -80871,8 +81087,9 @@ var PlantHeadView = () => {
80871
81087
  dateRange,
80872
81088
  displayDateRange: headerDateRange,
80873
81089
  trendMode,
81090
+ shiftFilterOptions,
80874
81091
  isLiveScope,
80875
- liveShiftName: isLiveScope && trendMode !== "all" ? trendMode : null,
81092
+ liveShiftName: isLiveScope && trendMode !== "all" ? activeLiveShiftName : null,
80876
81093
  lineOptions,
80877
81094
  supervisorOptions,
80878
81095
  selectedSupervisorId,
@@ -81742,6 +81959,7 @@ exports.getManufacturingInsights = getManufacturingInsights;
81742
81959
  exports.getMetricsTablePrefix = getMetricsTablePrefix;
81743
81960
  exports.getMonthKeyBounds = getMonthKeyBounds;
81744
81961
  exports.getMonthWeekRanges = getMonthWeekRanges;
81962
+ exports.getMonthlyTrendComparisonLabel = getMonthlyTrendComparisonLabel;
81745
81963
  exports.getNextUpdateInterval = getNextUpdateInterval;
81746
81964
  exports.getOperationalDate = getOperationalDate;
81747
81965
  exports.getRoleAssignmentKind = getRoleAssignmentKind;