@sybilion/uilib 1.3.23 → 1.3.26

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.
Files changed (52) hide show
  1. package/dist/esm/components/ui/TextWithDeferTooltip/TextWithDeferTooltip.js +1 -25
  2. package/dist/esm/components/ui/Tooltip/Tooltip.js +92 -7
  3. package/dist/esm/components/ui/Tooltip/Tooltip.styl.js +2 -2
  4. package/dist/esm/components/widgets/DriversComparisonChart/DriversComparisonChart.js +1 -2
  5. package/dist/esm/components/widgets/PerformanceChart/HorizonsSelector/HorizonsSelector.js +34 -0
  6. package/dist/esm/components/widgets/PerformanceChart/HorizonsSelector/HorizonsSelector.styl.js +7 -0
  7. package/dist/esm/components/widgets/PerformanceChart/PerformanceChart.constants.js +17 -0
  8. package/dist/esm/components/widgets/PerformanceChart/PerformanceChart.js +807 -0
  9. package/dist/esm/components/widgets/PerformanceChart/PerformanceChart.styl.js +7 -0
  10. package/dist/esm/components/widgets/PerformanceChart/PerformanceTable.js +130 -0
  11. package/dist/esm/components/widgets/PerformanceChart/PerformanceUnderChartLegend/PerformanceUnderChartLegend.js +20 -0
  12. package/dist/esm/components/widgets/PerformanceChart/PerformanceUnderChartLegend/PerformanceUnderChartLegend.styl.js +7 -0
  13. package/dist/esm/components/widgets/PerformanceChart/performanceChart.helpers.js +591 -0
  14. package/dist/esm/components/widgets/PerformanceChart/performanceChartUserSeries.js +109 -0
  15. package/dist/esm/index.js +4 -0
  16. package/dist/esm/types/src/components/ui/Tooltip/Tooltip.d.ts +3 -3
  17. package/dist/esm/types/src/components/ui/Tooltip/Tooltip.types.d.ts +1 -0
  18. package/dist/esm/types/src/components/widgets/PerformanceChart/HorizonsSelector/HorizonsSelector.d.ts +7 -0
  19. package/dist/esm/types/src/components/widgets/PerformanceChart/PerformanceChart.constants.d.ts +3 -0
  20. package/dist/esm/types/src/components/widgets/PerformanceChart/PerformanceChart.d.ts +54 -0
  21. package/dist/esm/types/src/components/widgets/PerformanceChart/PerformanceTable.d.ts +31 -0
  22. package/dist/esm/types/src/components/widgets/PerformanceChart/PerformanceUnderChartLegend/PerformanceUnderChartLegend.d.ts +20 -0
  23. package/dist/esm/types/src/components/widgets/PerformanceChart/index.d.ts +4 -0
  24. package/dist/esm/types/src/components/widgets/PerformanceChart/performanceChart.helpers.d.ts +212 -0
  25. package/dist/esm/types/src/components/widgets/PerformanceChart/performanceChartUserSeries.d.ts +20 -0
  26. package/dist/esm/types/src/docs/pages/PerformanceChartPage.d.ts +1 -0
  27. package/dist/esm/types/src/index.d.ts +1 -0
  28. package/package.json +1 -1
  29. package/src/components/ui/TextWithDeferTooltip/TextWithDeferTooltip.tsx +5 -37
  30. package/src/components/ui/Tooltip/Tooltip.styl +12 -0
  31. package/src/components/ui/Tooltip/Tooltip.styl.d.ts +1 -0
  32. package/src/components/ui/Tooltip/Tooltip.tsx +156 -8
  33. package/src/components/ui/Tooltip/Tooltip.types.ts +1 -0
  34. package/src/components/widgets/PerformanceChart/HorizonsSelector/HorizonsSelector.styl +25 -0
  35. package/src/components/widgets/PerformanceChart/HorizonsSelector/HorizonsSelector.styl.d.ts +11 -0
  36. package/src/components/widgets/PerformanceChart/HorizonsSelector/HorizonsSelector.tsx +67 -0
  37. package/src/components/widgets/PerformanceChart/PerformanceChart.constants.ts +17 -0
  38. package/src/components/widgets/PerformanceChart/PerformanceChart.styl +194 -0
  39. package/src/components/widgets/PerformanceChart/PerformanceChart.styl.d.ts +30 -0
  40. package/src/components/widgets/PerformanceChart/PerformanceChart.tsx +1251 -0
  41. package/src/components/widgets/PerformanceChart/PerformanceTable.tsx +381 -0
  42. package/src/components/widgets/PerformanceChart/PerformanceUnderChartLegend/PerformanceUnderChartLegend.styl +49 -0
  43. package/src/components/widgets/PerformanceChart/PerformanceUnderChartLegend/PerformanceUnderChartLegend.styl.d.ts +12 -0
  44. package/src/components/widgets/PerformanceChart/PerformanceUnderChartLegend/PerformanceUnderChartLegend.tsx +83 -0
  45. package/src/components/widgets/PerformanceChart/index.ts +28 -0
  46. package/src/components/widgets/PerformanceChart/performanceChart.helpers.ts +790 -0
  47. package/src/components/widgets/PerformanceChart/performanceChartUserSeries.ts +149 -0
  48. package/src/docs/pages/PerformanceChartPage.tsx +211 -0
  49. package/src/docs/pages/TextWithDeferTooltipPage.tsx +26 -10
  50. package/src/docs/pages/TooltipPage.tsx +30 -0
  51. package/src/docs/registry.ts +6 -0
  52. package/src/index.ts +1 -0
package/dist/esm/index.js CHANGED
@@ -106,6 +106,10 @@ export { getDriverImportance, getHighestImportanceDriver } from './components/wi
106
106
  export { geographicCoordinates, geographicToSVG, getContinentFromRegion, getPreciseCoordinates, getResponsiveCoordinates, svgToPercentage } from './components/widgets/DriverMap/driverMapGeography.js';
107
107
  export { DriversComparisonChart } from './components/widgets/DriversComparisonChart/DriversComparisonChart.js';
108
108
  export { DRIVER_COMPARISON_CHART_LEAD_MONTHS, DRIVER_FORECAST_ID_BASE, INITIAL_VISIBLE_SERIES_COUNT, buildDriversComparisonChartData, formatSeriesImportance, mergeBacktestsChartData, mergeDatasetHistoricalWithBacktestsChartData } from './components/widgets/DriversComparisonChart/driversComparisonChart.helpers.js';
109
+ export { PerformanceChart } from './components/widgets/PerformanceChart/PerformanceChart.js';
110
+ export { PerformanceTable } from './components/widgets/PerformanceChart/PerformanceTable.js';
111
+ export { SPAGHETTI_DRIFT_PER_HORIZON_ID_BASE, SPAGHETTI_MODEL_PER_HORIZON_ID_BASE, averageForecastErrorsVsHistoricalForMatrixColumn, buildDriftSpaghettiMatrixForCustomDialog, buildPerHorizonSpaghettiEntries, buildSpaghettiMergedChartData, calculateYRangeFromChartData, getForecastModelDisplayName, spaghettiGridFromHistoricalPreviousMonth } from './components/widgets/PerformanceChart/performanceChart.helpers.js';
112
+ export { SPAGHETTI_LOCAL_LS_USER_SERIES_ROW_ID, SPAGHETTI_TIME_SERIES_MATRIX_V, tryParseSpaghettiPerformanceMatrix } from './components/widgets/PerformanceChart/performanceChartUserSeries.js';
109
113
  export { SybilionAppHeader } from './components/widgets/SybilionAppHeader/SybilionAppHeader.js';
110
114
  export { SybilionAuthLayout } from './components/widgets/SybilionAuthLayout/SybilionAuthLayout.js';
111
115
  export { SybilionAuthHeadline } from './components/widgets/SybilionAuthLayout/SybilionAuthHeadline.js';
@@ -1,6 +1,6 @@
1
1
  import type { TooltipContentProps, TooltipProps, TooltipProviderProps, TooltipTriggerProps } from './Tooltip.types';
2
2
  declare function TooltipProvider({ delayDuration, ...props }: TooltipProviderProps): import("react/jsx-runtime").JSX.Element;
3
- declare function Tooltip(props: TooltipProps): import("react/jsx-runtime").JSX.Element;
4
- declare function TooltipTrigger({ ...props }: TooltipTriggerProps): import("react/jsx-runtime").JSX.Element;
5
- declare function TooltipContent({ className, sideOffset, children, maxWidth, ...props }: TooltipContentProps): import("react/jsx-runtime").JSX.Element;
3
+ declare function Tooltip({ children, ...props }: TooltipProps): import("react/jsx-runtime").JSX.Element;
4
+ declare function TooltipTrigger({ ref, ...props }: TooltipTriggerProps): import("react/jsx-runtime").JSX.Element;
5
+ declare function TooltipContent({ className, sideOffset, children, maxWidth, overTrigger, align, side, avoidCollisions, style: styleProp, ...props }: TooltipContentProps): import("react/jsx-runtime").JSX.Element;
6
6
  export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
@@ -10,4 +10,5 @@ export interface TooltipContentProps extends React.ComponentProps<typeof Tooltip
10
10
  className?: string;
11
11
  sideOffset?: number;
12
12
  maxWidth?: number;
13
+ overTrigger?: boolean;
13
14
  }
@@ -0,0 +1,7 @@
1
+ interface HorizonsSelectorProps {
2
+ selectedHorizon: string;
3
+ onHorizonChange: (horizon: string) => void;
4
+ availableHorizons: string[];
5
+ }
6
+ export declare function HorizonsSelector({ selectedHorizon, onHorizonChange, availableHorizons, }: HorizonsSelectorProps): import("react/jsx-runtime").JSX.Element;
7
+ export {};
@@ -0,0 +1,3 @@
1
+ export declare const METRICS_KEY: "24m";
2
+ export declare const MONTHS_24 = 24;
3
+ export declare const MONTH_NAMES: string[];
@@ -0,0 +1,54 @@
1
+ import { type ReactNode } from 'react';
2
+ import type { ChartDataPoint } from '#uilib/components/ui/ChartAreaInteractive/ChartAreaInteractive.types';
3
+ import type { ForecastData } from '#uilib/types/forecast-data';
4
+ import { type SpaghettiPerformanceMatrixPayload } from './performanceChartUserSeries';
5
+ export type PerformanceViewTab = 'perHorizon' | 'spaghetti';
6
+ export type PerformanceChartPayload = Record<string, {
7
+ forecasts?: Record<string, Record<string, number>>;
8
+ metrics_history?: Record<string, {
9
+ mae?: Record<string, number>;
10
+ mape?: Record<string, number>;
11
+ }>;
12
+ }>;
13
+ export type PerformanceChartProps = {
14
+ performanceData: PerformanceChartPayload | null;
15
+ historicalData?: ChartDataPoint[];
16
+ combinedData?: ChartDataPoint[];
17
+ forecastData?: Record<string, ForecastData>;
18
+ userSeries?: {
19
+ id: number;
20
+ name?: string;
21
+ }[];
22
+ spaghettiUserSeries?: {
23
+ id: number;
24
+ name?: string;
25
+ }[];
26
+ customPerformanceMatrix?: SpaghettiPerformanceMatrixPayload | null;
27
+ customPerformanceLabel?: string | null;
28
+ loading?: boolean;
29
+ chartLoading?: boolean;
30
+ performanceSectionPending?: boolean;
31
+ isEmpty?: boolean;
32
+ perfFetchSettled?: boolean;
33
+ performanceDataLoading?: boolean;
34
+ performanceAnalysisId?: number | null;
35
+ statusHint?: string | null;
36
+ statusTone?: 'destructive' | 'muted';
37
+ runAnalysisHint?: boolean;
38
+ timeRange?: string;
39
+ onTimeRangeChange?: (range: string, meta?: {
40
+ viewTab: PerformanceViewTab;
41
+ }) => void;
42
+ isDarkTheme?: boolean;
43
+ className?: string;
44
+ seriesInitKey?: string;
45
+ toolbarStart?: ReactNode;
46
+ onEditCustomPerformance?: () => void;
47
+ showAddEditCustomDataButton?: boolean;
48
+ addEditCustomDataDisabled?: boolean;
49
+ hiddenSeries?: Set<string>;
50
+ onToggleLegendSeries?: (key: string) => void;
51
+ onEnsureSeriesVisible?: (key: string) => void;
52
+ onHiddenSeriesChange?: (update: Set<string> | ((prev: Set<string>) => Set<string>)) => void;
53
+ };
54
+ export declare function PerformanceChart({ performanceData, historicalData, combinedData, forecastData, userSeries, spaghettiUserSeries, customPerformanceMatrix, customPerformanceLabel, loading, chartLoading, performanceSectionPending, isEmpty, perfFetchSettled, performanceDataLoading, performanceAnalysisId, statusHint, statusTone, runAnalysisHint, timeRange: timeRangeProp, onTimeRangeChange, isDarkTheme, className, seriesInitKey, toolbarStart, onEditCustomPerformance, showAddEditCustomDataButton, addEditCustomDataDisabled, hiddenSeries: hiddenSeriesProp, onToggleLegendSeries, onEnsureSeriesVisible, onHiddenSeriesChange, }: PerformanceChartProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,31 @@
1
+ export interface ForecastModelData {
2
+ key: string;
3
+ mae: number;
4
+ mape: number;
5
+ }
6
+ export interface AdjustParameters {
7
+ procurementVolume: number;
8
+ variableRawMaterialCostShare: number;
9
+ controllableCosts: number;
10
+ currentForecastAccuracy: number;
11
+ expectedImprovement: number;
12
+ analystHourlyRate: number;
13
+ }
14
+ interface PerformanceTableProps {
15
+ forecastModels: ForecastModelData[];
16
+ adjustParameters: AdjustParameters;
17
+ onAdjustParametersChange: (params: AdjustParameters) => void;
18
+ /** Shown when custom spaghetti matrix is saved (localStorage). Metrics vs historical for selected horizon. */
19
+ customPerformance?: {
20
+ label: string;
21
+ mae: number;
22
+ mape: number;
23
+ } | null;
24
+ /** Opens custom performance dialog; used by the custom row edit control. */
25
+ onEditCustomPerformance?: () => void;
26
+ /** Last row with Add custom data (hidden when custom row already present); per-horizon + spaghetti. */
27
+ showAddEditCustomDataButton?: boolean;
28
+ addEditCustomDataDisabled?: boolean;
29
+ }
30
+ export declare function PerformanceTable({ forecastModels, adjustParameters, onAdjustParametersChange, customPerformance, onEditCustomPerformance, showAddEditCustomDataButton, addEditCustomDataDisabled, }: PerformanceTableProps): import("react/jsx-runtime").JSX.Element;
31
+ export {};
@@ -0,0 +1,20 @@
1
+ export type PerformanceUnderChartLegendRowProps = {
2
+ label: React.ReactNode;
3
+ /** Line color (CSS color string). */
4
+ lineColor: string;
5
+ /** Historical uses solid bar; forecast lines use dashed preview. */
6
+ lineStyle?: 'solid' | 'dashed';
7
+ hidden: boolean;
8
+ onToggle?: () => void;
9
+ };
10
+ export type PerformanceUnderChartLegendItemConfig = PerformanceUnderChartLegendRowProps & {
11
+ id: string;
12
+ };
13
+ export declare function PerformanceUnderChartLegendRow({ label, lineColor, lineStyle, hidden, onToggle, }: PerformanceUnderChartLegendRowProps): import("react/jsx-runtime").JSX.Element;
14
+ /** Renders rows from tab-agnostic config (labels + toggles supplied by parent). */
15
+ export declare function PerformanceUnderChartLegendFromItems({ items, }: {
16
+ items: PerformanceUnderChartLegendItemConfig[];
17
+ }): import("react/jsx-runtime").JSX.Element;
18
+ export declare function PerformanceUnderChartLegend({ children, }: {
19
+ children: React.ReactNode;
20
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,4 @@
1
+ export { PerformanceChart, type PerformanceChartPayload, type PerformanceChartProps, type PerformanceViewTab, } from './PerformanceChart';
2
+ export { type AdjustParameters, type ForecastModelData, PerformanceTable, } from './PerformanceTable';
3
+ export { SPAGHETTI_DRIFT_PER_HORIZON_ID_BASE, SPAGHETTI_MODEL_PER_HORIZON_ID_BASE, averageForecastErrorsVsHistoricalForMatrixColumn, buildDriftSpaghettiMatrixForCustomDialog, buildPerHorizonSpaghettiEntries, buildSpaghettiMergedChartData, calculateYRangeFromChartData, getForecastModelDisplayName, spaghettiGridFromHistoricalPreviousMonth, } from './performanceChart.helpers';
4
+ export { SPAGHETTI_LOCAL_LS_USER_SERIES_ROW_ID, SPAGHETTI_TIME_SERIES_MATRIX_V, tryParseSpaghettiPerformanceMatrix, type SpaghettiPerformanceMatrixPayload, } from './performanceChartUserSeries';
@@ -0,0 +1,212 @@
1
+ /**
2
+ * Helper functions for Performance Tab calculations
3
+ * Note: ROI/calculatons.ts is only for reference/example
4
+ */
5
+ import type { ChartDataPoint } from '#uilib/components/ui/ChartAreaInteractive/ChartAreaInteractive.types';
6
+ import type { ForecastData } from '#uilib/types/forecast-data';
7
+ import type { SpaghettiPerformanceMatrixPayload } from './performanceChartUserSeries';
8
+ export type RealBacktestsEntry = {
9
+ forecast_start: string;
10
+ forecast_end: string;
11
+ forecast_series: Record<string, number>;
12
+ };
13
+ /** Legacy: API backtest spaghetti lines used `SPAGHETTI_FORECAST_ID_BASE + i` (spaghetti view now uses drift per-horizon instead). */
14
+ export declare const SPAGHETTI_FORECAST_ID_BASE = 6000;
15
+ /** Model PER_HORIZON_TO_SPAGHETTI lines use `SPAGHETTI_MODEL_PER_HORIZON_ID_BASE + i`. */
16
+ export declare const SPAGHETTI_MODEL_PER_HORIZON_ID_BASE = 7000;
17
+ /** Drift PER_HORIZON_TO_SPAGHETTI lines use `SPAGHETTI_DRIFT_PER_HORIZON_ID_BASE + i`. */
18
+ export declare const SPAGHETTI_DRIFT_PER_HORIZON_ID_BASE = 8000;
19
+ export declare function isSpaghettiModelPerHorizonLineId(id: number): boolean;
20
+ export declare function isSpaghettiDriftPerHorizonLineId(id: number): boolean;
21
+ /**
22
+ * Converts `performance.model` / `performance.drift` per-horizon forecasts into synthetic
23
+ * backtest-shaped entries: each line is [horizon_1[i], …, horizon_n[i]] as date→value points
24
+ * (aligned by sorted key index per horizon).
25
+ */
26
+ export declare function buildPerHorizonSpaghettiEntries(forecastRoot: {
27
+ forecasts?: Record<string, Record<string, number>>;
28
+ } | null | undefined, horizonKeys: string[]): RealBacktestsEntry[];
29
+ /**
30
+ * Same row alignment as {@link buildPerHorizonSpaghettiEntries}: row `i` uses each horizon's
31
+ * i-th sorted forecast month; `dates[i]` is horizon_1's month at that row (Date column).
32
+ * Use this for custom dialog seed + “copy statistical baseline (drift)” prefill.
33
+ */
34
+ export declare function buildDriftSpaghettiMatrixForCustomDialog(driftRoot: {
35
+ forecasts?: Record<string, Record<string, number>>;
36
+ } | null | undefined, horizonKeys: string[]): {
37
+ dates: string[];
38
+ grid: number[][];
39
+ perHorizonDates: string[][];
40
+ } | null;
41
+ /**
42
+ * Prefill for custom performance when copying drift layout: each spaghetti row is flat at the
43
+ * historical value for the month before the earliest forecast month in that row (same anchor as
44
+ * chart connection-point logic).
45
+ */
46
+ export declare function spaghettiGridFromHistoricalPreviousMonth(perHorizonDates: string[][], horizonCount: number, historicalByDate: Map<string, number>): number[][];
47
+ /** Mean absolute error / MAPE vs historical for one horizon column of a spaghetti matrix (aligned by month). */
48
+ export declare function averageForecastErrorsVsHistoricalForMatrixColumn(matrix: SpaghettiPerformanceMatrixPayload, horizonColumnIndex: number, historicalByDate: Map<string, number>): {
49
+ mae: number;
50
+ mape: number;
51
+ } | null;
52
+ /** @deprecated Use {@link buildPerHorizonSpaghettiEntries} (same implementation). */
53
+ export declare const buildModelPerHorizonSpaghettiEntries: typeof buildPerHorizonSpaghettiEntries;
54
+ export declare function mergeSpaghettiMergedBases(a: {
55
+ mergedData: ChartDataPoint[];
56
+ seriesMeta: {
57
+ id: number;
58
+ label: string;
59
+ }[];
60
+ }, b: {
61
+ mergedData: ChartDataPoint[];
62
+ seriesMeta: {
63
+ id: number;
64
+ label: string;
65
+ }[];
66
+ }): {
67
+ mergedData: ChartDataPoint[];
68
+ seriesMeta: {
69
+ id: number;
70
+ label: string;
71
+ }[];
72
+ };
73
+ export declare function buildSpaghettiMergedChartData(entries: RealBacktestsEntry[], historicalChartData: ChartDataPoint[], idBase?: number): {
74
+ mergedData: ChartDataPoint[];
75
+ seriesMeta: {
76
+ id: number;
77
+ label: string;
78
+ }[];
79
+ };
80
+ /** Appends forecast-backed user series (from dataset context) onto spaghetti backtest lines. */
81
+ export declare function mergeSpaghettiUserSeriesFromForecastData(base: {
82
+ mergedData: ChartDataPoint[];
83
+ seriesMeta: {
84
+ id: number;
85
+ label: string;
86
+ }[];
87
+ }, userSeries: {
88
+ id: number;
89
+ name?: string;
90
+ }[], forecastData: Record<string, ForecastData>): {
91
+ mergedData: ChartDataPoint[];
92
+ seriesMeta: {
93
+ id: number;
94
+ label: string;
95
+ }[];
96
+ };
97
+ export declare function filterChartDataLast24Months(data: ChartDataPoint[], months: number): ChartDataPoint[];
98
+ /**
99
+ * Spaghetti chart x-range: from two months before the earliest forecast_start
100
+ * (per-horizon synthetic entries + mergeable user forecast series), inclusive.
101
+ * Falls back to last N months when no forecast candidates exist.
102
+ *
103
+ * When `historicalWindowFloor` is set (e.g. first month of per-horizon `filtered24mData`),
104
+ * the range start is floored to that month so the historical line matches the per-horizon tab.
105
+ *
106
+ * When `historicalWindowCeiling` is set (e.g. last month of `filtered24mData`), drops points after
107
+ * that month so spaghetti X matches per-horizon (per-horizon chart trims forecasts past last historical).
108
+ */
109
+ export declare function filterSpaghettiDataFromEarliestForecastStart(mergedData: ChartDataPoint[], entries: RealBacktestsEntry[], userSeries: {
110
+ id: number;
111
+ name?: string;
112
+ }[], forecastData: Record<string, ForecastData>,
113
+ /** Series meta from the first merged batch (e.g. drift) — used to skip duplicate user-series ids. */
114
+ firstBatchSeriesMeta: {
115
+ id: number;
116
+ label: string;
117
+ }[], fallbackMonths: number, extraForecastCandidates?: RealBacktestsEntry[], historicalWindowFloor?: string | null, historicalWindowCeiling?: string | null): ChartDataPoint[];
118
+ /**
119
+ * Ensure each spaghetti row has `historical` from {@link historicalChartData} when that month
120
+ * falls in the filtered window — matches per-horizon chart (actuals line), and fills months that
121
+ * exist in history but were missing after merge/filter.
122
+ */
123
+ export declare function mergeHistoricalIntoSpaghettiChartData(filteredRows: ChartDataPoint[], historicalChartData: ChartDataPoint[]): ChartDataPoint[];
124
+ /**
125
+ * Calculate Accuracy: 100% - MAPE
126
+ * @param mape - MAPE value as decimal (e.g., 0.0436 for 4.36%)
127
+ * @returns Accuracy percentage
128
+ */
129
+ export declare function calculateAccuracy(mape: number): number;
130
+ /**
131
+ * Format Accuracy as percentage
132
+ * @param value - Accuracy value
133
+ * @returns Formatted accuracy string (e.g., "97%")
134
+ */
135
+ export declare function formatAccuracy(value: number): string;
136
+ /**
137
+ * Format Error as "$/Ton"
138
+ * @param mae - MAE value from metrics_summary.24m.mae
139
+ * @returns Formatted error string (e.g., "0.05$/Ton")
140
+ */
141
+ export declare function formatError(mae: number): string;
142
+ /**
143
+ * Calculate ROI
144
+ * Reference: ROI/calculatons.ts for example logic
145
+ * @param totalBenefit - Total annual benefit
146
+ * @param operatingCost - Annual operating cost
147
+ * @returns ROI percentage
148
+ */
149
+ export declare function calculateROI(totalBenefit: number, operatingCost: number): number;
150
+ /**
151
+ * Calculate ROI Multiple
152
+ * Formula: ROI Multiple = ROI / 100
153
+ * Where ROI = (AB - IC) / IC × 100
154
+ * @param totalBenefit - Total annual benefit (AB)
155
+ * @param operatingCost - Annual operating cost (IC)
156
+ * @returns ROI multiple (e.g., 1.0 for 100% ROI, 2.0 for 200% ROI)
157
+ */
158
+ export declare function calculateROIMultiple(totalBenefit: number, operatingCost: number): number;
159
+ /**
160
+ * Format ROI as multiplier (xN format)
161
+ * Formula: ROI Multiple = ROI / 100
162
+ * Examples: 100% ROI = x1, 200% ROI = x2, 0% ROI = x0
163
+ * Note: Negative ROI (when benefit < cost) is displayed as x1
164
+ * @param totalBenefit - Total annual benefit (AB)
165
+ * @param operatingCost - Annual operating cost (IC)
166
+ * @returns Formatted ROI string (e.g., "x1.5")
167
+ */
168
+ export declare function formatROI(totalBenefit: number, operatingCost: number): string;
169
+ /**
170
+ * Calculate Benefit p/a
171
+ * Formula: Annual Benefit = PV × V × C × PI
172
+ * Where:
173
+ * - PV = Procurement Volume
174
+ * - V = Variable Raw Material Share (as decimal)
175
+ * - C = Controllable Cost Share (as decimal)
176
+ * - PI = Price Improvement = |FA_new - FA_current| × 0.2 (correlation factor)
177
+ * @param mae - MAE value (not used in calculation, kept for backward compatibility)
178
+ * @param mape - MAPE value (used to calculate actual forecast accuracy)
179
+ * @param adjustParams - Adjust parameters from dialog
180
+ * @param baselineAccuracy - Optional baseline accuracy for comparison (if provided, uses actual improvement)
181
+ * @returns Benefit per annum
182
+ */
183
+ export declare function calculateBenefit(mae: number, mape: number, adjustParams: {
184
+ procurementVolume: number;
185
+ variableRawMaterialCostShare: number;
186
+ controllableCosts: number;
187
+ currentForecastAccuracy: number;
188
+ expectedImprovement: number;
189
+ analystHourlyRate: number;
190
+ }, baselineAccuracy?: number): number;
191
+ /**
192
+ * Format Benefit as euros
193
+ * @param value - Benefit value
194
+ * @returns Formatted benefit string (e.g., "€616.640")
195
+ */
196
+ export declare function formatBenefit(value: number): string;
197
+ /**
198
+ * Map forecast model key to display name
199
+ * @param key - Forecast model key (e.g., "mean", "drift", "seasonal")
200
+ * @returns Display name (e.g., "Mean", "Drift", "Seasonal")
201
+ */
202
+ export declare function getForecastModelDisplayName(key: string): string;
203
+ /**
204
+ * Calculate Y range (min/max) from chart data points
205
+ * Extracts all numeric values from chart data points and calculates min/max with padding
206
+ * @param chartData - Array of chart data points
207
+ * @returns Object with yMin and yMax values
208
+ */
209
+ export declare function calculateYRangeFromChartData(chartData: Array<Record<string, unknown>>): {
210
+ yMin: number;
211
+ yMax: number;
212
+ };
@@ -0,0 +1,20 @@
1
+ import type { ForecastData } from '#uilib/types/forecast-data';
2
+ export declare const SPAGHETTI_TIME_SERIES_MATRIX_V: 2;
3
+ export interface SpaghettiPerformanceMatrixPayload {
4
+ v: typeof SPAGHETTI_TIME_SERIES_MATRIX_V;
5
+ dates: string[];
6
+ horizonKeys: string[];
7
+ grid: number[][];
8
+ perHorizonDates?: string[][];
9
+ }
10
+ export declare const SPAGHETTI_MATRIX_SYNTHETIC_BASE = 9800000;
11
+ export declare const SPAGHETTI_MATRIX_MAX_COLS = 128;
12
+ export declare const SPAGHETTI_LOCAL_LS_USER_SERIES_ROW_ID = 9000001;
13
+ export declare function spaghettiMatrixSyntheticId(userSeriesRowId: number, horizonIndex: number): number;
14
+ export declare function isSpaghettiMatrixSyntheticLineId(id: number): boolean;
15
+ export declare function getCustomMatrixSeriesForHorizonTab(matrix: SpaghettiPerformanceMatrixPayload, horizonKey: string, rowId: number, forecastData: Record<string, ForecastData> | undefined): {
16
+ lineId: number;
17
+ dates: string[];
18
+ forecastValues: number[];
19
+ } | null;
20
+ export declare function tryParseSpaghettiPerformanceMatrix(parsed: unknown): SpaghettiPerformanceMatrixPayload | null;
@@ -0,0 +1 @@
1
+ export default function PerformanceChartPage(): import("react/jsx-runtime").JSX.Element;
@@ -69,6 +69,7 @@ export * from './components/widgets/SidebarDatasetsItemsGrouped';
69
69
  export * from './components/widgets/DriverCard';
70
70
  export * from './components/widgets/DriverMap';
71
71
  export * from './components/widgets/DriversComparisonChart';
72
+ export * from './components/widgets/PerformanceChart';
72
73
  export * from './components/widgets/SybilionAppHeader';
73
74
  export * from './components/widgets/SybilionAuthLayout';
74
75
  export * from './components/widgets/SybilionSignInPanel';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sybilion/uilib",
3
- "version": "1.3.23",
3
+ "version": "1.3.26",
4
4
  "description": "Sybilion Design System — React UI components (Webpack + Stylus)",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -1,4 +1,4 @@
1
- import { CSSProperties, useRef, useState } from 'react';
1
+ import { useRef, useState } from 'react';
2
2
 
3
3
  import {
4
4
  Tooltip,
@@ -18,9 +18,7 @@ function TextWithDeferTooltip({
18
18
  ...props
19
19
  }: TextWithDeferTooltipProps) {
20
20
  const [withTooltip, setWithTooltip] = useState(false);
21
- const [tooltipStyles, setTooltipStyles] = useState<CSSProperties>({});
22
21
  const ref = useRef<HTMLDivElement>(null);
23
- const cachedFontSizeRef = useRef<string | null>(null);
24
22
 
25
23
  const handleMouseEnter = () => {
26
24
  if (!ref.current) return;
@@ -31,33 +29,6 @@ function TextWithDeferTooltip({
31
29
  ref.current.scrollHeight - ref.current.clientHeight > 3;
32
30
 
33
31
  if (isOverflowingHorizontally || isOverflowingVertically) {
34
- const styles: CSSProperties = {
35
- fontSize: cachedFontSizeRef.current || undefined,
36
- };
37
-
38
- if (ref.current) {
39
- const {
40
- width: rectWidth,
41
- left,
42
- top,
43
- } = ref.current.getBoundingClientRect();
44
-
45
- styles.width = `${width ?? rectWidth}px`;
46
-
47
- if (!cachedFontSizeRef.current) {
48
- const { fontSize } = window.getComputedStyle(ref.current);
49
- cachedFontSizeRef.current = fontSize;
50
- styles.fontSize = fontSize;
51
- }
52
-
53
- if (overTrigger) {
54
- styles.transform = `translate(${left}px, ${top}px) !important`;
55
- // styles.translateX = `${left}px`;
56
- // styles.translateY = `${top}px`;
57
- }
58
- }
59
-
60
- setTooltipStyles(styles);
61
32
  setWithTooltip(true);
62
33
  }
63
34
  };
@@ -74,17 +45,14 @@ function TextWithDeferTooltip({
74
45
  );
75
46
 
76
47
  if (withTooltip) {
77
- const tooltipSide = overTrigger ? 'bottom' : side;
78
-
79
48
  return (
80
49
  <Tooltip open={withTooltip} onOpenChange={setWithTooltip}>
81
50
  <TooltipTrigger asChild>{textElement}</TooltipTrigger>
82
51
  <TooltipContent
83
- side={tooltipSide}
84
- style={{
85
- ...(maxWidth !== undefined && { maxWidth: `${maxWidth}px` }),
86
- ...tooltipStyles,
87
- }}
52
+ side={side}
53
+ overTrigger={overTrigger}
54
+ maxWidth={maxWidth}
55
+ style={width !== undefined ? { width: `${width}px` } : undefined}
88
56
  >
89
57
  {children}
90
58
  </TooltipContent>
@@ -57,6 +57,18 @@
57
57
  &[data-state="closed"][data-side="top"]
58
58
  animation fade-out 0.1s ease-in, zoom-out 0.1s ease-in
59
59
 
60
+ .tooltipContentOverTrigger
61
+ box-sizing border-box
62
+ height auto
63
+
64
+ &[data-state="open"],
65
+ &[data-state="instant-open"],
66
+ &[data-state="delayed-open"]
67
+ animation fade-in 0.15s ease-out
68
+
69
+ &[data-state="closed"]
70
+ animation fade-out 0.1s ease-in
71
+
60
72
  .tooltipArrow
61
73
  z-index 50
62
74
  width 10px
@@ -9,6 +9,7 @@ interface CssExports {
9
9
  'slide-in-from-top-2': string;
10
10
  'tooltipArrow': string;
11
11
  'tooltipContent': string;
12
+ 'tooltipContentOverTrigger': string;
12
13
  'zoom-in': string;
13
14
  'zoom-out': string;
14
15
  }