@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.mjs CHANGED
@@ -11742,6 +11742,9 @@ var toWorkspaceDetailedMetrics = ({
11742
11742
  const idealOutput = coerceNumber(data.ideal_output ?? data.ideal_output_until_now, 0);
11743
11743
  const outputDifference = totalActions - idealOutput;
11744
11744
  const hourlyCycleTimes = Array.isArray(data.hourly_cycle_times) ? data.hourly_cycle_times.map((value) => coerceNumber(value, 0)) : [];
11745
+ const cycleCompletionClipCount = data.cycle_completion_clip_count === null || data.cycle_completion_clip_count === void 0 ? null : coerceNumber(data.cycle_completion_clip_count, 0);
11746
+ const cycleTimeDataStatus = data.cycle_time_data_status === "missing_clips" ? "missing_clips" : data.cycle_time_data_status === "available" ? "available" : null;
11747
+ const cycleTimeTimezone = typeof data.cycle_time_timezone === "string" ? data.cycle_time_timezone : null;
11745
11748
  const totalWorkspacesValue = coerceNumber(
11746
11749
  data.total_workspaces ?? lineMetricsById?.[data.line_id || ""]?.total_workspaces ?? workspaceConfig.totalWorkspaces,
11747
11750
  0
@@ -11789,6 +11792,9 @@ var toWorkspaceDetailedMetrics = ({
11789
11792
  total_actions: totalActions,
11790
11793
  hourly_action_counts: hourlyActionCounts,
11791
11794
  hourly_cycle_times: hourlyCycleTimes,
11795
+ cycle_completion_clip_count: cycleCompletionClipCount,
11796
+ cycle_time_data_status: cycleTimeDataStatus,
11797
+ cycle_time_timezone: cycleTimeTimezone,
11792
11798
  workspace_rank: coerceNumber(data.workspace_rank, 0),
11793
11799
  total_workspaces: totalWorkspacesValue,
11794
11800
  ideal_output_until_now: idealOutput,
@@ -32669,16 +32675,162 @@ var CycleTimeOverTimeChart = ({
32669
32675
  return `${minutes} minutes ${seconds} seconds ago`;
32670
32676
  }
32671
32677
  };
32678
+ const getNumericValue = React141__default.useCallback((value) => typeof value === "number" && Number.isFinite(value) ? value : null, []);
32679
+ const renderChartTooltip = React141__default.useCallback((tooltipProps) => {
32680
+ const { active, payload } = tooltipProps;
32681
+ if (!active || !Array.isArray(payload) || payload.length === 0) {
32682
+ return null;
32683
+ }
32684
+ const visibleEntries = payload.filter((entry) => getNumericValue(entry.value) !== null);
32685
+ if (!visibleEntries.length) {
32686
+ return null;
32687
+ }
32688
+ return /* @__PURE__ */ jsxs(
32689
+ "div",
32690
+ {
32691
+ style: {
32692
+ backgroundColor: "white",
32693
+ border: "none",
32694
+ borderRadius: "8px",
32695
+ boxShadow: "0 4px 12px rgba(0,0,0,0.1)",
32696
+ padding: "8px 12px",
32697
+ fontSize: "13px"
32698
+ },
32699
+ children: [
32700
+ /* @__PURE__ */ jsx(
32701
+ "div",
32702
+ {
32703
+ style: {
32704
+ color: "#374151",
32705
+ fontWeight: 600,
32706
+ marginBottom: "4px"
32707
+ },
32708
+ children: payload[0]?.payload?.tooltip || ""
32709
+ }
32710
+ ),
32711
+ visibleEntries.map((entry) => {
32712
+ const numericValue = getNumericValue(entry.value);
32713
+ if (numericValue === null) {
32714
+ return null;
32715
+ }
32716
+ return /* @__PURE__ */ jsx(
32717
+ "div",
32718
+ {
32719
+ style: {
32720
+ color: "#4B5563",
32721
+ padding: "2px 0"
32722
+ },
32723
+ children: entry.name === "idleMinutes" ? `Idle Time: ${numericValue.toFixed(0)} minutes` : `Cycle Time: ${numericValue.toFixed(1)} seconds`
32724
+ },
32725
+ `${entry.name}-${numericValue}`
32726
+ );
32727
+ })
32728
+ ]
32729
+ }
32730
+ );
32731
+ }, [getNumericValue]);
32732
+ const renderCycleDot = React141__default.useCallback((props) => {
32733
+ const { cx: cx2, cy, payload } = props;
32734
+ const cycleTime = getNumericValue(payload?.cycleTime);
32735
+ if (cycleTime === null) {
32736
+ return /* @__PURE__ */ jsx("g", {});
32737
+ }
32738
+ return /* @__PURE__ */ jsx(
32739
+ "circle",
32740
+ {
32741
+ cx: cx2,
32742
+ cy,
32743
+ r: 4,
32744
+ fill: cycleTime <= idealCycleTime ? "#00AB45" : "#E34329",
32745
+ stroke: "#fff",
32746
+ strokeWidth: 1,
32747
+ style: {
32748
+ filter: "brightness(1)",
32749
+ transition: "filter 0.3s ease, transform 0.3s ease",
32750
+ cursor: "pointer"
32751
+ },
32752
+ onMouseEnter: (e) => {
32753
+ const target = e.target;
32754
+ target.style.filter = "brightness(1.2)";
32755
+ target.style.transform = "scale(1.2)";
32756
+ },
32757
+ onMouseLeave: (e) => {
32758
+ const target = e.target;
32759
+ target.style.filter = "brightness(1)";
32760
+ target.style.transform = "scale(1)";
32761
+ }
32762
+ }
32763
+ );
32764
+ }, [getNumericValue, idealCycleTime]);
32765
+ const renderCycleActiveDot = React141__default.useCallback((props) => {
32766
+ const { cx: cx2, cy, payload } = props;
32767
+ const cycleTime = getNumericValue(payload?.cycleTime);
32768
+ if (cycleTime === null) {
32769
+ return /* @__PURE__ */ jsx("g", {});
32770
+ }
32771
+ return /* @__PURE__ */ jsx(
32772
+ "circle",
32773
+ {
32774
+ cx: cx2,
32775
+ cy,
32776
+ r: 6,
32777
+ fill: cycleTime <= idealCycleTime ? "#00AB45" : "#E34329",
32778
+ stroke: "#fff",
32779
+ strokeWidth: 2,
32780
+ style: {
32781
+ filter: "drop-shadow(0 0 2px rgba(0,0,0,0.2))"
32782
+ }
32783
+ }
32784
+ );
32785
+ }, [getNumericValue, idealCycleTime]);
32786
+ const renderIdleDot = React141__default.useCallback((props) => {
32787
+ const { cx: cx2, cy, payload } = props;
32788
+ const idleMinutes = getNumericValue(payload?.idleMinutes);
32789
+ if (idleMinutes === null) {
32790
+ return /* @__PURE__ */ jsx("g", {});
32791
+ }
32792
+ return /* @__PURE__ */ jsx(
32793
+ "circle",
32794
+ {
32795
+ cx: cx2,
32796
+ cy,
32797
+ r: 4,
32798
+ fill: "#f59e0b",
32799
+ stroke: "#fff",
32800
+ strokeWidth: 1
32801
+ }
32802
+ );
32803
+ }, [getNumericValue]);
32804
+ const renderIdleActiveDot = React141__default.useCallback((props) => {
32805
+ const { cx: cx2, cy, payload } = props;
32806
+ const idleMinutes = getNumericValue(payload?.idleMinutes);
32807
+ if (idleMinutes === null) {
32808
+ return /* @__PURE__ */ jsx("g", {});
32809
+ }
32810
+ return /* @__PURE__ */ jsx(
32811
+ "circle",
32812
+ {
32813
+ cx: cx2,
32814
+ cy,
32815
+ r: 6,
32816
+ fill: "#f59e0b",
32817
+ stroke: "#fff",
32818
+ strokeWidth: 2
32819
+ }
32820
+ );
32821
+ }, [getNumericValue]);
32672
32822
  const chartData = React141__default.useMemo(() => Array.from({ length: DURATION }, (_, i) => {
32823
+ const cycleTime = getNumericValue(finalData[i]);
32824
+ const idleMinutes = showIdleTime ? getNumericValue(idleTimeData[i]) : null;
32673
32825
  return {
32674
32826
  timeIndex: i,
32675
32827
  label: formatTimeLabel(i),
32676
32828
  tooltip: formatTooltipTime(i),
32677
- cycleTime: finalData[i] || 0,
32678
- idleMinutes: showIdleTime && idleTimeData && idleTimeData[i] || 0,
32679
- color: (finalData[i] || 0) <= idealCycleTime ? "#00AB45" : "#E34329"
32829
+ cycleTime,
32830
+ idleMinutes,
32831
+ color: cycleTime !== null && cycleTime <= idealCycleTime ? "#00AB45" : "#E34329"
32680
32832
  };
32681
- }), [DURATION, finalData, showIdleTime, idleTimeData, idealCycleTime]);
32833
+ }), [DURATION, finalData, showIdleTime, idleTimeData, idealCycleTime, getNumericValue]);
32682
32834
  const renderLegend = () => {
32683
32835
  if (!showIdleTime) return null;
32684
32836
  return /* @__PURE__ */ jsx("div", { className: "flex items-center justify-start text-[10px] font-bold text-gray-500 mb-6 tracking-[0.05em] gap-5", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
@@ -32765,36 +32917,7 @@ var CycleTimeOverTimeChart = ({
32765
32917
  Tooltip,
32766
32918
  {
32767
32919
  cursor: { stroke: "#E5E7EB", strokeWidth: 1 },
32768
- contentStyle: {
32769
- backgroundColor: "white",
32770
- border: "none",
32771
- borderRadius: "8px",
32772
- boxShadow: "0 4px 12px rgba(0,0,0,0.1)",
32773
- padding: "8px 12px",
32774
- fontSize: "13px"
32775
- },
32776
- labelStyle: {
32777
- color: "#374151",
32778
- fontWeight: 600,
32779
- marginBottom: "4px"
32780
- },
32781
- itemStyle: {
32782
- color: "#4B5563",
32783
- padding: "2px 0"
32784
- },
32785
- labelFormatter: (label, payload) => {
32786
- if (payload && payload[0]) {
32787
- return payload[0].payload.tooltip;
32788
- }
32789
- return label;
32790
- },
32791
- formatter: (value, name) => {
32792
- const numValue = typeof value === "number" ? value : Number(value);
32793
- if (name === "idleMinutes") {
32794
- return [`${numValue.toFixed(0)} minutes`, "Idle Time"];
32795
- }
32796
- return [`${numValue.toFixed(1)} seconds`, "Cycle Time"];
32797
- },
32920
+ content: renderChartTooltip,
32798
32921
  animationDuration: 200
32799
32922
  }
32800
32923
  ),
@@ -32823,52 +32946,9 @@ var CycleTimeOverTimeChart = ({
32823
32946
  dataKey: "cycleTime",
32824
32947
  stroke: "#3B82F6",
32825
32948
  strokeWidth: 2,
32826
- dot: (props) => {
32827
- const { cx: cx2, cy, payload } = props;
32828
- return /* @__PURE__ */ jsx(
32829
- "circle",
32830
- {
32831
- cx: cx2,
32832
- cy,
32833
- r: 4,
32834
- fill: payload.cycleTime <= idealCycleTime ? "#00AB45" : "#E34329",
32835
- stroke: "#fff",
32836
- strokeWidth: 1,
32837
- style: {
32838
- filter: "brightness(1)",
32839
- transition: "filter 0.3s ease, transform 0.3s ease",
32840
- cursor: "pointer"
32841
- },
32842
- onMouseEnter: (e) => {
32843
- const target = e.target;
32844
- target.style.filter = "brightness(1.2)";
32845
- target.style.transform = "scale(1.2)";
32846
- },
32847
- onMouseLeave: (e) => {
32848
- const target = e.target;
32849
- target.style.filter = "brightness(1)";
32850
- target.style.transform = "scale(1)";
32851
- }
32852
- }
32853
- );
32854
- },
32855
- activeDot: (props) => {
32856
- const { cx: cx2, cy, payload } = props;
32857
- return /* @__PURE__ */ jsx(
32858
- "circle",
32859
- {
32860
- cx: cx2,
32861
- cy,
32862
- r: 6,
32863
- fill: payload.cycleTime <= idealCycleTime ? "#00AB45" : "#E34329",
32864
- stroke: "#fff",
32865
- strokeWidth: 2,
32866
- style: {
32867
- filter: "drop-shadow(0 0 2px rgba(0,0,0,0.2))"
32868
- }
32869
- }
32870
- );
32871
- },
32949
+ connectNulls: false,
32950
+ dot: renderCycleDot,
32951
+ activeDot: renderCycleActiveDot,
32872
32952
  isAnimationActive: shouldAnimate,
32873
32953
  animationBegin: 0,
32874
32954
  animationDuration: 1200,
@@ -32886,8 +32966,9 @@ var CycleTimeOverTimeChart = ({
32886
32966
  stroke: "#f59e0b",
32887
32967
  strokeWidth: 2,
32888
32968
  strokeDasharray: "4 4",
32889
- dot: { r: 4, fill: "#f59e0b", stroke: "#fff", strokeWidth: 1 },
32890
- activeDot: { r: 6, fill: "#f59e0b", stroke: "#fff", strokeWidth: 2 },
32969
+ connectNulls: false,
32970
+ dot: renderIdleDot,
32971
+ activeDot: renderIdleActiveDot,
32891
32972
  isAnimationActive: shouldAnimate,
32892
32973
  animationBegin: 0,
32893
32974
  animationDuration: 1200,
@@ -34615,8 +34696,20 @@ var CardFooter2 = (props) => {
34615
34696
  return /* @__PURE__ */ jsx(RegisteredCardFooter, { ...props });
34616
34697
  };
34617
34698
 
34699
+ // src/lib/utils/workspaceDetailCycleTime.ts
34700
+ var resolveWorkspaceDetailActionFamily = (workspace) => {
34701
+ if (workspace?.action_family === "assembly" || workspace?.action_family === "output" || workspace?.action_family === "other") {
34702
+ return workspace.action_family;
34703
+ }
34704
+ if (workspace?.action_type === "assembly" || workspace?.action_type === "output") {
34705
+ return workspace.action_type;
34706
+ }
34707
+ return null;
34708
+ };
34709
+ var shouldUseAssemblyCycleTimeLayout = (workspace) => workspace?.line_assembly_enabled === true && resolveWorkspaceDetailActionFamily(workspace) === "assembly";
34710
+
34618
34711
  // src/components/dashboard/workspace/workspaceDetailCardRules.ts
34619
- var shouldHideWorkspaceEfficiencyCard = (workspace) => workspace?.line_assembly_enabled === true && workspace?.action_type === "assembly";
34712
+ var shouldHideWorkspaceEfficiencyCard = (workspace) => shouldUseAssemblyCycleTimeLayout(workspace);
34620
34713
  var WorkspaceMetricCardsImpl = ({
34621
34714
  workspace,
34622
34715
  className,
@@ -35325,6 +35418,37 @@ var getShiftElapsedMinutes = ({
35325
35418
  const elapsed = differenceInMinutes(now4, shiftStartDate);
35326
35419
  return Math.min(Math.max(elapsed, 0), shiftMinutes);
35327
35420
  };
35421
+ var maskFutureHourlySeries = ({
35422
+ data,
35423
+ shiftStart,
35424
+ shiftEnd,
35425
+ shiftDate,
35426
+ timezone,
35427
+ now: now4 = /* @__PURE__ */ new Date()
35428
+ }) => {
35429
+ if (!Array.isArray(data)) {
35430
+ return [];
35431
+ }
35432
+ const normalizedData = data.map((value) => typeof value === "number" && Number.isFinite(value) ? value : null);
35433
+ if (!normalizedData.length) {
35434
+ return normalizedData;
35435
+ }
35436
+ const shiftMinutes = getShiftDurationMinutes(shiftStart, shiftEnd);
35437
+ const elapsedMinutes = getShiftElapsedMinutes({
35438
+ shiftStart,
35439
+ shiftEnd,
35440
+ shiftDate,
35441
+ timezone,
35442
+ now: now4
35443
+ });
35444
+ if (shiftMinutes === null || elapsedMinutes === null || elapsedMinutes >= shiftMinutes) {
35445
+ return normalizedData;
35446
+ }
35447
+ return normalizedData.map((value, index) => {
35448
+ const slotStartMinutes = index * 60;
35449
+ return slotStartMinutes > elapsedMinutes ? null : value;
35450
+ });
35451
+ };
35328
35452
  var buildUptimeSeries = ({
35329
35453
  idleTimeHourly,
35330
35454
  shiftStart,
@@ -48863,7 +48987,7 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
48863
48987
  setIsGenerating(true);
48864
48988
  try {
48865
48989
  const isUptimeMode = workspace.monitoring_mode === "uptime";
48866
- const isAssemblyCycleMode = !isUptimeMode && workspace.line_assembly_enabled === true && workspace.action_type === "assembly";
48990
+ const isAssemblyCycleMode = !isUptimeMode && shouldUseAssemblyCycleTimeLayout(workspace);
48867
48991
  const shiftMinutes = getShiftDurationMinutes(workspace.shift_start, workspace.shift_end);
48868
48992
  const shiftSeconds = shiftMinutes ? shiftMinutes * 60 : 0;
48869
48993
  const idleSeconds = Math.max(workspace.idle_time || 0, 0);
@@ -60324,6 +60448,7 @@ var MonthlyRangeFilter = ({
60324
60448
  {
60325
60449
  type: "button",
60326
60450
  onClick: () => setIsOpen((prev) => !prev),
60451
+ "aria-label": showLabel ? void 0 : singleDateOnly ? "Select date" : "Select date range",
60327
60452
  className: clsx(
60328
60453
  "flex items-center transition-all duration-200 focus:outline-none",
60329
60454
  !showLabel && "p-2 rounded-full hover:bg-gray-100",
@@ -62936,7 +63061,7 @@ var KPIsOverviewView = ({
62936
63061
  () => getShiftEndDate(currentShiftDetails, configuredTimezone),
62937
63062
  [currentShiftDetails, configuredTimezone]
62938
63063
  );
62939
- React141__default.useMemo(() => {
63064
+ const leaderboardShiftOptions = React141__default.useMemo(() => {
62940
63065
  if (shiftConfig?.shifts && shiftConfig.shifts.length > 0) {
62941
63066
  return shiftConfig.shifts.map((shift) => ({
62942
63067
  id: shift.shiftId,
@@ -63735,6 +63860,17 @@ var KPIsOverviewView = ({
63735
63860
  singleDateOnly: true
63736
63861
  }
63737
63862
  ),
63863
+ /* @__PURE__ */ jsx("div", { className: "relative", children: /* @__PURE__ */ jsx(
63864
+ "select",
63865
+ {
63866
+ "aria-label": "Leaderboard shift",
63867
+ value: effectiveLeaderboardShiftId,
63868
+ onChange: (e) => setSelectedLeaderboardShiftId(Number(e.target.value)),
63869
+ 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]",
63870
+ 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` },
63871
+ children: leaderboardShiftOptions.map((shiftOption) => /* @__PURE__ */ jsx("option", { value: shiftOption.id, children: shiftOption.label }, shiftOption.id))
63872
+ }
63873
+ ) }),
63738
63874
  isHistoricalLeaderboardDaily && /* @__PURE__ */ jsx(
63739
63875
  "button",
63740
63876
  {
@@ -68022,8 +68158,74 @@ var TargetsView = ({
68022
68158
  var TargetsViewWithDisplayNames = withAllWorkspaceDisplayNames(TargetsView);
68023
68159
  var TargetsView_default = TargetsViewWithDisplayNames;
68024
68160
  var AuthenticatedTargetsView = withAuth(React141__default.memo(TargetsViewWithDisplayNames));
68161
+ function useTimezone(options = {}) {
68162
+ const dashboardConfig = useDashboardConfig();
68163
+ const workspaceConfig = useWorkspaceConfig();
68164
+ const defaultTimezone = dashboardConfig?.dateTimeConfig?.defaultTimezone || "Asia/Kolkata";
68165
+ const [timezone, setTimezone] = useState(defaultTimezone);
68166
+ const [isLoading, setIsLoading] = useState(true);
68167
+ const [error, setError] = useState(null);
68168
+ const fetchTimezone = useCallback(async () => {
68169
+ setIsLoading(true);
68170
+ setError(null);
68171
+ try {
68172
+ let fetchedTimezone = defaultTimezone;
68173
+ if (options.lineId) {
68174
+ fetchedTimezone = await timezoneService.getTimezoneForLine(options.lineId, defaultTimezone);
68175
+ } else if (options.workspaceId || workspaceConfig && "id" in workspaceConfig) {
68176
+ const wsId = options.workspaceId || (workspaceConfig && "id" in workspaceConfig ? workspaceConfig.id : void 0);
68177
+ if (wsId) {
68178
+ fetchedTimezone = await timezoneService.getTimezoneForWorkspace(wsId, defaultTimezone);
68179
+ }
68180
+ } else if (options.companyId || dashboardConfig && "company" in dashboardConfig) {
68181
+ const compId = options.companyId || (dashboardConfig && "company" in dashboardConfig ? dashboardConfig.company?.id : void 0);
68182
+ if (compId) {
68183
+ fetchedTimezone = await timezoneService.getTimezoneForCompany(compId, defaultTimezone);
68184
+ }
68185
+ }
68186
+ setTimezone(fetchedTimezone);
68187
+ } catch (err) {
68188
+ console.error("Error fetching timezone:", err);
68189
+ setError(err instanceof Error ? err : new Error("Failed to fetch timezone"));
68190
+ setTimezone(defaultTimezone);
68191
+ } finally {
68192
+ setIsLoading(false);
68193
+ }
68194
+ }, [
68195
+ options.lineId,
68196
+ options.workspaceId,
68197
+ options.companyId,
68198
+ workspaceConfig,
68199
+ dashboardConfig,
68200
+ defaultTimezone
68201
+ ]);
68202
+ useEffect(() => {
68203
+ fetchTimezone();
68204
+ }, [fetchTimezone]);
68205
+ return {
68206
+ timezone,
68207
+ isLoading,
68208
+ error,
68209
+ refetch: fetchTimezone
68210
+ };
68211
+ }
68025
68212
 
68026
68213
  // src/views/workspace-detail-view.utils.ts
68214
+ var getWorkspaceCycleTimePresentation = ({
68215
+ workspace,
68216
+ showCycleTimeChart
68217
+ }) => {
68218
+ if (workspace?.monitoring_mode === "uptime") {
68219
+ return "output";
68220
+ }
68221
+ if (showCycleTimeChart === false) {
68222
+ return "output";
68223
+ }
68224
+ if (!shouldUseAssemblyCycleTimeLayout(workspace)) {
68225
+ return "output";
68226
+ }
68227
+ return workspace?.cycle_time_data_status === "missing_clips" ? "cycle_unavailable" : "cycle_chart";
68228
+ };
68027
68229
  var formatDateInTimezone = (date = /* @__PURE__ */ new Date(), timezone, options) => {
68028
68230
  const defaultOptions = {
68029
68231
  day: "numeric",
@@ -68407,6 +68609,7 @@ var WorkspaceDetailView = ({
68407
68609
  date: cachedOverviewMetrics.date,
68408
68610
  shift_id: cachedOverviewMetrics.shift_id,
68409
68611
  action_name: "",
68612
+ action_family: cachedOverviewMetrics.action_family ?? null,
68410
68613
  action_type: cachedOverviewMetrics.action_type ?? null,
68411
68614
  monitoring_mode: cachedOverviewMetrics.monitoring_mode ?? "output",
68412
68615
  shift_start: shiftDefinition?.startTime || "",
@@ -68430,6 +68633,11 @@ var WorkspaceDetailView = ({
68430
68633
  };
68431
68634
  }, [cachedOverviewMetrics, shiftConfig?.shifts]);
68432
68635
  const workspace = (isHistoricView ? historicMetrics : liveMetrics) || cachedDetailedMetrics || overviewFallback;
68636
+ const { timezone: cycleTimeTimezone } = useTimezone({
68637
+ lineId: effectiveLineId || workspace?.line_id || void 0,
68638
+ workspaceId: workspaceId || void 0
68639
+ });
68640
+ const effectiveCycleTimeTimezone = cycleTimeTimezone || timezone;
68433
68641
  const detailedWorkspaceMetrics = (isHistoricView ? historicMetrics : liveMetrics) || cachedDetailedMetrics;
68434
68642
  const cycleTimeChartData = useMemo(
68435
68643
  () => Array.isArray(workspace?.hourly_cycle_times) ? workspace.hourly_cycle_times.map((value) => {
@@ -68438,12 +68646,31 @@ var WorkspaceDetailView = ({
68438
68646
  }) : [],
68439
68647
  [workspace?.hourly_cycle_times]
68440
68648
  );
68649
+ const maskedCycleTimeChartData = useMemo(
68650
+ () => maskFutureHourlySeries({
68651
+ data: cycleTimeChartData,
68652
+ shiftStart: workspace?.shift_start,
68653
+ shiftEnd: workspace?.shift_end,
68654
+ shiftDate: workspace?.date || date || calculatedOperationalDate || null,
68655
+ timezone: effectiveCycleTimeTimezone
68656
+ }),
68657
+ [
68658
+ cycleTimeChartData,
68659
+ workspace?.shift_start,
68660
+ workspace?.shift_end,
68661
+ workspace?.date,
68662
+ date,
68663
+ calculatedOperationalDate,
68664
+ effectiveCycleTimeTimezone
68665
+ ]
68666
+ );
68441
68667
  const cycleTimeDatasetKey = useMemo(
68442
68668
  () => [
68443
68669
  workspace?.workspace_id || workspaceId || "workspace",
68444
68670
  date || workspace?.date || "live",
68445
68671
  parsedShiftId ?? workspace?.shift_id ?? "current",
68446
- "hourly"
68672
+ "hourly",
68673
+ "backend"
68447
68674
  ].join(":"),
68448
68675
  [workspace?.workspace_id, workspaceId, date, workspace?.date, parsedShiftId, workspace?.shift_id]
68449
68676
  );
@@ -68688,14 +68915,28 @@ var WorkspaceDetailView = ({
68688
68915
  return filterDataByDateKeyRange(monthlyData, range);
68689
68916
  }, [monthlyData, range]);
68690
68917
  const formattedWorkspaceName = displayName || formatWorkspaceName3(workspace?.workspace_name || "", effectiveLineId);
68691
- const workspaceActionType = workspace && "action_type" in workspace ? workspace.action_type : void 0;
68692
- const workspaceAssemblyEnabled = workspace && "line_assembly_enabled" in workspace ? workspace.line_assembly_enabled === true : false;
68693
- const isAssemblyWorkspace = workspaceActionType === "assembly" || workspaceAssemblyEnabled;
68694
- const shouldShowCycleTimeChart = !isUptimeMode && (showCycleTimeChart ?? isAssemblyWorkspace);
68918
+ const workspaceCycleTimeEligibility = workspace ? {
68919
+ line_assembly_enabled: workspace.line_assembly_enabled,
68920
+ action_family: workspace.action_family,
68921
+ action_type: workspace.action_type
68922
+ } : null;
68923
+ const isAssemblyWorkspace = shouldUseAssemblyCycleTimeLayout(workspaceCycleTimeEligibility);
68924
+ const cycleTimePresentation = getWorkspaceCycleTimePresentation({
68925
+ workspace: workspace ? {
68926
+ monitoring_mode: workspace.monitoring_mode,
68927
+ line_assembly_enabled: workspace.line_assembly_enabled,
68928
+ action_family: workspace.action_family,
68929
+ action_type: workspace.action_type,
68930
+ cycle_time_data_status: workspace.cycle_time_data_status
68931
+ } : null,
68932
+ showCycleTimeChart
68933
+ });
68934
+ const shouldShowCycleTimeChart = cycleTimePresentation !== "output";
68935
+ const shouldShowCycleTimeUnavailableState = cycleTimePresentation === "cycle_unavailable";
68695
68936
  const showIdleBreakdownChart = !shouldShowCycleTimeChart && idleTimeVlmEnabled;
68696
68937
  const idleClipDate = date || workspace?.date || calculatedOperationalDate || getOperationalDate(timezone);
68697
68938
  const idleClipShiftId = parsedShiftId ?? workspace?.shift_id;
68698
- const hourlyIdleMinutes = useMemo(() => {
68939
+ const rawHourlyIdleMinutes = useMemo(() => {
68699
68940
  if (!shouldShowCycleTimeChart || !workspace?.idle_time_hourly || !workspace?.shift_start) return [];
68700
68941
  const parseTimeToMinutes3 = (time2) => {
68701
68942
  const [h, m] = time2.split(":").map(Number);
@@ -68729,6 +68970,30 @@ var WorkspaceDetailView = ({
68729
68970
  }
68730
68971
  return result;
68731
68972
  }, [shouldShowCycleTimeChart, workspace?.idle_time_hourly, workspace?.shift_start, workspace?.shift_end]);
68973
+ const hourlyIdleMinutes = useMemo(
68974
+ () => maskFutureHourlySeries({
68975
+ data: rawHourlyIdleMinutes,
68976
+ shiftStart: workspace?.shift_start,
68977
+ shiftEnd: workspace?.shift_end,
68978
+ shiftDate: idleClipDate,
68979
+ timezone: effectiveCycleTimeTimezone
68980
+ }),
68981
+ [
68982
+ rawHourlyIdleMinutes,
68983
+ workspace?.shift_start,
68984
+ workspace?.shift_end,
68985
+ idleClipDate,
68986
+ effectiveCycleTimeTimezone
68987
+ ]
68988
+ );
68989
+ const cycleTimeUnavailableView = useMemo(() => /* @__PURE__ */ 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: [
68990
+ /* @__PURE__ */ jsx("h4", { className: "text-base font-semibold text-amber-900", children: "Cycle data unavailable" }),
68991
+ /* @__PURE__ */ 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." }),
68992
+ typeof workspace?.cycle_completion_clip_count === "number" && /* @__PURE__ */ jsxs("p", { className: "mt-3 text-xs font-medium uppercase tracking-[0.08em] text-amber-700", children: [
68993
+ "matched cycle clips: ",
68994
+ workspace.cycle_completion_clip_count
68995
+ ] })
68996
+ ] }), [workspace?.cycle_completion_clip_count]);
68732
68997
  const shiftDurationMinutes = useMemo(
68733
68998
  () => getShiftDurationMinutes(workspace?.shift_start, workspace?.shift_end),
68734
68999
  [workspace?.shift_start, workspace?.shift_end]
@@ -69225,7 +69490,7 @@ var WorkspaceDetailView = ({
69225
69490
  children: [
69226
69491
  /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center mb-4", children: [
69227
69492
  /* @__PURE__ */ jsx("h3", { className: "text-lg font-bold text-gray-700", children: isUptimeMode ? "Machine Utilization" : shouldShowCycleTimeChart ? "Cycle time trend" : "Hourly Output" }),
69228
- !isUptimeMode && /* @__PURE__ */ jsx(
69493
+ !isUptimeMode && !shouldShowCycleTimeUnavailableState && /* @__PURE__ */ jsx(
69229
69494
  "button",
69230
69495
  {
69231
69496
  onClick: () => setShowChartIdleTime(!showChartIdleTime),
@@ -69254,10 +69519,10 @@ var WorkspaceDetailView = ({
69254
69519
  timezone,
69255
69520
  elapsedMinutes: elapsedShiftMinutes
69256
69521
  }
69257
- ) : shouldShowCycleTimeChart ? /* @__PURE__ */ jsx(
69522
+ ) : shouldShowCycleTimeUnavailableState ? cycleTimeUnavailableView : shouldShowCycleTimeChart ? /* @__PURE__ */ jsx(
69258
69523
  CycleTimeOverTimeChart,
69259
69524
  {
69260
- data: cycleTimeChartData,
69525
+ data: maskedCycleTimeChartData,
69261
69526
  idealCycleTime: workspace.ideal_cycle_time || 0,
69262
69527
  shiftStart: workspace.shift_start || "",
69263
69528
  shiftEnd: workspace.shift_end || "",
@@ -69358,7 +69623,7 @@ var WorkspaceDetailView = ({
69358
69623
  children: [
69359
69624
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3 mb-4 flex-none", children: [
69360
69625
  /* @__PURE__ */ jsx("h3", { className: "text-lg font-bold text-gray-700", children: isUptimeMode ? "Machine Utilization" : shouldShowCycleTimeChart ? "Cycle time trend" : "Hourly Output" }),
69361
- !isUptimeMode && /* @__PURE__ */ jsx(
69626
+ !isUptimeMode && !shouldShowCycleTimeUnavailableState && /* @__PURE__ */ jsx(
69362
69627
  "button",
69363
69628
  {
69364
69629
  onClick: () => setShowChartIdleTime(!showChartIdleTime),
@@ -69383,10 +69648,10 @@ var WorkspaceDetailView = ({
69383
69648
  timezone,
69384
69649
  elapsedMinutes: elapsedShiftMinutes
69385
69650
  }
69386
- ) : shouldShowCycleTimeChart ? /* @__PURE__ */ jsx(
69651
+ ) : shouldShowCycleTimeUnavailableState ? cycleTimeUnavailableView : shouldShowCycleTimeChart ? /* @__PURE__ */ jsx(
69387
69652
  CycleTimeOverTimeChart,
69388
69653
  {
69389
- data: cycleTimeChartData,
69654
+ data: maskedCycleTimeChartData,
69390
69655
  idealCycleTime: workspace.ideal_cycle_time || 0,
69391
69656
  shiftStart: workspace.shift_start || "",
69392
69657
  shiftEnd: workspace.shift_end || "",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optifye/dashboard-core",
3
- "version": "6.11.13",
3
+ "version": "6.11.15",
4
4
  "description": "Reusable UI & logic for Optifye dashboard",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",