@principal-ai/subsystems-react 0.17.0 → 0.19.0

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 (108) hide show
  1. package/README.md +11 -85
  2. package/dist/index.d.ts +7 -44
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +6 -32
  5. package/dist/index.js.map +1 -1
  6. package/dist/subsystem/SubsystemCarousel.d.ts +36 -0
  7. package/dist/subsystem/SubsystemCarousel.d.ts.map +1 -0
  8. package/dist/subsystem/SubsystemCarousel.js +163 -0
  9. package/dist/subsystem/SubsystemCarousel.js.map +1 -0
  10. package/dist/subsystem/SubsystemComponentGraph.d.ts +25 -0
  11. package/dist/subsystem/SubsystemComponentGraph.d.ts.map +1 -1
  12. package/dist/subsystem/SubsystemComponentGraph.js +329 -68
  13. package/dist/subsystem/SubsystemComponentGraph.js.map +1 -1
  14. package/package.json +4 -4
  15. package/src/index.ts +9 -199
  16. package/src/subsystem/SubsystemComponentGraph.tsx +396 -50
  17. package/src/components/ConfigurationSelector.tsx +0 -133
  18. package/src/components/GraphRenderer.tsx +0 -3195
  19. package/src/components/MultiCanvasRenderer.tsx +0 -295
  20. package/src/components/NodeTooltip.tsx +0 -279
  21. package/src/components/PendingChanges.test.tsx +0 -375
  22. package/src/components/SelectionSidebar.tsx +0 -352
  23. package/src/components/SequenceDiagramRenderer.tsx +0 -1156
  24. package/src/components/WorkflowSequenceDiagram.tsx +0 -270
  25. package/src/components/dashboard/DashboardRenderer.tsx +0 -317
  26. package/src/components/dashboard/MetricPanel.tsx +0 -254
  27. package/src/components/dashboard/MockDataProvider.ts +0 -330
  28. package/src/components/dashboard/components/BarChart.tsx +0 -299
  29. package/src/components/dashboard/components/LineChart.tsx +0 -279
  30. package/src/components/dashboard/components/MetricCard.tsx +0 -270
  31. package/src/components/dashboard/components/SourceLink.tsx +0 -63
  32. package/src/components/dashboard/components/TimeRangeSelector.tsx +0 -280
  33. package/src/components/dashboard/components/index.ts +0 -5
  34. package/src/components/dashboard/index.ts +0 -47
  35. package/src/components/dashboard/types.ts +0 -126
  36. package/src/components/state-view/PipelineView.tsx +0 -347
  37. package/src/components/state-view/index.ts +0 -14
  38. package/src/components/state-view/types.ts +0 -261
  39. package/src/components/state-view/useStateView.ts +0 -205
  40. package/src/contexts/GraphEditContext.tsx +0 -27
  41. package/src/contexts/TooltipPortalContext.ts +0 -8
  42. package/src/edges/CustomEdge.tsx +0 -402
  43. package/src/edges/GenericEdge.tsx +0 -36
  44. package/src/hooks/usePathBasedEvents.ts +0 -192
  45. package/src/hooks/useSequenceLayout.ts +0 -543
  46. package/src/hooks/useUndoRedo.ts +0 -144
  47. package/src/nodes/CustomNode.tsx +0 -1231
  48. package/src/nodes/GenericNode.tsx +0 -55
  49. package/src/nodes/otel/OtelBoundaryNode.tsx +0 -224
  50. package/src/nodes/otel/OtelEventNode.tsx +0 -225
  51. package/src/nodes/otel/OtelResourceNode.tsx +0 -251
  52. package/src/nodes/otel/OtelScopeNode.tsx +0 -221
  53. package/src/nodes/otel/OtelSpanConventionNode.tsx +0 -295
  54. package/src/nodes/otel/index.ts +0 -23
  55. package/src/nodes/otel/shared/NodeBadges.tsx +0 -295
  56. package/src/nodes/otel/shared/NodeContent.tsx +0 -204
  57. package/src/nodes/otel/shared/index.ts +0 -8
  58. package/src/nodes/otel/shared/types.ts +0 -138
  59. package/src/nodes/otel/shared/useNodeBehavior.ts +0 -114
  60. package/src/stories/Audit/BoundaryNodes.stories.tsx +0 -385
  61. package/src/stories/Audit/CanvasEdgeTypes.stories.tsx +0 -1448
  62. package/src/stories/Audit/CanvasNodeTypes.stories.tsx +0 -1049
  63. package/src/stories/Audit/ColorPriority.stories.tsx +0 -895
  64. package/src/stories/Audit/DiamondBadges.stories.tsx +0 -629
  65. package/src/stories/Audit/NodeDimensionsTesting.stories.tsx +0 -724
  66. package/src/stories/Audit/NodeFieldsAudit.stories.tsx +0 -996
  67. package/src/stories/Audit/NodeFontSizeTesting.stories.tsx +0 -715
  68. package/src/stories/Audit/NodeShapes.stories.tsx +0 -1208
  69. package/src/stories/Audit/WideNodesTesting.stories.tsx +0 -1045
  70. package/src/stories/Components/DashboardRenderer.stories.tsx +0 -263
  71. package/src/stories/Components/GraphRenderer.stories.tsx +0 -3236
  72. package/src/stories/Components/MultiCanvasRenderer.stories.tsx +0 -370
  73. package/src/stories/Components/SequenceDiagram.stories.tsx +0 -255
  74. package/src/stories/Components/StateView.stories.tsx +0 -417
  75. package/src/stories/Components/TraceViewer.stories.tsx +0 -442
  76. package/src/stories/Components/WorkflowSequenceDiagram.stories.tsx +0 -802
  77. package/src/stories/Features/AnimationWorkshop.stories.tsx +0 -727
  78. package/src/stories/Features/ChangeTypeVisual.stories.tsx +0 -67
  79. package/src/stories/Features/EventDrivenAnimations.stories.tsx +0 -513
  80. package/src/stories/Features/MultiConfig.stories.tsx +0 -539
  81. package/src/stories/Layout/ElkEdgeRouting.stories.tsx +0 -539
  82. package/src/stories/Layout/GraphOrientation.stories.tsx +0 -500
  83. package/src/stories/Layout/MultiDirectionalConnections.stories.tsx +0 -352
  84. package/src/stories/Nodes/NodeDefinitionComparison.stories.tsx +0 -560
  85. package/src/stories/Nodes/NodeNameFallbacks.stories.tsx +0 -491
  86. package/src/stories/Nodes/NumericColors.stories.tsx +0 -440
  87. package/src/stories/OTEL/EventNameDisplay.stories.tsx +0 -694
  88. package/src/stories/OTEL/EventNameFallback.stories.tsx +0 -578
  89. package/src/stories/OTEL/OtelComponents.stories.tsx +0 -422
  90. package/src/stories/OTEL/OtelNodeTypesPrototype.stories.tsx +0 -806
  91. package/src/stories/OTEL/RawEventCards.stories.tsx +0 -57
  92. package/src/stories/OTEL/SpanBadges.stories.tsx +0 -840
  93. package/src/stories/OTEL/ValidationEventsCanvas.stories.tsx +0 -209
  94. package/src/stories/RealWorld/BacklogMdCanvases.stories.tsx +0 -721
  95. package/src/stories/RealWorld/FileCitySequence.stories.tsx +0 -516
  96. package/src/stories/RealWorld/RealCanvasFiles.stories.tsx +0 -336
  97. package/src/stories/__traces__/test-run.canvas.json +0 -241
  98. package/src/stories/data/activity-feed-analytics.dashboard.json +0 -300
  99. package/src/stories/data/file-city-images-transformed.otel.canvas.json +0 -295
  100. package/src/stories/data/file-city-workflows.json +0 -269
  101. package/src/stories/data/graph-converter-test-execution.json +0 -443
  102. package/src/stories/data/graph-converter-test.workflow.json +0 -105
  103. package/src/stories/data/graph-converter-validated-execution.json +0 -58
  104. package/src/utils/animationMapping.ts +0 -166
  105. package/src/utils/canvasBounds.ts +0 -146
  106. package/src/utils/graphConverter.ts +0 -148
  107. package/src/utils/iconResolver.tsx +0 -53
  108. package/src/utils/orientationUtils.ts +0 -73
@@ -1,254 +0,0 @@
1
- /**
2
- * MetricPanel
3
- *
4
- * Wrapper component that determines which visualization to render
5
- * based on the metric definition and data.
6
- */
7
-
8
- import React from 'react';
9
- import { useTheme } from '@principal-ade/industry-theme';
10
- import type {
11
- MetricPanelProps,
12
- MetricDefinition,
13
- MetricData,
14
- DisplayComponent,
15
- } from './types';
16
- import { MetricCard, LineChart, BarChart, SourceLink } from './components';
17
-
18
- export function MetricPanel({
19
- metric,
20
- data,
21
- onMetricClick,
22
- onSourceClick,
23
- }: MetricPanelProps) {
24
- const { theme } = useTheme();
25
-
26
- // Determine which component to use
27
- const componentType = getComponentType(metric, data);
28
-
29
- // Render loading state
30
- if (data.loading) {
31
- return (
32
- <div
33
- style={{
34
- backgroundColor: theme.colors.surface || theme.colors.background,
35
- border: `1px solid ${theme.colors.border}`,
36
- borderRadius: theme.radii?.[2] || 8,
37
- padding: theme.space?.[3] || 16,
38
- minHeight: 100,
39
- display: 'flex',
40
- alignItems: 'center',
41
- justifyContent: 'center',
42
- fontFamily: theme.fonts.body,
43
- }}
44
- >
45
- <span style={{ color: theme.colors.textSecondary, fontSize: theme.fontSizes[1] }}>
46
- Loading...
47
- </span>
48
- </div>
49
- );
50
- }
51
-
52
- // Render error state
53
- if (data.error) {
54
- return (
55
- <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
56
- <div
57
- style={{
58
- backgroundColor: theme.colors.surface || theme.colors.background,
59
- border: `1px solid ${theme.colors.error}`,
60
- borderRadius: theme.radii?.[2] || 8,
61
- padding: theme.space?.[3] || 16,
62
- fontFamily: theme.fonts.body,
63
- }}
64
- >
65
- <div
66
- style={{
67
- color: theme.colors.error,
68
- marginBottom: theme.space?.[2] || 8,
69
- fontSize: theme.fontSizes[1],
70
- fontWeight: theme.fontWeights.medium,
71
- }}
72
- >
73
- {metric.name}
74
- </div>
75
- <div style={{ color: theme.colors.textSecondary, fontSize: theme.fontSizes[0] }}>
76
- {data.error}
77
- </div>
78
- </div>
79
-
80
- {/* Source links - show even in error state for navigation to workflows */}
81
- {metric.sources && metric.sources.length > 0 && onSourceClick && (
82
- <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
83
- {metric.sources.map((source, i) => (
84
- <SourceLink key={i} source={source} onClick={onSourceClick} />
85
- ))}
86
- </div>
87
- )}
88
- </div>
89
- );
90
- }
91
-
92
- const handleClick = () => {
93
- onMetricClick?.(metric.id);
94
- };
95
-
96
- return (
97
- <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
98
- {/* The visualization component */}
99
- {renderComponent(componentType, metric, data, handleClick)}
100
-
101
- {/* Source links */}
102
- {metric.sources && metric.sources.length > 0 && onSourceClick && (
103
- <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
104
- {metric.sources.map((source, i) => (
105
- <SourceLink key={i} source={source} onClick={onSourceClick} />
106
- ))}
107
- </div>
108
- )}
109
- </div>
110
- );
111
- }
112
-
113
- /**
114
- * Determine which component to render based on metric definition and data
115
- */
116
- function getComponentType(metric: MetricDefinition, data: MetricData): DisplayComponent {
117
- // Explicit display hint takes precedence
118
- if (metric.display?.component) {
119
- return metric.display.component;
120
- }
121
-
122
- const { type, query } = metric;
123
-
124
- // Time series data → chart
125
- if (data.series && data.series.length > 0) {
126
- if (query.groupBy && query.groupBy.length > 0) {
127
- return 'StackedBarChart';
128
- }
129
- return 'LineChart';
130
- }
131
-
132
- // Histogram data
133
- if (data.histogram) {
134
- return 'Histogram';
135
- }
136
-
137
- // Counter with time grouping → line/bar chart (but no series data yet)
138
- if (type === 'counter' && query.timeGroup) {
139
- return query.groupBy?.length ? 'BarChart' : 'LineChart';
140
- }
141
-
142
- // Gauge (percentage) → gauge or metric card
143
- if (type === 'gauge') {
144
- return metric.thresholds ? 'GaugeChart' : 'MetricCard';
145
- }
146
-
147
- // Default to metric card
148
- return 'MetricCard';
149
- }
150
-
151
- /**
152
- * Render the appropriate component
153
- */
154
- function renderComponent(
155
- componentType: DisplayComponent,
156
- metric: MetricDefinition,
157
- data: MetricData,
158
- onClick?: () => void
159
- ): React.ReactNode {
160
- switch (componentType) {
161
- case 'MetricCard':
162
- case 'GaugeChart': // Use MetricCard for now, can add GaugeChart later
163
- return (
164
- <MetricCard
165
- title={metric.name}
166
- value={data.current ?? 0}
167
- unit={metric.unit}
168
- description={metric.description}
169
- trend={data.trend}
170
- changePercent={data.changePercent}
171
- showTrend={metric.display?.showTrend ?? true}
172
- showSparkline={metric.display?.showSparkline}
173
- sparklineData={data.series}
174
- thresholds={metric.thresholds}
175
- size={metric.display?.size || 'medium'}
176
- onClick={onClick}
177
- />
178
- );
179
-
180
- case 'LineChart':
181
- return (
182
- <LineChart
183
- title={metric.name}
184
- data={data.series || []}
185
- xKey="date"
186
- yKey="value"
187
- unit={metric.unit}
188
- onClick={onClick}
189
- />
190
- );
191
-
192
- case 'BarChart':
193
- case 'StackedBarChart':
194
- // Extract series keys from data
195
- const series = data.series && data.series.length > 0
196
- ? Object.keys(data.series[0]).filter(k => k !== 'date')
197
- : metric.query.groupBy || [];
198
-
199
- return (
200
- <BarChart
201
- title={metric.name}
202
- data={data.series || []}
203
- xKey="date"
204
- series={series}
205
- stacked={componentType === 'StackedBarChart'}
206
- unit={metric.unit}
207
- onClick={onClick}
208
- />
209
- );
210
-
211
- case 'Histogram':
212
- // TODO: Implement Histogram component
213
- return (
214
- <MetricCard
215
- title={metric.name}
216
- value="Histogram (TODO)"
217
- description={metric.description}
218
- onClick={onClick}
219
- />
220
- );
221
-
222
- case 'PieChart':
223
- // TODO: Implement PieChart component
224
- return (
225
- <MetricCard
226
- title={metric.name}
227
- value="PieChart (TODO)"
228
- description={metric.description}
229
- onClick={onClick}
230
- />
231
- );
232
-
233
- case 'DataTable':
234
- // TODO: Implement DataTable component
235
- return (
236
- <MetricCard
237
- title={metric.name}
238
- value="DataTable (TODO)"
239
- description={metric.description}
240
- onClick={onClick}
241
- />
242
- );
243
-
244
- default:
245
- return (
246
- <MetricCard
247
- title={metric.name}
248
- value={data.current ?? 0}
249
- unit={metric.unit}
250
- onClick={onClick}
251
- />
252
- );
253
- }
254
- }
@@ -1,330 +0,0 @@
1
- /**
2
- * MockDataProvider
3
- *
4
- * Provides mock data for dashboard metrics during prototyping.
5
- * Can use inline _mockData from the dashboard definition or generate realistic data.
6
- */
7
-
8
- import type {
9
- DashboardDefinition,
10
- MetricDefinition,
11
- MetricData,
12
- DataProvider,
13
- TimeSeriesPoint,
14
- } from './types';
15
-
16
- export class MockDataProvider implements DataProvider {
17
- private data: Record<string, MetricData> = {};
18
-
19
- constructor(dashboard: DashboardDefinition) {
20
- for (const metric of dashboard.metrics) {
21
- this.data[metric.id] = this.resolveData(metric);
22
- }
23
- }
24
-
25
- get(metricId: string): MetricData {
26
- return this.data[metricId] || { error: `No data for metric: ${metricId}` };
27
- }
28
-
29
- getAll(): Record<string, MetricData> {
30
- return this.data;
31
- }
32
-
33
- private resolveData(metric: MetricDefinition): MetricData {
34
- // Use inline mock data if provided
35
- if (metric._mockData) {
36
- return this.transformMockData(metric._mockData, metric);
37
- }
38
-
39
- // Generate data based on metric type and query
40
- return this.generateData(metric);
41
- }
42
-
43
- private transformMockData(
44
- mockData: NonNullable<MetricDefinition['_mockData']>,
45
- _metric: MetricDefinition
46
- ): MetricData {
47
- const result: MetricData = {};
48
-
49
- if (mockData.current !== undefined) {
50
- result.current = mockData.current;
51
- }
52
-
53
- if (mockData.previous !== undefined) {
54
- result.previous = mockData.previous;
55
- // Calculate change percent if not provided
56
- if (result.current !== undefined) {
57
- result.changePercent =
58
- ((result.current - mockData.previous) / mockData.previous) * 100;
59
- result.trend =
60
- result.changePercent > 0
61
- ? 'up'
62
- : result.changePercent < 0
63
- ? 'down'
64
- : 'flat';
65
- }
66
- }
67
-
68
- if (mockData.trend) {
69
- result.trend = mockData.trend;
70
- }
71
-
72
- if (mockData.series) {
73
- result.series = mockData.series;
74
- }
75
-
76
- if (mockData.breakdown) {
77
- result.breakdown = mockData.breakdown;
78
- }
79
-
80
- if (mockData.histogram) {
81
- result.histogram = mockData.histogram;
82
- }
83
-
84
- return result;
85
- }
86
-
87
- private generateData(metric: MetricDefinition): MetricData {
88
- const { type, query } = metric;
89
-
90
- // Time series data
91
- if (query.timeGroup) {
92
- return this.generateTimeSeries(metric);
93
- }
94
-
95
- // Single value
96
- if (type === 'gauge') {
97
- return this.generateGaugeData(metric);
98
- }
99
-
100
- if (type === 'counter') {
101
- return this.generateCounterData(metric);
102
- }
103
-
104
- if (type === 'histogram') {
105
- return this.generateHistogramData(metric);
106
- }
107
-
108
- // Fallback
109
- return {
110
- current: Math.floor(Math.random() * 1000),
111
- trend: 'flat',
112
- };
113
- }
114
-
115
- private generateTimeSeries(metric: MetricDefinition): MetricData {
116
- const { query } = metric;
117
- const points = this.getPointCount(query.timeGroup);
118
- const series: TimeSeriesPoint[] = [];
119
-
120
- const baseValue = this.getBaseValue(metric);
121
- const variance = 0.2;
122
-
123
- // Generate dates
124
- const dates = this.generateDates(query.timeGroup, points);
125
-
126
- if (query.groupBy && query.groupBy.length > 0) {
127
- // Grouped data (e.g., by isMobile)
128
- const groups = this.getGroupValues(query.groupBy[0]);
129
-
130
- for (let i = 0; i < points; i++) {
131
- const point: TimeSeriesPoint = { date: dates[i] };
132
- for (const group of groups) {
133
- point[group] = Math.floor(
134
- baseValue * (0.3 + Math.random() * 0.7) * (1 + (Math.random() - 0.5) * variance)
135
- );
136
- }
137
- series.push(point);
138
- }
139
- } else {
140
- // Simple time series
141
- for (let i = 0; i < points; i++) {
142
- series.push({
143
- date: dates[i],
144
- value: Math.floor(baseValue * (1 + (Math.random() - 0.5) * variance)),
145
- });
146
- }
147
- }
148
-
149
- return { series };
150
- }
151
-
152
- private generateGaugeData(metric: MetricDefinition): MetricData {
153
- const { query } = metric;
154
-
155
- // Percentage derivations
156
- if (
157
- query.derivation === 'percentage' ||
158
- query.derivation === 'error_rate' ||
159
- query.derivation === 'success_rate'
160
- ) {
161
- const current = Math.random() * 100;
162
- const previous = Math.random() * 100;
163
- return {
164
- current: Math.round(current * 10) / 10,
165
- previous: Math.round(previous * 10) / 10,
166
- trend: current > previous ? 'up' : current < previous ? 'down' : 'flat',
167
- changePercent: Math.round(((current - previous) / previous) * 100 * 10) / 10,
168
- };
169
- }
170
-
171
- // Rate
172
- if (query.derivation === 'rate') {
173
- const current = Math.floor(Math.random() * 1000);
174
- const previous = Math.floor(Math.random() * 1000);
175
- return {
176
- current,
177
- previous,
178
- trend: current > previous ? 'up' : current < previous ? 'down' : 'flat',
179
- changePercent: Math.round(((current - previous) / previous) * 100),
180
- };
181
- }
182
-
183
- const current = Math.floor(Math.random() * 10000);
184
- return { current, trend: 'flat' };
185
- }
186
-
187
- private generateCounterData(_metric: MetricDefinition): MetricData {
188
- const current = Math.floor(Math.random() * 100000);
189
- const previous = Math.floor(Math.random() * 100000);
190
- return {
191
- current,
192
- previous,
193
- trend: current > previous ? 'up' : current < previous ? 'down' : 'flat',
194
- changePercent: Math.round(((current - previous) / previous) * 100),
195
- };
196
- }
197
-
198
- private generateHistogramData(metric: MetricDefinition): MetricData {
199
- const { query } = metric;
200
-
201
- // Percentile value
202
- if (query.percentile) {
203
- const values = [50, 120, 200, 350, 500, 800, 1200];
204
- const percentileIndex = Math.min(
205
- Math.floor((query.percentile / 100) * values.length),
206
- values.length - 1
207
- );
208
- const current = values[percentileIndex] + Math.floor(Math.random() * 100);
209
- const previous = values[percentileIndex] + Math.floor(Math.random() * 100);
210
-
211
- return {
212
- current,
213
- previous,
214
- trend: current > previous ? 'up' : current < previous ? 'down' : 'flat',
215
- changePercent: Math.round(((current - previous) / previous) * 100),
216
- };
217
- }
218
-
219
- // Full histogram
220
- return {
221
- histogram: {
222
- buckets: ['0-50ms', '50-100ms', '100-200ms', '200-500ms', '500ms-1s', '>1s'],
223
- counts: [
224
- Math.floor(Math.random() * 1000),
225
- Math.floor(Math.random() * 800),
226
- Math.floor(Math.random() * 400),
227
- Math.floor(Math.random() * 200),
228
- Math.floor(Math.random() * 100),
229
- Math.floor(Math.random() * 50),
230
- ],
231
- },
232
- };
233
- }
234
-
235
- private getPointCount(timeGroup?: string): number {
236
- switch (timeGroup) {
237
- case 'minute':
238
- return 60;
239
- case 'hour':
240
- return 24;
241
- case 'day':
242
- return 7;
243
- case 'week':
244
- return 12;
245
- case 'month':
246
- return 12;
247
- default:
248
- return 7;
249
- }
250
- }
251
-
252
- private getBaseValue(metric: MetricDefinition): number {
253
- // Estimate reasonable base value based on metric type
254
- if (metric.query.derivation === 'percentage' || metric.query.derivation === 'error_rate') {
255
- return 50;
256
- }
257
- if (metric.unit === 'views' || metric.unit === 'count') {
258
- return 10000;
259
- }
260
- if (metric.unit === 'milliseconds' || metric.unit === 'ms') {
261
- return 200;
262
- }
263
- return 1000;
264
- }
265
-
266
- private generateDates(timeGroup: string | undefined, count: number): string[] {
267
- const dates: string[] = [];
268
- const now = new Date();
269
-
270
- for (let i = count - 1; i >= 0; i--) {
271
- const date = new Date(now);
272
-
273
- switch (timeGroup) {
274
- case 'minute':
275
- date.setMinutes(date.getMinutes() - i);
276
- dates.push(date.toISOString().slice(11, 16)); // HH:MM
277
- break;
278
- case 'hour':
279
- date.setHours(date.getHours() - i);
280
- dates.push(date.toISOString().slice(11, 13) + ':00'); // HH:00
281
- break;
282
- case 'day':
283
- date.setDate(date.getDate() - i);
284
- dates.push(date.toISOString().slice(0, 10)); // YYYY-MM-DD
285
- break;
286
- case 'week':
287
- date.setDate(date.getDate() - i * 7);
288
- dates.push(`W${this.getWeekNumber(date)}`);
289
- break;
290
- case 'month':
291
- date.setMonth(date.getMonth() - i);
292
- dates.push(date.toISOString().slice(0, 7)); // YYYY-MM
293
- break;
294
- default:
295
- date.setDate(date.getDate() - i);
296
- dates.push(date.toISOString().slice(0, 10));
297
- }
298
- }
299
-
300
- return dates;
301
- }
302
-
303
- private getWeekNumber(date: Date): number {
304
- const firstDayOfYear = new Date(date.getFullYear(), 0, 1);
305
- const pastDaysOfYear = (date.getTime() - firstDayOfYear.getTime()) / 86400000;
306
- return Math.ceil((pastDaysOfYear + firstDayOfYear.getDay() + 1) / 7);
307
- }
308
-
309
- private getGroupValues(groupBy: string): string[] {
310
- // Common group values based on attribute name
311
- if (groupBy === 'isMobile') {
312
- return ['mobile', 'desktop'];
313
- }
314
- if (groupBy === 'status' || groupBy === 'status.code') {
315
- return ['success', 'error'];
316
- }
317
- if (groupBy === 'user.tier') {
318
- return ['free', 'pro', 'enterprise'];
319
- }
320
- // Default generic groups
321
- return ['group_a', 'group_b'];
322
- }
323
- }
324
-
325
- /**
326
- * Create a mock data provider from a dashboard definition
327
- */
328
- export function createMockDataProvider(dashboard: DashboardDefinition): DataProvider {
329
- return new MockDataProvider(dashboard);
330
- }