@optifye/dashboard-core 6.11.13 → 6.11.15

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
@@ -11771,6 +11771,9 @@ var toWorkspaceDetailedMetrics = ({
11771
11771
  const idealOutput = coerceNumber(data.ideal_output ?? data.ideal_output_until_now, 0);
11772
11772
  const outputDifference = totalActions - idealOutput;
11773
11773
  const hourlyCycleTimes = Array.isArray(data.hourly_cycle_times) ? data.hourly_cycle_times.map((value) => coerceNumber(value, 0)) : [];
11774
+ const cycleCompletionClipCount = data.cycle_completion_clip_count === null || data.cycle_completion_clip_count === void 0 ? null : coerceNumber(data.cycle_completion_clip_count, 0);
11775
+ const cycleTimeDataStatus = data.cycle_time_data_status === "missing_clips" ? "missing_clips" : data.cycle_time_data_status === "available" ? "available" : null;
11776
+ const cycleTimeTimezone = typeof data.cycle_time_timezone === "string" ? data.cycle_time_timezone : null;
11774
11777
  const totalWorkspacesValue = coerceNumber(
11775
11778
  data.total_workspaces ?? lineMetricsById?.[data.line_id || ""]?.total_workspaces ?? workspaceConfig.totalWorkspaces,
11776
11779
  0
@@ -11818,6 +11821,9 @@ var toWorkspaceDetailedMetrics = ({
11818
11821
  total_actions: totalActions,
11819
11822
  hourly_action_counts: hourlyActionCounts,
11820
11823
  hourly_cycle_times: hourlyCycleTimes,
11824
+ cycle_completion_clip_count: cycleCompletionClipCount,
11825
+ cycle_time_data_status: cycleTimeDataStatus,
11826
+ cycle_time_timezone: cycleTimeTimezone,
11821
11827
  workspace_rank: coerceNumber(data.workspace_rank, 0),
11822
11828
  total_workspaces: totalWorkspacesValue,
11823
11829
  ideal_output_until_now: idealOutput,
@@ -32698,16 +32704,162 @@ var CycleTimeOverTimeChart = ({
32698
32704
  return `${minutes} minutes ${seconds} seconds ago`;
32699
32705
  }
32700
32706
  };
32707
+ const getNumericValue = React141__namespace.default.useCallback((value) => typeof value === "number" && Number.isFinite(value) ? value : null, []);
32708
+ const renderChartTooltip = React141__namespace.default.useCallback((tooltipProps) => {
32709
+ const { active, payload } = tooltipProps;
32710
+ if (!active || !Array.isArray(payload) || payload.length === 0) {
32711
+ return null;
32712
+ }
32713
+ const visibleEntries = payload.filter((entry) => getNumericValue(entry.value) !== null);
32714
+ if (!visibleEntries.length) {
32715
+ return null;
32716
+ }
32717
+ return /* @__PURE__ */ jsxRuntime.jsxs(
32718
+ "div",
32719
+ {
32720
+ style: {
32721
+ backgroundColor: "white",
32722
+ border: "none",
32723
+ borderRadius: "8px",
32724
+ boxShadow: "0 4px 12px rgba(0,0,0,0.1)",
32725
+ padding: "8px 12px",
32726
+ fontSize: "13px"
32727
+ },
32728
+ children: [
32729
+ /* @__PURE__ */ jsxRuntime.jsx(
32730
+ "div",
32731
+ {
32732
+ style: {
32733
+ color: "#374151",
32734
+ fontWeight: 600,
32735
+ marginBottom: "4px"
32736
+ },
32737
+ children: payload[0]?.payload?.tooltip || ""
32738
+ }
32739
+ ),
32740
+ visibleEntries.map((entry) => {
32741
+ const numericValue = getNumericValue(entry.value);
32742
+ if (numericValue === null) {
32743
+ return null;
32744
+ }
32745
+ return /* @__PURE__ */ jsxRuntime.jsx(
32746
+ "div",
32747
+ {
32748
+ style: {
32749
+ color: "#4B5563",
32750
+ padding: "2px 0"
32751
+ },
32752
+ children: entry.name === "idleMinutes" ? `Idle Time: ${numericValue.toFixed(0)} minutes` : `Cycle Time: ${numericValue.toFixed(1)} seconds`
32753
+ },
32754
+ `${entry.name}-${numericValue}`
32755
+ );
32756
+ })
32757
+ ]
32758
+ }
32759
+ );
32760
+ }, [getNumericValue]);
32761
+ const renderCycleDot = React141__namespace.default.useCallback((props) => {
32762
+ const { cx: cx2, cy, payload } = props;
32763
+ const cycleTime = getNumericValue(payload?.cycleTime);
32764
+ if (cycleTime === null) {
32765
+ return /* @__PURE__ */ jsxRuntime.jsx("g", {});
32766
+ }
32767
+ return /* @__PURE__ */ jsxRuntime.jsx(
32768
+ "circle",
32769
+ {
32770
+ cx: cx2,
32771
+ cy,
32772
+ r: 4,
32773
+ fill: cycleTime <= idealCycleTime ? "#00AB45" : "#E34329",
32774
+ stroke: "#fff",
32775
+ strokeWidth: 1,
32776
+ style: {
32777
+ filter: "brightness(1)",
32778
+ transition: "filter 0.3s ease, transform 0.3s ease",
32779
+ cursor: "pointer"
32780
+ },
32781
+ onMouseEnter: (e) => {
32782
+ const target = e.target;
32783
+ target.style.filter = "brightness(1.2)";
32784
+ target.style.transform = "scale(1.2)";
32785
+ },
32786
+ onMouseLeave: (e) => {
32787
+ const target = e.target;
32788
+ target.style.filter = "brightness(1)";
32789
+ target.style.transform = "scale(1)";
32790
+ }
32791
+ }
32792
+ );
32793
+ }, [getNumericValue, idealCycleTime]);
32794
+ const renderCycleActiveDot = React141__namespace.default.useCallback((props) => {
32795
+ const { cx: cx2, cy, payload } = props;
32796
+ const cycleTime = getNumericValue(payload?.cycleTime);
32797
+ if (cycleTime === null) {
32798
+ return /* @__PURE__ */ jsxRuntime.jsx("g", {});
32799
+ }
32800
+ return /* @__PURE__ */ jsxRuntime.jsx(
32801
+ "circle",
32802
+ {
32803
+ cx: cx2,
32804
+ cy,
32805
+ r: 6,
32806
+ fill: cycleTime <= idealCycleTime ? "#00AB45" : "#E34329",
32807
+ stroke: "#fff",
32808
+ strokeWidth: 2,
32809
+ style: {
32810
+ filter: "drop-shadow(0 0 2px rgba(0,0,0,0.2))"
32811
+ }
32812
+ }
32813
+ );
32814
+ }, [getNumericValue, idealCycleTime]);
32815
+ const renderIdleDot = React141__namespace.default.useCallback((props) => {
32816
+ const { cx: cx2, cy, payload } = props;
32817
+ const idleMinutes = getNumericValue(payload?.idleMinutes);
32818
+ if (idleMinutes === null) {
32819
+ return /* @__PURE__ */ jsxRuntime.jsx("g", {});
32820
+ }
32821
+ return /* @__PURE__ */ jsxRuntime.jsx(
32822
+ "circle",
32823
+ {
32824
+ cx: cx2,
32825
+ cy,
32826
+ r: 4,
32827
+ fill: "#f59e0b",
32828
+ stroke: "#fff",
32829
+ strokeWidth: 1
32830
+ }
32831
+ );
32832
+ }, [getNumericValue]);
32833
+ const renderIdleActiveDot = React141__namespace.default.useCallback((props) => {
32834
+ const { cx: cx2, cy, payload } = props;
32835
+ const idleMinutes = getNumericValue(payload?.idleMinutes);
32836
+ if (idleMinutes === null) {
32837
+ return /* @__PURE__ */ jsxRuntime.jsx("g", {});
32838
+ }
32839
+ return /* @__PURE__ */ jsxRuntime.jsx(
32840
+ "circle",
32841
+ {
32842
+ cx: cx2,
32843
+ cy,
32844
+ r: 6,
32845
+ fill: "#f59e0b",
32846
+ stroke: "#fff",
32847
+ strokeWidth: 2
32848
+ }
32849
+ );
32850
+ }, [getNumericValue]);
32701
32851
  const chartData = React141__namespace.default.useMemo(() => Array.from({ length: DURATION }, (_, i) => {
32852
+ const cycleTime = getNumericValue(finalData[i]);
32853
+ const idleMinutes = showIdleTime ? getNumericValue(idleTimeData[i]) : null;
32702
32854
  return {
32703
32855
  timeIndex: i,
32704
32856
  label: formatTimeLabel(i),
32705
32857
  tooltip: formatTooltipTime(i),
32706
- cycleTime: finalData[i] || 0,
32707
- idleMinutes: showIdleTime && idleTimeData && idleTimeData[i] || 0,
32708
- color: (finalData[i] || 0) <= idealCycleTime ? "#00AB45" : "#E34329"
32858
+ cycleTime,
32859
+ idleMinutes,
32860
+ color: cycleTime !== null && cycleTime <= idealCycleTime ? "#00AB45" : "#E34329"
32709
32861
  };
32710
- }), [DURATION, finalData, showIdleTime, idleTimeData, idealCycleTime]);
32862
+ }), [DURATION, finalData, showIdleTime, idleTimeData, idealCycleTime, getNumericValue]);
32711
32863
  const renderLegend = () => {
32712
32864
  if (!showIdleTime) return null;
32713
32865
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-start text-[10px] font-bold text-gray-500 mb-6 tracking-[0.05em] gap-5", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
@@ -32794,36 +32946,7 @@ var CycleTimeOverTimeChart = ({
32794
32946
  recharts.Tooltip,
32795
32947
  {
32796
32948
  cursor: { stroke: "#E5E7EB", strokeWidth: 1 },
32797
- contentStyle: {
32798
- backgroundColor: "white",
32799
- border: "none",
32800
- borderRadius: "8px",
32801
- boxShadow: "0 4px 12px rgba(0,0,0,0.1)",
32802
- padding: "8px 12px",
32803
- fontSize: "13px"
32804
- },
32805
- labelStyle: {
32806
- color: "#374151",
32807
- fontWeight: 600,
32808
- marginBottom: "4px"
32809
- },
32810
- itemStyle: {
32811
- color: "#4B5563",
32812
- padding: "2px 0"
32813
- },
32814
- labelFormatter: (label, payload) => {
32815
- if (payload && payload[0]) {
32816
- return payload[0].payload.tooltip;
32817
- }
32818
- return label;
32819
- },
32820
- formatter: (value, name) => {
32821
- const numValue = typeof value === "number" ? value : Number(value);
32822
- if (name === "idleMinutes") {
32823
- return [`${numValue.toFixed(0)} minutes`, "Idle Time"];
32824
- }
32825
- return [`${numValue.toFixed(1)} seconds`, "Cycle Time"];
32826
- },
32949
+ content: renderChartTooltip,
32827
32950
  animationDuration: 200
32828
32951
  }
32829
32952
  ),
@@ -32852,52 +32975,9 @@ var CycleTimeOverTimeChart = ({
32852
32975
  dataKey: "cycleTime",
32853
32976
  stroke: "#3B82F6",
32854
32977
  strokeWidth: 2,
32855
- dot: (props) => {
32856
- const { cx: cx2, cy, payload } = props;
32857
- return /* @__PURE__ */ jsxRuntime.jsx(
32858
- "circle",
32859
- {
32860
- cx: cx2,
32861
- cy,
32862
- r: 4,
32863
- fill: payload.cycleTime <= idealCycleTime ? "#00AB45" : "#E34329",
32864
- stroke: "#fff",
32865
- strokeWidth: 1,
32866
- style: {
32867
- filter: "brightness(1)",
32868
- transition: "filter 0.3s ease, transform 0.3s ease",
32869
- cursor: "pointer"
32870
- },
32871
- onMouseEnter: (e) => {
32872
- const target = e.target;
32873
- target.style.filter = "brightness(1.2)";
32874
- target.style.transform = "scale(1.2)";
32875
- },
32876
- onMouseLeave: (e) => {
32877
- const target = e.target;
32878
- target.style.filter = "brightness(1)";
32879
- target.style.transform = "scale(1)";
32880
- }
32881
- }
32882
- );
32883
- },
32884
- activeDot: (props) => {
32885
- const { cx: cx2, cy, payload } = props;
32886
- return /* @__PURE__ */ jsxRuntime.jsx(
32887
- "circle",
32888
- {
32889
- cx: cx2,
32890
- cy,
32891
- r: 6,
32892
- fill: payload.cycleTime <= idealCycleTime ? "#00AB45" : "#E34329",
32893
- stroke: "#fff",
32894
- strokeWidth: 2,
32895
- style: {
32896
- filter: "drop-shadow(0 0 2px rgba(0,0,0,0.2))"
32897
- }
32898
- }
32899
- );
32900
- },
32978
+ connectNulls: false,
32979
+ dot: renderCycleDot,
32980
+ activeDot: renderCycleActiveDot,
32901
32981
  isAnimationActive: shouldAnimate,
32902
32982
  animationBegin: 0,
32903
32983
  animationDuration: 1200,
@@ -32915,8 +32995,9 @@ var CycleTimeOverTimeChart = ({
32915
32995
  stroke: "#f59e0b",
32916
32996
  strokeWidth: 2,
32917
32997
  strokeDasharray: "4 4",
32918
- dot: { r: 4, fill: "#f59e0b", stroke: "#fff", strokeWidth: 1 },
32919
- activeDot: { r: 6, fill: "#f59e0b", stroke: "#fff", strokeWidth: 2 },
32998
+ connectNulls: false,
32999
+ dot: renderIdleDot,
33000
+ activeDot: renderIdleActiveDot,
32920
33001
  isAnimationActive: shouldAnimate,
32921
33002
  animationBegin: 0,
32922
33003
  animationDuration: 1200,
@@ -34644,8 +34725,20 @@ var CardFooter2 = (props) => {
34644
34725
  return /* @__PURE__ */ jsxRuntime.jsx(RegisteredCardFooter, { ...props });
34645
34726
  };
34646
34727
 
34728
+ // src/lib/utils/workspaceDetailCycleTime.ts
34729
+ var resolveWorkspaceDetailActionFamily = (workspace) => {
34730
+ if (workspace?.action_family === "assembly" || workspace?.action_family === "output" || workspace?.action_family === "other") {
34731
+ return workspace.action_family;
34732
+ }
34733
+ if (workspace?.action_type === "assembly" || workspace?.action_type === "output") {
34734
+ return workspace.action_type;
34735
+ }
34736
+ return null;
34737
+ };
34738
+ var shouldUseAssemblyCycleTimeLayout = (workspace) => workspace?.line_assembly_enabled === true && resolveWorkspaceDetailActionFamily(workspace) === "assembly";
34739
+
34647
34740
  // src/components/dashboard/workspace/workspaceDetailCardRules.ts
34648
- var shouldHideWorkspaceEfficiencyCard = (workspace) => workspace?.line_assembly_enabled === true && workspace?.action_type === "assembly";
34741
+ var shouldHideWorkspaceEfficiencyCard = (workspace) => shouldUseAssemblyCycleTimeLayout(workspace);
34649
34742
  var WorkspaceMetricCardsImpl = ({
34650
34743
  workspace,
34651
34744
  className,
@@ -35354,6 +35447,37 @@ var getShiftElapsedMinutes = ({
35354
35447
  const elapsed = dateFns.differenceInMinutes(now4, shiftStartDate);
35355
35448
  return Math.min(Math.max(elapsed, 0), shiftMinutes);
35356
35449
  };
35450
+ var maskFutureHourlySeries = ({
35451
+ data,
35452
+ shiftStart,
35453
+ shiftEnd,
35454
+ shiftDate,
35455
+ timezone,
35456
+ now: now4 = /* @__PURE__ */ new Date()
35457
+ }) => {
35458
+ if (!Array.isArray(data)) {
35459
+ return [];
35460
+ }
35461
+ const normalizedData = data.map((value) => typeof value === "number" && Number.isFinite(value) ? value : null);
35462
+ if (!normalizedData.length) {
35463
+ return normalizedData;
35464
+ }
35465
+ const shiftMinutes = getShiftDurationMinutes(shiftStart, shiftEnd);
35466
+ const elapsedMinutes = getShiftElapsedMinutes({
35467
+ shiftStart,
35468
+ shiftEnd,
35469
+ shiftDate,
35470
+ timezone,
35471
+ now: now4
35472
+ });
35473
+ if (shiftMinutes === null || elapsedMinutes === null || elapsedMinutes >= shiftMinutes) {
35474
+ return normalizedData;
35475
+ }
35476
+ return normalizedData.map((value, index) => {
35477
+ const slotStartMinutes = index * 60;
35478
+ return slotStartMinutes > elapsedMinutes ? null : value;
35479
+ });
35480
+ };
35357
35481
  var buildUptimeSeries = ({
35358
35482
  idleTimeHourly,
35359
35483
  shiftStart,
@@ -48892,7 +49016,7 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
48892
49016
  setIsGenerating(true);
48893
49017
  try {
48894
49018
  const isUptimeMode = workspace.monitoring_mode === "uptime";
48895
- const isAssemblyCycleMode = !isUptimeMode && workspace.line_assembly_enabled === true && workspace.action_type === "assembly";
49019
+ const isAssemblyCycleMode = !isUptimeMode && shouldUseAssemblyCycleTimeLayout(workspace);
48896
49020
  const shiftMinutes = getShiftDurationMinutes(workspace.shift_start, workspace.shift_end);
48897
49021
  const shiftSeconds = shiftMinutes ? shiftMinutes * 60 : 0;
48898
49022
  const idleSeconds = Math.max(workspace.idle_time || 0, 0);
@@ -60353,6 +60477,7 @@ var MonthlyRangeFilter = ({
60353
60477
  {
60354
60478
  type: "button",
60355
60479
  onClick: () => setIsOpen((prev) => !prev),
60480
+ "aria-label": showLabel ? void 0 : singleDateOnly ? "Select date" : "Select date range",
60356
60481
  className: clsx(
60357
60482
  "flex items-center transition-all duration-200 focus:outline-none",
60358
60483
  !showLabel && "p-2 rounded-full hover:bg-gray-100",
@@ -62965,7 +63090,7 @@ var KPIsOverviewView = ({
62965
63090
  () => getShiftEndDate(currentShiftDetails, configuredTimezone),
62966
63091
  [currentShiftDetails, configuredTimezone]
62967
63092
  );
62968
- React141__namespace.default.useMemo(() => {
63093
+ const leaderboardShiftOptions = React141__namespace.default.useMemo(() => {
62969
63094
  if (shiftConfig?.shifts && shiftConfig.shifts.length > 0) {
62970
63095
  return shiftConfig.shifts.map((shift) => ({
62971
63096
  id: shift.shiftId,
@@ -63764,6 +63889,17 @@ var KPIsOverviewView = ({
63764
63889
  singleDateOnly: true
63765
63890
  }
63766
63891
  ),
63892
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative", children: /* @__PURE__ */ jsxRuntime.jsx(
63893
+ "select",
63894
+ {
63895
+ "aria-label": "Leaderboard shift",
63896
+ value: effectiveLeaderboardShiftId,
63897
+ onChange: (e) => setSelectedLeaderboardShiftId(Number(e.target.value)),
63898
+ className: "appearance-none pl-3 pr-8 py-1.5 text-sm font-medium bg-white border border-gray-200 hover:border-gray-300 rounded-lg text-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500 transition-all cursor-pointer shadow-sm min-w-[170px]",
63899
+ style: { backgroundImage: `url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e")`, backgroundPosition: `right 0.5rem center`, backgroundRepeat: `no-repeat`, backgroundSize: `1.2em 1.2em` },
63900
+ children: leaderboardShiftOptions.map((shiftOption) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: shiftOption.id, children: shiftOption.label }, shiftOption.id))
63901
+ }
63902
+ ) }),
63767
63903
  isHistoricalLeaderboardDaily && /* @__PURE__ */ jsxRuntime.jsx(
63768
63904
  "button",
63769
63905
  {
@@ -68051,8 +68187,74 @@ var TargetsView = ({
68051
68187
  var TargetsViewWithDisplayNames = withAllWorkspaceDisplayNames(TargetsView);
68052
68188
  var TargetsView_default = TargetsViewWithDisplayNames;
68053
68189
  var AuthenticatedTargetsView = withAuth(React141__namespace.default.memo(TargetsViewWithDisplayNames));
68190
+ function useTimezone(options = {}) {
68191
+ const dashboardConfig = useDashboardConfig();
68192
+ const workspaceConfig = useWorkspaceConfig();
68193
+ const defaultTimezone = dashboardConfig?.dateTimeConfig?.defaultTimezone || "Asia/Kolkata";
68194
+ const [timezone, setTimezone] = React141.useState(defaultTimezone);
68195
+ const [isLoading, setIsLoading] = React141.useState(true);
68196
+ const [error, setError] = React141.useState(null);
68197
+ const fetchTimezone = React141.useCallback(async () => {
68198
+ setIsLoading(true);
68199
+ setError(null);
68200
+ try {
68201
+ let fetchedTimezone = defaultTimezone;
68202
+ if (options.lineId) {
68203
+ fetchedTimezone = await timezoneService.getTimezoneForLine(options.lineId, defaultTimezone);
68204
+ } else if (options.workspaceId || workspaceConfig && "id" in workspaceConfig) {
68205
+ const wsId = options.workspaceId || (workspaceConfig && "id" in workspaceConfig ? workspaceConfig.id : void 0);
68206
+ if (wsId) {
68207
+ fetchedTimezone = await timezoneService.getTimezoneForWorkspace(wsId, defaultTimezone);
68208
+ }
68209
+ } else if (options.companyId || dashboardConfig && "company" in dashboardConfig) {
68210
+ const compId = options.companyId || (dashboardConfig && "company" in dashboardConfig ? dashboardConfig.company?.id : void 0);
68211
+ if (compId) {
68212
+ fetchedTimezone = await timezoneService.getTimezoneForCompany(compId, defaultTimezone);
68213
+ }
68214
+ }
68215
+ setTimezone(fetchedTimezone);
68216
+ } catch (err) {
68217
+ console.error("Error fetching timezone:", err);
68218
+ setError(err instanceof Error ? err : new Error("Failed to fetch timezone"));
68219
+ setTimezone(defaultTimezone);
68220
+ } finally {
68221
+ setIsLoading(false);
68222
+ }
68223
+ }, [
68224
+ options.lineId,
68225
+ options.workspaceId,
68226
+ options.companyId,
68227
+ workspaceConfig,
68228
+ dashboardConfig,
68229
+ defaultTimezone
68230
+ ]);
68231
+ React141.useEffect(() => {
68232
+ fetchTimezone();
68233
+ }, [fetchTimezone]);
68234
+ return {
68235
+ timezone,
68236
+ isLoading,
68237
+ error,
68238
+ refetch: fetchTimezone
68239
+ };
68240
+ }
68054
68241
 
68055
68242
  // src/views/workspace-detail-view.utils.ts
68243
+ var getWorkspaceCycleTimePresentation = ({
68244
+ workspace,
68245
+ showCycleTimeChart
68246
+ }) => {
68247
+ if (workspace?.monitoring_mode === "uptime") {
68248
+ return "output";
68249
+ }
68250
+ if (showCycleTimeChart === false) {
68251
+ return "output";
68252
+ }
68253
+ if (!shouldUseAssemblyCycleTimeLayout(workspace)) {
68254
+ return "output";
68255
+ }
68256
+ return workspace?.cycle_time_data_status === "missing_clips" ? "cycle_unavailable" : "cycle_chart";
68257
+ };
68056
68258
  var formatDateInTimezone = (date = /* @__PURE__ */ new Date(), timezone, options) => {
68057
68259
  const defaultOptions = {
68058
68260
  day: "numeric",
@@ -68436,6 +68638,7 @@ var WorkspaceDetailView = ({
68436
68638
  date: cachedOverviewMetrics.date,
68437
68639
  shift_id: cachedOverviewMetrics.shift_id,
68438
68640
  action_name: "",
68641
+ action_family: cachedOverviewMetrics.action_family ?? null,
68439
68642
  action_type: cachedOverviewMetrics.action_type ?? null,
68440
68643
  monitoring_mode: cachedOverviewMetrics.monitoring_mode ?? "output",
68441
68644
  shift_start: shiftDefinition?.startTime || "",
@@ -68459,6 +68662,11 @@ var WorkspaceDetailView = ({
68459
68662
  };
68460
68663
  }, [cachedOverviewMetrics, shiftConfig?.shifts]);
68461
68664
  const workspace = (isHistoricView ? historicMetrics : liveMetrics) || cachedDetailedMetrics || overviewFallback;
68665
+ const { timezone: cycleTimeTimezone } = useTimezone({
68666
+ lineId: effectiveLineId || workspace?.line_id || void 0,
68667
+ workspaceId: workspaceId || void 0
68668
+ });
68669
+ const effectiveCycleTimeTimezone = cycleTimeTimezone || timezone;
68462
68670
  const detailedWorkspaceMetrics = (isHistoricView ? historicMetrics : liveMetrics) || cachedDetailedMetrics;
68463
68671
  const cycleTimeChartData = React141.useMemo(
68464
68672
  () => Array.isArray(workspace?.hourly_cycle_times) ? workspace.hourly_cycle_times.map((value) => {
@@ -68467,12 +68675,31 @@ var WorkspaceDetailView = ({
68467
68675
  }) : [],
68468
68676
  [workspace?.hourly_cycle_times]
68469
68677
  );
68678
+ const maskedCycleTimeChartData = React141.useMemo(
68679
+ () => maskFutureHourlySeries({
68680
+ data: cycleTimeChartData,
68681
+ shiftStart: workspace?.shift_start,
68682
+ shiftEnd: workspace?.shift_end,
68683
+ shiftDate: workspace?.date || date || calculatedOperationalDate || null,
68684
+ timezone: effectiveCycleTimeTimezone
68685
+ }),
68686
+ [
68687
+ cycleTimeChartData,
68688
+ workspace?.shift_start,
68689
+ workspace?.shift_end,
68690
+ workspace?.date,
68691
+ date,
68692
+ calculatedOperationalDate,
68693
+ effectiveCycleTimeTimezone
68694
+ ]
68695
+ );
68470
68696
  const cycleTimeDatasetKey = React141.useMemo(
68471
68697
  () => [
68472
68698
  workspace?.workspace_id || workspaceId || "workspace",
68473
68699
  date || workspace?.date || "live",
68474
68700
  parsedShiftId ?? workspace?.shift_id ?? "current",
68475
- "hourly"
68701
+ "hourly",
68702
+ "backend"
68476
68703
  ].join(":"),
68477
68704
  [workspace?.workspace_id, workspaceId, date, workspace?.date, parsedShiftId, workspace?.shift_id]
68478
68705
  );
@@ -68717,14 +68944,28 @@ var WorkspaceDetailView = ({
68717
68944
  return filterDataByDateKeyRange(monthlyData, range);
68718
68945
  }, [monthlyData, range]);
68719
68946
  const formattedWorkspaceName = displayName || formatWorkspaceName3(workspace?.workspace_name || "", effectiveLineId);
68720
- const workspaceActionType = workspace && "action_type" in workspace ? workspace.action_type : void 0;
68721
- const workspaceAssemblyEnabled = workspace && "line_assembly_enabled" in workspace ? workspace.line_assembly_enabled === true : false;
68722
- const isAssemblyWorkspace = workspaceActionType === "assembly" || workspaceAssemblyEnabled;
68723
- const shouldShowCycleTimeChart = !isUptimeMode && (showCycleTimeChart ?? isAssemblyWorkspace);
68947
+ const workspaceCycleTimeEligibility = workspace ? {
68948
+ line_assembly_enabled: workspace.line_assembly_enabled,
68949
+ action_family: workspace.action_family,
68950
+ action_type: workspace.action_type
68951
+ } : null;
68952
+ const isAssemblyWorkspace = shouldUseAssemblyCycleTimeLayout(workspaceCycleTimeEligibility);
68953
+ const cycleTimePresentation = getWorkspaceCycleTimePresentation({
68954
+ workspace: workspace ? {
68955
+ monitoring_mode: workspace.monitoring_mode,
68956
+ line_assembly_enabled: workspace.line_assembly_enabled,
68957
+ action_family: workspace.action_family,
68958
+ action_type: workspace.action_type,
68959
+ cycle_time_data_status: workspace.cycle_time_data_status
68960
+ } : null,
68961
+ showCycleTimeChart
68962
+ });
68963
+ const shouldShowCycleTimeChart = cycleTimePresentation !== "output";
68964
+ const shouldShowCycleTimeUnavailableState = cycleTimePresentation === "cycle_unavailable";
68724
68965
  const showIdleBreakdownChart = !shouldShowCycleTimeChart && idleTimeVlmEnabled;
68725
68966
  const idleClipDate = date || workspace?.date || calculatedOperationalDate || getOperationalDate(timezone);
68726
68967
  const idleClipShiftId = parsedShiftId ?? workspace?.shift_id;
68727
- const hourlyIdleMinutes = React141.useMemo(() => {
68968
+ const rawHourlyIdleMinutes = React141.useMemo(() => {
68728
68969
  if (!shouldShowCycleTimeChart || !workspace?.idle_time_hourly || !workspace?.shift_start) return [];
68729
68970
  const parseTimeToMinutes3 = (time2) => {
68730
68971
  const [h, m] = time2.split(":").map(Number);
@@ -68758,6 +68999,30 @@ var WorkspaceDetailView = ({
68758
68999
  }
68759
69000
  return result;
68760
69001
  }, [shouldShowCycleTimeChart, workspace?.idle_time_hourly, workspace?.shift_start, workspace?.shift_end]);
69002
+ const hourlyIdleMinutes = React141.useMemo(
69003
+ () => maskFutureHourlySeries({
69004
+ data: rawHourlyIdleMinutes,
69005
+ shiftStart: workspace?.shift_start,
69006
+ shiftEnd: workspace?.shift_end,
69007
+ shiftDate: idleClipDate,
69008
+ timezone: effectiveCycleTimeTimezone
69009
+ }),
69010
+ [
69011
+ rawHourlyIdleMinutes,
69012
+ workspace?.shift_start,
69013
+ workspace?.shift_end,
69014
+ idleClipDate,
69015
+ effectiveCycleTimeTimezone
69016
+ ]
69017
+ );
69018
+ const cycleTimeUnavailableView = React141.useMemo(() => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full h-full rounded-lg border border-amber-200 bg-amber-50/70 px-6 py-5 text-center flex flex-col items-center justify-center", children: [
69019
+ /* @__PURE__ */ jsxRuntime.jsx("h4", { className: "text-base font-semibold text-amber-900", children: "Cycle data unavailable" }),
69020
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 max-w-md text-sm text-amber-800", children: "This workstation has cycle-time metrics for the selected shift, but no matching `cycle_completion` clips were found for the chart." }),
69021
+ typeof workspace?.cycle_completion_clip_count === "number" && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mt-3 text-xs font-medium uppercase tracking-[0.08em] text-amber-700", children: [
69022
+ "matched cycle clips: ",
69023
+ workspace.cycle_completion_clip_count
69024
+ ] })
69025
+ ] }), [workspace?.cycle_completion_clip_count]);
68761
69026
  const shiftDurationMinutes = React141.useMemo(
68762
69027
  () => getShiftDurationMinutes(workspace?.shift_start, workspace?.shift_end),
68763
69028
  [workspace?.shift_start, workspace?.shift_end]
@@ -69254,7 +69519,7 @@ var WorkspaceDetailView = ({
69254
69519
  children: [
69255
69520
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center mb-4", children: [
69256
69521
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-bold text-gray-700", children: isUptimeMode ? "Machine Utilization" : shouldShowCycleTimeChart ? "Cycle time trend" : "Hourly Output" }),
69257
- !isUptimeMode && /* @__PURE__ */ jsxRuntime.jsx(
69522
+ !isUptimeMode && !shouldShowCycleTimeUnavailableState && /* @__PURE__ */ jsxRuntime.jsx(
69258
69523
  "button",
69259
69524
  {
69260
69525
  onClick: () => setShowChartIdleTime(!showChartIdleTime),
@@ -69283,10 +69548,10 @@ var WorkspaceDetailView = ({
69283
69548
  timezone,
69284
69549
  elapsedMinutes: elapsedShiftMinutes
69285
69550
  }
69286
- ) : shouldShowCycleTimeChart ? /* @__PURE__ */ jsxRuntime.jsx(
69551
+ ) : shouldShowCycleTimeUnavailableState ? cycleTimeUnavailableView : shouldShowCycleTimeChart ? /* @__PURE__ */ jsxRuntime.jsx(
69287
69552
  CycleTimeOverTimeChart,
69288
69553
  {
69289
- data: cycleTimeChartData,
69554
+ data: maskedCycleTimeChartData,
69290
69555
  idealCycleTime: workspace.ideal_cycle_time || 0,
69291
69556
  shiftStart: workspace.shift_start || "",
69292
69557
  shiftEnd: workspace.shift_end || "",
@@ -69387,7 +69652,7 @@ var WorkspaceDetailView = ({
69387
69652
  children: [
69388
69653
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-3 mb-4 flex-none", children: [
69389
69654
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-bold text-gray-700", children: isUptimeMode ? "Machine Utilization" : shouldShowCycleTimeChart ? "Cycle time trend" : "Hourly Output" }),
69390
- !isUptimeMode && /* @__PURE__ */ jsxRuntime.jsx(
69655
+ !isUptimeMode && !shouldShowCycleTimeUnavailableState && /* @__PURE__ */ jsxRuntime.jsx(
69391
69656
  "button",
69392
69657
  {
69393
69658
  onClick: () => setShowChartIdleTime(!showChartIdleTime),
@@ -69412,10 +69677,10 @@ var WorkspaceDetailView = ({
69412
69677
  timezone,
69413
69678
  elapsedMinutes: elapsedShiftMinutes
69414
69679
  }
69415
- ) : shouldShowCycleTimeChart ? /* @__PURE__ */ jsxRuntime.jsx(
69680
+ ) : shouldShowCycleTimeUnavailableState ? cycleTimeUnavailableView : shouldShowCycleTimeChart ? /* @__PURE__ */ jsxRuntime.jsx(
69416
69681
  CycleTimeOverTimeChart,
69417
69682
  {
69418
- data: cycleTimeChartData,
69683
+ data: maskedCycleTimeChartData,
69419
69684
  idealCycleTime: workspace.ideal_cycle_time || 0,
69420
69685
  shiftStart: workspace.shift_start || "",
69421
69686
  shiftEnd: workspace.shift_end || "",