@contractspec/example.analytics-dashboard 3.7.5 → 3.8.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 (65) hide show
  1. package/README.md +64 -271
  2. package/dist/browser/dashboard.feature.js +592 -0
  3. package/dist/browser/events.js +1 -1
  4. package/dist/browser/index.js +1171 -627
  5. package/dist/browser/ui/AnalyticsDashboard.js +826 -194
  6. package/dist/browser/ui/AnalyticsDashboard.widgets.js +94 -0
  7. package/dist/browser/ui/AnalyticsQueriesTable.js +99 -0
  8. package/dist/browser/ui/hooks/index.js +594 -3
  9. package/dist/browser/ui/hooks/useAnalyticsData.js +594 -3
  10. package/dist/browser/ui/index.js +964 -440
  11. package/dist/browser/ui/renderers/analytics.markdown.js +620 -138
  12. package/dist/browser/ui/renderers/index.js +620 -138
  13. package/dist/browser/visualizations/catalog.js +457 -0
  14. package/dist/browser/visualizations/index.js +611 -0
  15. package/dist/browser/visualizations/specs.breakdown.js +140 -0
  16. package/dist/browser/visualizations/specs.performance.js +198 -0
  17. package/dist/browser/visualizations/widgets.js +595 -0
  18. package/dist/dashboard/index.d.ts +3 -3
  19. package/dist/dashboard.feature.js +592 -0
  20. package/dist/events.js +1 -1
  21. package/dist/index.d.ts +4 -3
  22. package/dist/index.js +1171 -627
  23. package/dist/node/dashboard.feature.js +592 -0
  24. package/dist/node/events.js +1 -1
  25. package/dist/node/index.js +1171 -627
  26. package/dist/node/ui/AnalyticsDashboard.js +826 -194
  27. package/dist/node/ui/AnalyticsDashboard.widgets.js +94 -0
  28. package/dist/node/ui/AnalyticsQueriesTable.js +99 -0
  29. package/dist/node/ui/hooks/index.js +594 -3
  30. package/dist/node/ui/hooks/useAnalyticsData.js +594 -3
  31. package/dist/node/ui/index.js +964 -440
  32. package/dist/node/ui/renderers/analytics.markdown.js +620 -138
  33. package/dist/node/ui/renderers/index.js +620 -138
  34. package/dist/node/visualizations/catalog.js +457 -0
  35. package/dist/node/visualizations/index.js +611 -0
  36. package/dist/node/visualizations/specs.breakdown.js +140 -0
  37. package/dist/node/visualizations/specs.performance.js +198 -0
  38. package/dist/node/visualizations/widgets.js +595 -0
  39. package/dist/query/index.d.ts +1 -1
  40. package/dist/query-engine/index.d.ts +1 -1
  41. package/dist/ui/AnalyticsDashboard.js +826 -194
  42. package/dist/ui/AnalyticsDashboard.widgets.d.ts +5 -0
  43. package/dist/ui/AnalyticsDashboard.widgets.js +95 -0
  44. package/dist/ui/AnalyticsQueriesTable.d.ts +4 -0
  45. package/dist/ui/AnalyticsQueriesTable.js +100 -0
  46. package/dist/ui/hooks/index.d.ts +1 -1
  47. package/dist/ui/hooks/index.js +594 -3
  48. package/dist/ui/hooks/useAnalyticsData.js +594 -3
  49. package/dist/ui/index.d.ts +1 -1
  50. package/dist/ui/index.js +964 -440
  51. package/dist/ui/renderers/analytics.markdown.d.ts +0 -9
  52. package/dist/ui/renderers/analytics.markdown.js +620 -138
  53. package/dist/ui/renderers/index.js +620 -138
  54. package/dist/visualizations/catalog.d.ts +9 -0
  55. package/dist/visualizations/catalog.js +458 -0
  56. package/dist/visualizations/index.d.ts +4 -0
  57. package/dist/visualizations/index.js +612 -0
  58. package/dist/visualizations/specs.breakdown.d.ts +4 -0
  59. package/dist/visualizations/specs.breakdown.js +141 -0
  60. package/dist/visualizations/specs.performance.d.ts +5 -0
  61. package/dist/visualizations/specs.performance.js +199 -0
  62. package/dist/visualizations/widgets.d.ts +24 -0
  63. package/dist/visualizations/widgets.js +596 -0
  64. package/dist/visualizations/widgets.test.d.ts +1 -0
  65. package/package.json +109 -11
package/dist/ui/index.js CHANGED
@@ -1,277 +1,787 @@
1
1
  // @bun
2
- // src/ui/renderers/analytics.markdown.ts
3
- var mockDashboards = [
4
- {
5
- id: "dash-1",
6
- name: "Sales Overview",
7
- slug: "sales-overview",
8
- status: "PUBLISHED",
9
- widgetCount: 8,
10
- viewCount: 1250,
11
- lastViewedAt: "2024-01-16T12:00:00Z"
2
+ // src/visualizations/specs.breakdown.ts
3
+ import { defineVisualization } from "@contractspec/lib.contracts-spec/visualizations";
4
+ var QUERY_REF = { key: "analytics.query.execute", version: "1.0.0" };
5
+ var META = {
6
+ version: "1.0.0",
7
+ domain: "analytics",
8
+ stability: "experimental",
9
+ owners: ["@example.analytics-dashboard"],
10
+ tags: ["analytics", "dashboard", "visualization"]
11
+ };
12
+ var ChannelMixVisualization = defineVisualization({
13
+ meta: {
14
+ ...META,
15
+ key: "analytics.visualization.channel-mix",
16
+ title: "Channel Mix",
17
+ description: "Session distribution across acquisition channels.",
18
+ goal: "Explain which channels currently drive the largest share of traffic.",
19
+ context: "Marketing attribution dashboard."
12
20
  },
13
- {
14
- id: "dash-2",
15
- name: "User Engagement",
16
- slug: "user-engagement",
17
- status: "PUBLISHED",
18
- widgetCount: 6,
19
- viewCount: 890,
20
- lastViewedAt: "2024-01-16T10:00:00Z"
21
+ source: { primary: QUERY_REF, resultPath: "data" },
22
+ visualization: {
23
+ kind: "pie",
24
+ nameDimension: "channel",
25
+ valueMeasure: "sessions",
26
+ dimensions: [
27
+ {
28
+ key: "channel",
29
+ label: "Channel",
30
+ dataPath: "channel",
31
+ type: "category"
32
+ }
33
+ ],
34
+ measures: [{ key: "sessions", label: "Sessions", dataPath: "sessions" }],
35
+ table: { caption: "Sessions by acquisition channel." }
36
+ }
37
+ });
38
+ var EngagementHeatmapVisualization = defineVisualization({
39
+ meta: {
40
+ ...META,
41
+ key: "analytics.visualization.engagement-heatmap",
42
+ title: "Engagement Heatmap",
43
+ description: "Average engagement score by weekday and time band.",
44
+ goal: "Reveal the highest-engagement time windows for product activity.",
45
+ context: "Usage analytics dashboard."
21
46
  },
22
- {
23
- id: "dash-3",
24
- name: "Product Analytics",
25
- slug: "product-analytics",
26
- status: "PUBLISHED",
27
- widgetCount: 10,
28
- viewCount: 560,
29
- lastViewedAt: "2024-01-15T14:00:00Z"
47
+ source: { primary: QUERY_REF, resultPath: "data" },
48
+ visualization: {
49
+ kind: "heatmap",
50
+ xDimension: "timeBand",
51
+ yDimension: "weekday",
52
+ valueMeasure: "engagementScore",
53
+ dimensions: [
54
+ {
55
+ key: "timeBand",
56
+ label: "Time Band",
57
+ dataPath: "timeBand",
58
+ type: "category"
59
+ },
60
+ {
61
+ key: "weekday",
62
+ label: "Weekday",
63
+ dataPath: "weekday",
64
+ type: "category"
65
+ }
66
+ ],
67
+ measures: [
68
+ {
69
+ key: "engagementScore",
70
+ label: "Engagement",
71
+ dataPath: "engagementScore"
72
+ }
73
+ ],
74
+ table: { caption: "Engagement score by weekday and time band." }
75
+ }
76
+ });
77
+ var ConversionFunnelVisualization = defineVisualization({
78
+ meta: {
79
+ ...META,
80
+ key: "analytics.visualization.conversion-funnel",
81
+ title: "Conversion Funnel",
82
+ description: "Progression through the main acquisition funnel.",
83
+ goal: "Show where the product is losing the largest share of prospects.",
84
+ context: "Growth dashboard."
30
85
  },
31
- {
32
- id: "dash-4",
33
- name: "Finance Report",
34
- slug: "finance-report",
35
- status: "DRAFT",
36
- widgetCount: 4,
37
- viewCount: 0,
38
- lastViewedAt: null
86
+ source: { primary: QUERY_REF, resultPath: "data" },
87
+ visualization: {
88
+ kind: "funnel",
89
+ nameDimension: "stage",
90
+ valueMeasure: "users",
91
+ sort: "descending",
92
+ dimensions: [
93
+ { key: "stage", label: "Stage", dataPath: "stage", type: "category" }
94
+ ],
95
+ measures: [{ key: "users", label: "Users", dataPath: "users" }],
96
+ table: { caption: "Users per conversion stage." }
39
97
  }
40
- ];
41
- var mockWidgets = [
42
- {
43
- id: "w-1",
44
- dashboardId: "dash-1",
45
- name: "Total Revenue",
46
- type: "METRIC",
47
- value: 125000,
48
- change: 12.5
98
+ });
99
+ var AccountCoverageGeoVisualization = defineVisualization({
100
+ meta: {
101
+ ...META,
102
+ key: "analytics.visualization.account-coverage-geo",
103
+ title: "Account Coverage",
104
+ description: "High-value accounts plotted on a slippy-map surface.",
105
+ goal: "Locate where active commercial concentration is strongest.",
106
+ context: "Territory coverage dashboard."
49
107
  },
50
- {
51
- id: "w-2",
52
- dashboardId: "dash-1",
53
- name: "Active Users",
54
- type: "METRIC",
55
- value: 4500,
56
- change: 8.2
108
+ source: { primary: QUERY_REF, resultPath: "data" },
109
+ visualization: {
110
+ kind: "geo",
111
+ mode: "slippy-map",
112
+ variant: "scatter",
113
+ nameDimension: "city",
114
+ latitudeDimension: "latitude",
115
+ longitudeDimension: "longitude",
116
+ valueMeasure: "accounts",
117
+ dimensions: [
118
+ { key: "city", label: "City", dataPath: "city", type: "category" },
119
+ {
120
+ key: "latitude",
121
+ label: "Latitude",
122
+ dataPath: "latitude",
123
+ type: "latitude"
124
+ },
125
+ {
126
+ key: "longitude",
127
+ label: "Longitude",
128
+ dataPath: "longitude",
129
+ type: "longitude"
130
+ }
131
+ ],
132
+ measures: [{ key: "accounts", label: "Accounts", dataPath: "accounts" }],
133
+ table: { caption: "Account concentration by city." }
134
+ }
135
+ });
136
+
137
+ // src/visualizations/specs.performance.ts
138
+ import { defineVisualization as defineVisualization2 } from "@contractspec/lib.contracts-spec/visualizations";
139
+ var QUERY_REF2 = { key: "analytics.query.execute", version: "1.0.0" };
140
+ var META2 = {
141
+ version: "1.0.0",
142
+ domain: "analytics",
143
+ stability: "experimental",
144
+ owners: ["@example.analytics-dashboard"],
145
+ tags: ["analytics", "dashboard", "visualization"]
146
+ };
147
+ var RevenueMetricVisualization = defineVisualization2({
148
+ meta: {
149
+ ...META2,
150
+ key: "analytics.visualization.revenue-metric",
151
+ title: "Revenue Snapshot",
152
+ description: "Current recurring revenue with prior-period comparison.",
153
+ goal: "Highlight the headline commercial metric for the dashboard.",
154
+ context: "Executive revenue overview."
57
155
  },
58
- {
59
- id: "w-3",
60
- dashboardId: "dash-1",
61
- name: "Revenue Trend",
62
- type: "LINE_CHART",
63
- dataPoints: 30
156
+ source: { primary: QUERY_REF2, resultPath: "data" },
157
+ visualization: {
158
+ kind: "metric",
159
+ measure: "totalRevenue",
160
+ comparisonMeasure: "priorRevenue",
161
+ dimensions: [
162
+ { key: "period", label: "Period", dataPath: "period", type: "time" }
163
+ ],
164
+ measures: [
165
+ {
166
+ key: "totalRevenue",
167
+ label: "Revenue",
168
+ dataPath: "totalRevenue",
169
+ format: "currency"
170
+ },
171
+ {
172
+ key: "priorRevenue",
173
+ label: "Prior Revenue",
174
+ dataPath: "priorRevenue",
175
+ format: "currency"
176
+ }
177
+ ],
178
+ sparkline: { dimension: "period", measure: "totalRevenue" },
179
+ table: { caption: "Revenue trend and prior period values." }
180
+ }
181
+ });
182
+ var RevenueTrendVisualization = defineVisualization2({
183
+ meta: {
184
+ ...META2,
185
+ key: "analytics.visualization.revenue-trend",
186
+ title: "Revenue Trend",
187
+ description: "Monthly revenue progression for the current quarter.",
188
+ goal: "Track whether revenue growth is accelerating or stalling.",
189
+ context: "Quarterly commercial dashboard."
64
190
  },
65
- {
66
- id: "w-4",
67
- dashboardId: "dash-1",
68
- name: "Top Products",
69
- type: "BAR_CHART",
70
- dataPoints: 10
191
+ source: { primary: QUERY_REF2, resultPath: "data" },
192
+ visualization: {
193
+ kind: "cartesian",
194
+ variant: "line",
195
+ xDimension: "date",
196
+ yMeasures: ["revenue"],
197
+ dimensions: [
198
+ { key: "date", label: "Month", dataPath: "date", type: "time" }
199
+ ],
200
+ measures: [
201
+ {
202
+ key: "revenue",
203
+ label: "Revenue",
204
+ dataPath: "revenue",
205
+ format: "currency",
206
+ color: "#0f766e"
207
+ }
208
+ ],
209
+ thresholds: [
210
+ { key: "target", value: 140000, label: "Target", color: "#dc2626" }
211
+ ],
212
+ table: { caption: "Monthly revenue values." }
213
+ }
214
+ });
215
+ var RegionalRevenueVisualization = defineVisualization2({
216
+ meta: {
217
+ ...META2,
218
+ key: "analytics.visualization.regional-revenue",
219
+ title: "Regional Revenue",
220
+ description: "Revenue split by sales territory.",
221
+ goal: "Compare the strongest and weakest performing territories.",
222
+ context: "Territory planning and commercial comparison."
71
223
  },
72
- {
73
- id: "w-5",
74
- dashboardId: "dash-2",
75
- name: "Daily Active Users",
76
- type: "LINE_CHART",
77
- dataPoints: 30
224
+ source: { primary: QUERY_REF2, resultPath: "data" },
225
+ visualization: {
226
+ kind: "cartesian",
227
+ variant: "bar",
228
+ xDimension: "region",
229
+ yMeasures: ["revenue"],
230
+ dimensions: [
231
+ { key: "region", label: "Region", dataPath: "region", type: "category" }
232
+ ],
233
+ measures: [
234
+ {
235
+ key: "revenue",
236
+ label: "Revenue",
237
+ dataPath: "revenue",
238
+ format: "currency",
239
+ color: "#1d4ed8"
240
+ }
241
+ ],
242
+ table: { caption: "Revenue by region." }
243
+ }
244
+ });
245
+ var RetentionAreaVisualization = defineVisualization2({
246
+ meta: {
247
+ ...META2,
248
+ key: "analytics.visualization.retention-area",
249
+ title: "Retention Curve",
250
+ description: "Weekly retention progression across the active cohort.",
251
+ goal: "Show whether user retention remains above the desired floor.",
252
+ context: "Product health dashboard."
78
253
  },
79
- {
80
- id: "w-6",
81
- dashboardId: "dash-2",
82
- name: "Session Duration",
83
- type: "METRIC",
84
- value: 245,
85
- change: -3.1
254
+ source: { primary: QUERY_REF2, resultPath: "data" },
255
+ visualization: {
256
+ kind: "cartesian",
257
+ variant: "area",
258
+ xDimension: "week",
259
+ yMeasures: ["retentionRate"],
260
+ dimensions: [
261
+ { key: "week", label: "Week", dataPath: "week", type: "category" }
262
+ ],
263
+ measures: [
264
+ {
265
+ key: "retentionRate",
266
+ label: "Retention",
267
+ dataPath: "retentionRate",
268
+ format: "percentage",
269
+ color: "#16a34a"
270
+ }
271
+ ],
272
+ thresholds: [
273
+ { key: "floor", value: 0.5, label: "Floor", color: "#f59e0b" }
274
+ ],
275
+ table: { caption: "Weekly retention rate." }
86
276
  }
277
+ });
278
+ var PipelineScatterVisualization = defineVisualization2({
279
+ meta: {
280
+ ...META2,
281
+ key: "analytics.visualization.pipeline-scatter",
282
+ title: "Pipeline Velocity",
283
+ description: "Deal-cycle length against win rate for active accounts.",
284
+ goal: "Spot outliers where the sales cycle is long but conversion remains weak.",
285
+ context: "Commercial operations dashboard."
286
+ },
287
+ source: { primary: QUERY_REF2, resultPath: "data" },
288
+ visualization: {
289
+ kind: "cartesian",
290
+ variant: "scatter",
291
+ xDimension: "cycleDays",
292
+ yMeasures: ["winRate"],
293
+ dimensions: [
294
+ {
295
+ key: "cycleDays",
296
+ label: "Cycle Days",
297
+ dataPath: "cycleDays",
298
+ type: "number"
299
+ }
300
+ ],
301
+ measures: [
302
+ {
303
+ key: "winRate",
304
+ label: "Win Rate",
305
+ dataPath: "winRate",
306
+ format: "percentage",
307
+ color: "#7c3aed"
308
+ },
309
+ {
310
+ key: "arr",
311
+ label: "ARR",
312
+ dataPath: "arr",
313
+ format: "currency"
314
+ }
315
+ ],
316
+ series: [
317
+ {
318
+ key: "pipeline",
319
+ label: "Accounts",
320
+ measure: "winRate",
321
+ type: "scatter",
322
+ color: "#7c3aed"
323
+ }
324
+ ],
325
+ table: { caption: "Sales cycle and win rate per account." }
326
+ }
327
+ });
328
+
329
+ // src/visualizations/catalog.ts
330
+ import { VisualizationRegistry } from "@contractspec/lib.contracts-spec/visualizations";
331
+ var AnalyticsVisualizationSpecs = [
332
+ RevenueMetricVisualization,
333
+ RevenueTrendVisualization,
334
+ RegionalRevenueVisualization,
335
+ RetentionAreaVisualization,
336
+ PipelineScatterVisualization,
337
+ ChannelMixVisualization,
338
+ EngagementHeatmapVisualization,
339
+ ConversionFunnelVisualization,
340
+ AccountCoverageGeoVisualization
87
341
  ];
88
- var mockQueries = [
89
- {
90
- id: "q-1",
91
- name: "Monthly Revenue",
92
- type: "AGGREGATION",
93
- isShared: true,
94
- executionCount: 1500
342
+ var AnalyticsVisualizationRegistry = new VisualizationRegistry([
343
+ ...AnalyticsVisualizationSpecs
344
+ ]);
345
+ var AnalyticsVisualizationRefs = AnalyticsVisualizationSpecs.map((spec) => refOf(spec));
346
+ var AnalyticsVisualizationSpecMap = new Map(AnalyticsVisualizationSpecs.map((spec) => [
347
+ visualizationRefKey(spec.meta),
348
+ spec
349
+ ]));
350
+ var AnalyticsVisualizationSampleData = {
351
+ [visualizationRefKey(RevenueMetricVisualization.meta)]: {
352
+ data: [
353
+ { period: "2025-11-01", totalRevenue: 112000, priorRevenue: 103000 },
354
+ { period: "2025-12-01", totalRevenue: 119000, priorRevenue: 110000 },
355
+ { period: "2026-01-01", totalRevenue: 126500, priorRevenue: 116000 },
356
+ { period: "2026-02-01", totalRevenue: 133000, priorRevenue: 124000 },
357
+ { period: "2026-03-01", totalRevenue: 145500, priorRevenue: 133000 }
358
+ ]
95
359
  },
96
- {
97
- id: "q-2",
98
- name: "User Growth",
99
- type: "METRIC",
100
- isShared: true,
101
- executionCount: 890
360
+ [visualizationRefKey(RevenueTrendVisualization.meta)]: {
361
+ data: [
362
+ { date: "2025-11-01", revenue: 112000 },
363
+ { date: "2025-12-01", revenue: 119000 },
364
+ { date: "2026-01-01", revenue: 126500 },
365
+ { date: "2026-02-01", revenue: 133000 },
366
+ { date: "2026-03-01", revenue: 145500 }
367
+ ]
102
368
  },
103
- {
104
- id: "q-3",
105
- name: "Product Sales",
106
- type: "SQL",
107
- isShared: false,
108
- executionCount: 340
369
+ [visualizationRefKey(RegionalRevenueVisualization.meta)]: {
370
+ data: [
371
+ { region: "North America", revenue: 210000 },
372
+ { region: "EMEA", revenue: 174000 },
373
+ { region: "APAC", revenue: 132000 },
374
+ { region: "LATAM", revenue: 88000 }
375
+ ]
109
376
  },
110
- {
111
- id: "q-4",
112
- name: "Conversion Funnel",
113
- type: "AGGREGATION",
114
- isShared: true,
115
- executionCount: 450
116
- }
117
- ];
118
- function formatNumber(value) {
119
- if (value >= 1e6) {
120
- return `${(value / 1e6).toFixed(1)}M`;
121
- }
122
- if (value >= 1000) {
123
- return `${(value / 1000).toFixed(1)}K`;
377
+ [visualizationRefKey(RetentionAreaVisualization.meta)]: {
378
+ data: [
379
+ { week: "Week 1", retentionRate: 0.71 },
380
+ { week: "Week 2", retentionRate: 0.66 },
381
+ { week: "Week 3", retentionRate: 0.62 },
382
+ { week: "Week 4", retentionRate: 0.58 },
383
+ { week: "Week 5", retentionRate: 0.55 },
384
+ { week: "Week 6", retentionRate: 0.53 }
385
+ ]
386
+ },
387
+ [visualizationRefKey(PipelineScatterVisualization.meta)]: {
388
+ data: [
389
+ { cycleDays: 18, winRate: 0.31, arr: 82000 },
390
+ { cycleDays: 26, winRate: 0.44, arr: 65000 },
391
+ { cycleDays: 33, winRate: 0.27, arr: 91000 },
392
+ { cycleDays: 14, winRate: 0.56, arr: 47000 },
393
+ { cycleDays: 21, winRate: 0.48, arr: 59000 },
394
+ { cycleDays: 39, winRate: 0.22, arr: 114000 }
395
+ ]
396
+ },
397
+ [visualizationRefKey(ChannelMixVisualization.meta)]: {
398
+ data: [
399
+ { channel: "Direct", sessions: 4200 },
400
+ { channel: "Organic Search", sessions: 3600 },
401
+ { channel: "Paid Search", sessions: 2100 },
402
+ { channel: "Partner", sessions: 1400 },
403
+ { channel: "Referral", sessions: 900 }
404
+ ]
405
+ },
406
+ [visualizationRefKey(EngagementHeatmapVisualization.meta)]: {
407
+ data: [
408
+ { weekday: "Mon", timeBand: "09:00", engagementScore: 74 },
409
+ { weekday: "Mon", timeBand: "13:00", engagementScore: 82 },
410
+ { weekday: "Tue", timeBand: "09:00", engagementScore: 69 },
411
+ { weekday: "Tue", timeBand: "13:00", engagementScore: 88 },
412
+ { weekday: "Wed", timeBand: "09:00", engagementScore: 77 },
413
+ { weekday: "Wed", timeBand: "13:00", engagementScore: 91 },
414
+ { weekday: "Thu", timeBand: "09:00", engagementScore: 72 },
415
+ { weekday: "Thu", timeBand: "13:00", engagementScore: 86 },
416
+ { weekday: "Fri", timeBand: "09:00", engagementScore: 65 },
417
+ { weekday: "Fri", timeBand: "13:00", engagementScore: 79 }
418
+ ]
419
+ },
420
+ [visualizationRefKey(ConversionFunnelVisualization.meta)]: {
421
+ data: [
422
+ { stage: "Visited Site", users: 12000 },
423
+ { stage: "Started Trial", users: 4200 },
424
+ { stage: "Activated Workspace", users: 2400 },
425
+ { stage: "Requested Demo", users: 980 },
426
+ { stage: "Closed Won", users: 310 }
427
+ ]
428
+ },
429
+ [visualizationRefKey(AccountCoverageGeoVisualization.meta)]: {
430
+ data: [
431
+ { city: "Paris", latitude: 48.8566, longitude: 2.3522, accounts: 48 },
432
+ { city: "London", latitude: 51.5072, longitude: -0.1276, accounts: 62 },
433
+ { city: "New York", latitude: 40.7128, longitude: -74.006, accounts: 71 },
434
+ { city: "Toronto", latitude: 43.6532, longitude: -79.3832, accounts: 36 },
435
+ {
436
+ city: "Singapore",
437
+ latitude: 1.3521,
438
+ longitude: 103.8198,
439
+ accounts: 29
440
+ }
441
+ ]
124
442
  }
125
- return value.toString();
443
+ };
444
+ function refOf(spec) {
445
+ return { key: spec.meta.key, version: spec.meta.version };
126
446
  }
127
- function formatChange(change) {
128
- const icon = change >= 0 ? "\uD83D\uDCC8" : "\uD83D\uDCC9";
129
- const sign = change >= 0 ? "+" : "";
130
- return `${icon} ${sign}${change.toFixed(1)}%`;
447
+ function visualizationRefKey(ref) {
448
+ return `${ref.key}.v${ref.version}`;
131
449
  }
132
- var analyticsDashboardMarkdownRenderer = {
133
- target: "markdown",
134
- render: async (desc) => {
135
- if (desc.source.type !== "component" || desc.source.componentKey !== "AnalyticsDashboard") {
136
- throw new Error("analyticsDashboardMarkdownRenderer: not AnalyticsDashboard");
137
- }
138
- const dashboards = mockDashboards;
139
- const widgets = mockWidgets;
140
- const queries = mockQueries;
141
- const dashboard = dashboards[0];
142
- if (!dashboard) {
143
- return {
144
- mimeType: "text/markdown",
145
- body: `# No Dashboards
146
450
 
147
- No dashboards available.`
148
- };
149
- }
150
- const dashboardWidgets = widgets.filter((w) => w.dashboardId === dashboard.id);
151
- const publishedDashboards = dashboards.filter((d) => d.status === "PUBLISHED");
152
- const totalViews = dashboards.reduce((sum, d) => sum + d.viewCount, 0);
153
- const sharedQueries = queries.filter((q) => q.isShared);
154
- const lines = [
155
- `# ${dashboard.name}`,
156
- "",
157
- "> Analytics dashboard overview",
158
- "",
159
- "## Key Metrics",
160
- ""
161
- ];
162
- const metricWidgets = dashboardWidgets.filter((w) => w.type === "METRIC");
163
- for (const widget of metricWidgets) {
164
- const w = widget;
165
- lines.push(`### ${w.name}`);
166
- lines.push(`**${formatNumber(w.value)}** ${formatChange(w.change)}`);
167
- lines.push("");
168
- }
169
- lines.push("## Visualizations");
170
- lines.push("");
171
- const chartWidgets = dashboardWidgets.filter((w) => w.type !== "METRIC");
172
- for (const widget of chartWidgets) {
173
- const w = widget;
174
- lines.push(`- **${w.name}** (${w.type.replace("_", " ")}) - ${w.dataPoints} data points`);
175
- }
176
- lines.push("");
177
- lines.push("## Dashboard Stats");
178
- lines.push("");
179
- lines.push("| Metric | Value |");
180
- lines.push("|--------|-------|");
181
- lines.push(`| Total Dashboards | ${dashboards.length} |`);
182
- lines.push(`| Published | ${publishedDashboards.length} |`);
183
- lines.push(`| Total Views | ${totalViews.toLocaleString()} |`);
184
- lines.push(`| Shared Queries | ${sharedQueries.length} |`);
185
- return {
186
- mimeType: "text/markdown",
187
- body: lines.join(`
188
- `)
189
- };
190
- }
451
+ // src/visualizations/widgets.ts
452
+ var LEGACY_VISUALIZATION_REFS = {
453
+ METRIC: refOf(RevenueMetricVisualization),
454
+ LINE_CHART: refOf(RevenueTrendVisualization),
455
+ BAR_CHART: refOf(RegionalRevenueVisualization),
456
+ AREA_CHART: refOf(RetentionAreaVisualization),
457
+ SCATTER_PLOT: refOf(PipelineScatterVisualization),
458
+ PIE_CHART: refOf(ChannelMixVisualization),
459
+ HEATMAP: refOf(EngagementHeatmapVisualization),
460
+ FUNNEL: refOf(ConversionFunnelVisualization),
461
+ MAP: refOf(AccountCoverageGeoVisualization)
191
462
  };
192
- var dashboardListMarkdownRenderer = {
193
- target: "markdown",
194
- render: async (desc) => {
195
- if (desc.source.type !== "component" || desc.source.componentKey !== "DashboardList") {
196
- throw new Error("dashboardListMarkdownRenderer: not DashboardList");
197
- }
198
- const dashboards = mockDashboards;
199
- const lines = [
200
- "# Dashboards",
201
- "",
202
- "> Browse and manage analytics dashboards",
203
- "",
204
- "| Dashboard | Widgets | Views | Status | Last Viewed |",
205
- "|-----------|---------|-------|--------|-------------|"
206
- ];
207
- for (const dashboard of dashboards) {
208
- const lastViewed = dashboard.lastViewedAt ? new Date(dashboard.lastViewedAt).toLocaleDateString() : "Never";
209
- const statusIcon = dashboard.status === "PUBLISHED" ? "\uD83D\uDFE2" : "\u26AB";
210
- lines.push(`| [${dashboard.name}](/dashboards/${dashboard.slug}) | ${dashboard.widgetCount} | ${dashboard.viewCount.toLocaleString()} | ${statusIcon} ${dashboard.status} | ${lastViewed} |`);
211
- }
212
- lines.push("");
213
- lines.push("## Quick Actions");
214
- lines.push("");
215
- lines.push("- **Create Dashboard** - Start with a blank canvas");
216
- lines.push("- **Import Template** - Use a pre-built template");
217
- lines.push("- **Clone Dashboard** - Duplicate an existing dashboard");
463
+ function createExampleWidgets(dashboardId) {
464
+ return [
465
+ widget(dashboardId, "widget_revenue_metric", "Revenue Snapshot", "METRIC", 0, 0, 4, 2, {
466
+ layout: "single",
467
+ bindings: [binding(RevenueMetricVisualization, 200)]
468
+ }),
469
+ widget(dashboardId, "widget_revenue_trend", "Revenue Trend", "LINE_CHART", 4, 0, 8, 4, {
470
+ layout: "single",
471
+ bindings: [binding(RevenueTrendVisualization)]
472
+ }),
473
+ widget(dashboardId, "widget_regional_revenue", "Regional Revenue", "BAR_CHART", 0, 2, 6, 4, {
474
+ layout: "single",
475
+ bindings: [binding(RegionalRevenueVisualization)]
476
+ }),
477
+ widget(dashboardId, "widget_channel_mix", "Channel Mix", "PIE_CHART", 6, 2, 6, 4, {
478
+ layout: "single",
479
+ bindings: [binding(ChannelMixVisualization)]
480
+ }),
481
+ widget(dashboardId, "widget_retention", "Retention Curve", "AREA_CHART", 0, 6, 6, 4, {
482
+ layout: "single",
483
+ bindings: [binding(RetentionAreaVisualization)]
484
+ }),
485
+ widget(dashboardId, "widget_pipeline", "Pipeline Velocity", "SCATTER_PLOT", 6, 6, 6, 4, {
486
+ layout: "single",
487
+ bindings: [binding(PipelineScatterVisualization)]
488
+ }),
489
+ widget(dashboardId, "widget_heatmap", "Engagement Heatmap", "HEATMAP", 0, 10, 8, 4, {
490
+ layout: "single",
491
+ bindings: [binding(EngagementHeatmapVisualization)]
492
+ }),
493
+ widget(dashboardId, "widget_funnel", "Conversion Funnel", "FUNNEL", 8, 10, 4, 4, {
494
+ layout: "single",
495
+ bindings: [binding(ConversionFunnelVisualization)]
496
+ }),
497
+ widget(dashboardId, "widget_geo", "Account Coverage", "MAP", 0, 14, 12, 5, {
498
+ layout: "single",
499
+ bindings: [binding(AccountCoverageGeoVisualization, 360)]
500
+ }),
501
+ widget(dashboardId, "widget_comparison", "Commercial Comparison", "EMBED", 0, 19, 12, 6, {
502
+ layout: "comparison",
503
+ description: "Compare regional distribution, channel balance, and funnel shape.",
504
+ bindings: [
505
+ binding(RegionalRevenueVisualization, 240),
506
+ binding(ChannelMixVisualization, 240),
507
+ binding(ConversionFunnelVisualization, 240)
508
+ ]
509
+ }),
510
+ widget(dashboardId, "widget_timeline", "Growth Timeline", "EMBED", 0, 25, 12, 6, {
511
+ layout: "timeline",
512
+ description: "Track revenue and retention over the same reporting cadence.",
513
+ bindings: [
514
+ binding(RevenueTrendVisualization, 220),
515
+ binding(RetentionAreaVisualization, 220)
516
+ ]
517
+ })
518
+ ];
519
+ }
520
+ function resolveAnalyticsWidget(widget) {
521
+ const config = parseWidgetConfig(widget);
522
+ const bindings = config.bindings.map((binding) => {
523
+ const spec = AnalyticsVisualizationSpecMap.get(visualizationRefKey(binding.ref));
524
+ if (!spec)
525
+ return null;
218
526
  return {
219
- mimeType: "text/markdown",
220
- body: lines.join(`
221
- `)
527
+ key: `${widget.id}:${visualizationRefKey(binding.ref)}`,
528
+ spec,
529
+ data: binding.data,
530
+ title: binding.title ?? spec.meta.title,
531
+ description: binding.description ?? spec.meta.description,
532
+ height: binding.height
222
533
  };
534
+ }).filter((binding) => Boolean(binding));
535
+ if (!bindings.length)
536
+ return null;
537
+ return {
538
+ id: widget.id,
539
+ name: widget.name,
540
+ description: config.description,
541
+ layout: config.layout,
542
+ gridX: widget.gridX,
543
+ gridY: widget.gridY,
544
+ gridWidth: widget.gridWidth,
545
+ gridHeight: widget.gridHeight,
546
+ bindings
547
+ };
548
+ }
549
+ function parseWidgetConfig(widget) {
550
+ if (isAnalyticsWidgetConfig(widget.config)) {
551
+ return widget.config;
223
552
  }
224
- };
225
- var queryBuilderMarkdownRenderer = {
226
- target: "markdown",
227
- render: async (desc) => {
228
- if (desc.source.type !== "component" || desc.source.componentKey !== "QueryBuilder") {
229
- throw new Error("queryBuilderMarkdownRenderer: not QueryBuilder");
230
- }
231
- const queries = mockQueries;
232
- const lines = [
233
- "# Query Builder",
234
- "",
235
- "> Create and manage data queries",
236
- "",
237
- "## Saved Queries",
238
- "",
239
- "| Query | Type | Shared | Executions |",
240
- "|-------|------|--------|------------|"
241
- ];
242
- for (const query of queries) {
243
- const sharedIcon = query.isShared ? "\uD83C\uDF10" : "\uD83D\uDD12";
244
- lines.push(`| ${query.name} | ${query.type} | ${sharedIcon} | ${query.executionCount.toLocaleString()} |`);
245
- }
246
- lines.push("");
247
- lines.push("## Query Types");
248
- lines.push("");
249
- lines.push("### METRIC");
250
- lines.push("Query usage metrics from the metering system.");
251
- lines.push("");
252
- lines.push("### AGGREGATION");
253
- lines.push("Build aggregations with measures and dimensions without writing SQL.");
254
- lines.push("");
255
- lines.push("### SQL");
256
- lines.push("Write custom SQL queries for advanced analysis.");
257
- lines.push("");
258
- lines.push("## Features");
259
- lines.push("");
260
- lines.push("- Visual query builder");
261
- lines.push("- Auto-complete for fields and functions");
262
- lines.push("- Query validation and optimization");
263
- lines.push("- Result caching");
264
- lines.push("- Query sharing and collaboration");
265
- return {
266
- mimeType: "text/markdown",
267
- body: lines.join(`
268
- `)
269
- };
553
+ const legacyRef = LEGACY_VISUALIZATION_REFS[widget.type];
554
+ return legacyRef ? {
555
+ layout: "single",
556
+ bindings: [
557
+ {
558
+ ref: legacyRef,
559
+ data: AnalyticsVisualizationSampleData[visualizationRefKey(legacyRef)]
560
+ }
561
+ ]
562
+ } : { layout: "single", bindings: [] };
563
+ }
564
+ function binding(spec, height = 280) {
565
+ return {
566
+ ref: refOf(spec),
567
+ data: AnalyticsVisualizationSampleData[visualizationRefKey(spec.meta)],
568
+ height
569
+ };
570
+ }
571
+ function widget(dashboardId, id, name, type, gridX, gridY, gridWidth, gridHeight, config) {
572
+ const now = new Date;
573
+ return {
574
+ id,
575
+ dashboardId,
576
+ name,
577
+ type,
578
+ gridX,
579
+ gridY,
580
+ gridWidth,
581
+ gridHeight,
582
+ config,
583
+ createdAt: now,
584
+ updatedAt: now
585
+ };
586
+ }
587
+ function isAnalyticsWidgetConfig(value) {
588
+ if (!value || typeof value !== "object")
589
+ return false;
590
+ const candidate = value;
591
+ return (candidate.layout === "single" || candidate.layout === "comparison" || candidate.layout === "timeline") && Array.isArray(candidate.bindings);
592
+ }
593
+ // src/ui/AnalyticsDashboard.widgets.tsx
594
+ import {
595
+ ComparisonView,
596
+ TimelineView,
597
+ VisualizationCard,
598
+ VisualizationGrid
599
+ } from "@contractspec/lib.design-system";
600
+ import { jsxDEV } from "react/jsx-dev-runtime";
601
+ "use client";
602
+ function AnalyticsWidgetBoard({
603
+ dashboardName,
604
+ widgets
605
+ }) {
606
+ if (!widgets.length) {
607
+ return /* @__PURE__ */ jsxDEV("div", {
608
+ className: "rounded-lg border border-dashed p-10 text-center text-muted-foreground",
609
+ children: [
610
+ 'No visualization widgets configured for "',
611
+ dashboardName,
612
+ '".'
613
+ ]
614
+ }, undefined, true, undefined, this);
270
615
  }
616
+ return /* @__PURE__ */ jsxDEV("div", {
617
+ children: [
618
+ /* @__PURE__ */ jsxDEV("h3", {
619
+ className: "mb-4 font-semibold text-lg",
620
+ children: [
621
+ 'Widgets in "',
622
+ dashboardName,
623
+ '"'
624
+ ]
625
+ }, undefined, true, undefined, this),
626
+ /* @__PURE__ */ jsxDEV(VisualizationGrid, {
627
+ children: widgets.map((widget2) => /* @__PURE__ */ jsxDEV("div", {
628
+ className: gridSpanClass(widget2.gridWidth),
629
+ children: renderVisualizationWidget(widget2)
630
+ }, widget2.id, false, undefined, this))
631
+ }, undefined, false, undefined, this)
632
+ ]
633
+ }, undefined, true, undefined, this);
634
+ }
635
+ function renderVisualizationWidget(widget2) {
636
+ const footer = /* @__PURE__ */ jsxDEV("span", {
637
+ className: "text-muted-foreground text-xs",
638
+ children: [
639
+ "Position: (",
640
+ widget2.gridX,
641
+ ", ",
642
+ widget2.gridY,
643
+ ") \u2022 ",
644
+ widget2.gridWidth,
645
+ "x",
646
+ widget2.gridHeight
647
+ ]
648
+ }, undefined, true, undefined, this);
649
+ if (widget2.layout === "comparison") {
650
+ return /* @__PURE__ */ jsxDEV(ComparisonView, {
651
+ description: widget2.description,
652
+ items: widget2.bindings.map((binding3) => ({ ...binding3, footer })),
653
+ title: widget2.name
654
+ }, undefined, false, undefined, this);
655
+ }
656
+ if (widget2.layout === "timeline") {
657
+ return /* @__PURE__ */ jsxDEV(TimelineView, {
658
+ description: widget2.description,
659
+ items: widget2.bindings.map((binding3) => ({ ...binding3, footer })),
660
+ title: widget2.name
661
+ }, undefined, false, undefined, this);
662
+ }
663
+ const binding2 = widget2.bindings[0];
664
+ if (!binding2)
665
+ return null;
666
+ return /* @__PURE__ */ jsxDEV(VisualizationCard, {
667
+ data: binding2.data,
668
+ description: widget2.description ?? binding2.description,
669
+ footer,
670
+ height: binding2.height,
671
+ spec: binding2.spec,
672
+ title: widget2.name
673
+ }, undefined, false, undefined, this);
674
+ }
675
+ function gridSpanClass(gridWidth) {
676
+ if (gridWidth >= 12)
677
+ return "md:col-span-2 xl:col-span-3";
678
+ if (gridWidth >= 8)
679
+ return "xl:col-span-2";
680
+ if (gridWidth >= 6)
681
+ return "md:col-span-2";
682
+ return "";
683
+ }
684
+
685
+ // src/ui/AnalyticsQueriesTable.tsx
686
+ import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
687
+ "use client";
688
+ var QUERY_TYPE_COLORS = {
689
+ SQL: "bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400",
690
+ METRIC: "bg-purple-100 text-purple-700 dark:bg-purple-900/30 dark:text-purple-400",
691
+ AGGREGATION: "bg-indigo-100 text-indigo-700 dark:bg-indigo-900/30 dark:text-indigo-400",
692
+ CUSTOM: "bg-gray-100 text-gray-700 dark:bg-gray-900/30 dark:text-gray-400"
271
693
  };
694
+ function AnalyticsQueriesTable({ queries }) {
695
+ return /* @__PURE__ */ jsxDEV2("div", {
696
+ className: "rounded-lg border border-border",
697
+ children: /* @__PURE__ */ jsxDEV2("table", {
698
+ className: "w-full",
699
+ children: [
700
+ /* @__PURE__ */ jsxDEV2("thead", {
701
+ className: "border-border border-b bg-muted/30",
702
+ children: /* @__PURE__ */ jsxDEV2("tr", {
703
+ children: [
704
+ /* @__PURE__ */ jsxDEV2("th", {
705
+ className: "px-4 py-3 text-left font-medium text-sm",
706
+ children: "Query"
707
+ }, undefined, false, undefined, this),
708
+ /* @__PURE__ */ jsxDEV2("th", {
709
+ className: "px-4 py-3 text-left font-medium text-sm",
710
+ children: "Type"
711
+ }, undefined, false, undefined, this),
712
+ /* @__PURE__ */ jsxDEV2("th", {
713
+ className: "px-4 py-3 text-left font-medium text-sm",
714
+ children: "Cache TTL"
715
+ }, undefined, false, undefined, this),
716
+ /* @__PURE__ */ jsxDEV2("th", {
717
+ className: "px-4 py-3 text-left font-medium text-sm",
718
+ children: "Shared"
719
+ }, undefined, false, undefined, this)
720
+ ]
721
+ }, undefined, true, undefined, this)
722
+ }, undefined, false, undefined, this),
723
+ /* @__PURE__ */ jsxDEV2("tbody", {
724
+ className: "divide-y divide-border",
725
+ children: [
726
+ queries.map((query) => /* @__PURE__ */ jsxDEV2("tr", {
727
+ className: "hover:bg-muted/50",
728
+ children: [
729
+ /* @__PURE__ */ jsxDEV2("td", {
730
+ className: "px-4 py-3",
731
+ children: [
732
+ /* @__PURE__ */ jsxDEV2("div", {
733
+ className: "font-medium",
734
+ children: query.name
735
+ }, undefined, false, undefined, this),
736
+ /* @__PURE__ */ jsxDEV2("div", {
737
+ className: "text-muted-foreground text-sm",
738
+ children: query.description
739
+ }, undefined, false, undefined, this)
740
+ ]
741
+ }, undefined, true, undefined, this),
742
+ /* @__PURE__ */ jsxDEV2("td", {
743
+ className: "px-4 py-3",
744
+ children: /* @__PURE__ */ jsxDEV2("span", {
745
+ className: `inline-flex rounded-full px-2 py-0.5 font-medium text-xs ${QUERY_TYPE_COLORS[query.type] ?? ""}`,
746
+ children: query.type
747
+ }, undefined, false, undefined, this)
748
+ }, undefined, false, undefined, this),
749
+ /* @__PURE__ */ jsxDEV2("td", {
750
+ className: "px-4 py-3 text-muted-foreground text-sm",
751
+ children: [
752
+ query.cacheTtlSeconds,
753
+ "s"
754
+ ]
755
+ }, undefined, true, undefined, this),
756
+ /* @__PURE__ */ jsxDEV2("td", {
757
+ className: "px-4 py-3",
758
+ children: query.isShared ? /* @__PURE__ */ jsxDEV2("span", {
759
+ className: "text-green-600 dark:text-green-400",
760
+ children: "\u2713"
761
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV2("span", {
762
+ className: "text-muted-foreground",
763
+ children: "\u2014"
764
+ }, undefined, false, undefined, this)
765
+ }, undefined, false, undefined, this)
766
+ ]
767
+ }, query.id, true, undefined, this)),
768
+ queries.length === 0 && /* @__PURE__ */ jsxDEV2("tr", {
769
+ children: /* @__PURE__ */ jsxDEV2("td", {
770
+ colSpan: 4,
771
+ className: "px-4 py-8 text-center text-muted-foreground",
772
+ children: "No queries saved"
773
+ }, undefined, false, undefined, this)
774
+ }, undefined, false, undefined, this)
775
+ ]
776
+ }, undefined, true, undefined, this)
777
+ ]
778
+ }, undefined, true, undefined, this)
779
+ }, undefined, false, undefined, this);
780
+ }
781
+
272
782
  // src/ui/hooks/useAnalyticsData.ts
273
- import { useCallback, useEffect, useState } from "react";
274
783
  import { useTemplateRuntime } from "@contractspec/lib.example-shared-ui";
784
+ import { useCallback, useEffect, useState } from "react";
275
785
  "use client";
276
786
  function useAnalyticsData(projectId = "local-project") {
277
787
  const { handlers } = useTemplateRuntime();
@@ -297,7 +807,7 @@ function useAnalyticsData(projectId = "local-project") {
297
807
  if (first) {
298
808
  setSelectedDashboard(first);
299
809
  const dashboardWidgets = await analytics.getWidgets(first.id);
300
- setWidgets(dashboardWidgets);
810
+ setWidgets(dashboardWidgets.length > 0 ? dashboardWidgets : createExampleWidgets(first.id));
301
811
  }
302
812
  }
303
813
  } catch (err) {
@@ -312,7 +822,7 @@ function useAnalyticsData(projectId = "local-project") {
312
822
  const selectDashboard = useCallback(async (dashboard) => {
313
823
  setSelectedDashboard(dashboard);
314
824
  const dashboardWidgets = await analytics.getWidgets(dashboard.id);
315
- setWidgets(dashboardWidgets);
825
+ setWidgets(dashboardWidgets.length > 0 ? dashboardWidgets : createExampleWidgets(dashboard.id));
316
826
  }, [analytics]);
317
827
  const stats = {
318
828
  totalDashboards: dashboards.length,
@@ -334,7 +844,6 @@ function useAnalyticsData(projectId = "local-project") {
334
844
  }
335
845
 
336
846
  // src/ui/AnalyticsDashboard.tsx
337
- import { useState as useState2 } from "react";
338
847
  import {
339
848
  Button,
340
849
  ErrorState,
@@ -342,33 +851,14 @@ import {
342
851
  StatCard,
343
852
  StatCardGroup
344
853
  } from "@contractspec/lib.design-system";
345
- import { jsxDEV } from "react/jsx-dev-runtime";
854
+ import { useMemo, useState as useState2 } from "react";
855
+ import { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
346
856
  "use client";
347
857
  var STATUS_COLORS = {
348
858
  PUBLISHED: "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400",
349
859
  DRAFT: "bg-gray-100 text-gray-700 dark:bg-gray-900/30 dark:text-gray-400",
350
860
  ARCHIVED: "bg-yellow-100 text-yellow-700 dark:bg-yellow-900/30 dark:text-yellow-400"
351
861
  };
352
- var WIDGET_ICONS = {
353
- LINE_CHART: "\uD83D\uDCC8",
354
- BAR_CHART: "\uD83D\uDCCA",
355
- PIE_CHART: "\uD83E\uDD67",
356
- AREA_CHART: "\uD83D\uDCC9",
357
- SCATTER_PLOT: "\u26AC",
358
- METRIC: "\uD83D\uDD22",
359
- TABLE: "\uD83D\uDCCB",
360
- HEATMAP: "\uD83D\uDDFA\uFE0F",
361
- FUNNEL: "\u23EC",
362
- MAP: "\uD83C\uDF0D",
363
- TEXT: "\uD83D\uDCDD",
364
- EMBED: "\uD83D\uDD17"
365
- };
366
- var QUERY_TYPE_COLORS = {
367
- SQL: "bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400",
368
- METRIC: "bg-purple-100 text-purple-700 dark:bg-purple-900/30 dark:text-purple-400",
369
- AGGREGATION: "bg-indigo-100 text-indigo-700 dark:bg-indigo-900/30 dark:text-indigo-400",
370
- CUSTOM: "bg-gray-100 text-gray-700 dark:bg-gray-900/30 dark:text-gray-400"
371
- };
372
862
  function AnalyticsDashboard() {
373
863
  const [activeTab, setActiveTab] = useState2("dashboards");
374
864
  const {
@@ -386,33 +876,34 @@ function AnalyticsDashboard() {
386
876
  { id: "dashboards", label: "Dashboards", icon: "\uD83D\uDCCA" },
387
877
  { id: "queries", label: "Queries", icon: "\uD83D\uDD0D" }
388
878
  ];
879
+ const resolvedWidgets = useMemo(() => widgets.map((widget2) => resolveAnalyticsWidget(widget2)).filter((widget2) => Boolean(widget2)), [widgets]);
389
880
  if (loading) {
390
- return /* @__PURE__ */ jsxDEV(LoaderBlock, {
881
+ return /* @__PURE__ */ jsxDEV3(LoaderBlock, {
391
882
  label: "Loading Analytics..."
392
883
  }, undefined, false, undefined, this);
393
884
  }
394
885
  if (error) {
395
- return /* @__PURE__ */ jsxDEV(ErrorState, {
886
+ return /* @__PURE__ */ jsxDEV3(ErrorState, {
396
887
  title: "Failed to load Analytics",
397
888
  description: error.message,
398
889
  onRetry: refetch,
399
890
  retryLabel: "Retry"
400
891
  }, undefined, false, undefined, this);
401
892
  }
402
- return /* @__PURE__ */ jsxDEV("div", {
893
+ return /* @__PURE__ */ jsxDEV3("div", {
403
894
  className: "space-y-6",
404
895
  children: [
405
- /* @__PURE__ */ jsxDEV("div", {
896
+ /* @__PURE__ */ jsxDEV3("div", {
406
897
  className: "flex items-center justify-between",
407
898
  children: [
408
- /* @__PURE__ */ jsxDEV("h2", {
409
- className: "text-2xl font-bold",
899
+ /* @__PURE__ */ jsxDEV3("h2", {
900
+ className: "font-bold text-2xl",
410
901
  children: "Analytics Dashboard"
411
902
  }, undefined, false, undefined, this),
412
- /* @__PURE__ */ jsxDEV(Button, {
903
+ /* @__PURE__ */ jsxDEV3(Button, {
413
904
  onClick: () => alert("Create dashboard modal"),
414
905
  children: [
415
- /* @__PURE__ */ jsxDEV("span", {
906
+ /* @__PURE__ */ jsxDEV3("span", {
416
907
  className: "mr-2",
417
908
  children: "+"
418
909
  }, undefined, false, undefined, this),
@@ -421,55 +912,55 @@ function AnalyticsDashboard() {
421
912
  }, undefined, true, undefined, this)
422
913
  ]
423
914
  }, undefined, true, undefined, this),
424
- /* @__PURE__ */ jsxDEV(StatCardGroup, {
915
+ /* @__PURE__ */ jsxDEV3(StatCardGroup, {
425
916
  children: [
426
- /* @__PURE__ */ jsxDEV(StatCard, {
917
+ /* @__PURE__ */ jsxDEV3(StatCard, {
427
918
  label: "Dashboards",
428
919
  value: stats.totalDashboards,
429
920
  hint: `${stats.publishedDashboards} published`
430
921
  }, undefined, false, undefined, this),
431
- /* @__PURE__ */ jsxDEV(StatCard, {
922
+ /* @__PURE__ */ jsxDEV3(StatCard, {
432
923
  label: "Queries",
433
924
  value: stats.totalQueries,
434
925
  hint: `${stats.sharedQueries} shared`
435
926
  }, undefined, false, undefined, this),
436
- /* @__PURE__ */ jsxDEV(StatCard, {
927
+ /* @__PURE__ */ jsxDEV3(StatCard, {
437
928
  label: "Widgets",
438
929
  value: widgets.length,
439
930
  hint: "on current dashboard"
440
931
  }, undefined, false, undefined, this)
441
932
  ]
442
933
  }, undefined, true, undefined, this),
443
- /* @__PURE__ */ jsxDEV("nav", {
444
- className: "bg-muted flex gap-1 rounded-lg p-1",
934
+ /* @__PURE__ */ jsxDEV3("nav", {
935
+ className: "flex gap-1 rounded-lg bg-muted p-1",
445
936
  role: "tablist",
446
- children: tabs.map((tab) => /* @__PURE__ */ jsxDEV(Button, {
937
+ children: tabs.map((tab) => /* @__PURE__ */ jsxDEV3(Button, {
447
938
  type: "button",
448
939
  role: "tab",
449
940
  "aria-selected": activeTab === tab.id,
450
941
  onClick: () => setActiveTab(tab.id),
451
- className: `flex flex-1 items-center justify-center gap-2 rounded-md px-4 py-2 text-sm font-medium transition-colors ${activeTab === tab.id ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"}`,
942
+ className: `flex flex-1 items-center justify-center gap-2 rounded-md px-4 py-2 font-medium text-sm transition-colors ${activeTab === tab.id ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"}`,
452
943
  children: [
453
- /* @__PURE__ */ jsxDEV("span", {
944
+ /* @__PURE__ */ jsxDEV3("span", {
454
945
  children: tab.icon
455
946
  }, undefined, false, undefined, this),
456
947
  tab.label
457
948
  ]
458
949
  }, tab.id, true, undefined, this))
459
950
  }, undefined, false, undefined, this),
460
- /* @__PURE__ */ jsxDEV("div", {
951
+ /* @__PURE__ */ jsxDEV3("div", {
461
952
  className: "min-h-[400px]",
462
953
  role: "tabpanel",
463
954
  children: [
464
- activeTab === "dashboards" && /* @__PURE__ */ jsxDEV("div", {
955
+ activeTab === "dashboards" && /* @__PURE__ */ jsxDEV3("div", {
465
956
  className: "space-y-6",
466
957
  children: [
467
- /* @__PURE__ */ jsxDEV("div", {
958
+ /* @__PURE__ */ jsxDEV3("div", {
468
959
  className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3",
469
960
  children: [
470
- dashboards.map((dashboard) => /* @__PURE__ */ jsxDEV("div", {
961
+ dashboards.map((dashboard) => /* @__PURE__ */ jsxDEV3("div", {
471
962
  onClick: () => selectDashboard(dashboard),
472
- className: `border-border bg-card cursor-pointer rounded-lg border p-4 transition-all ${selectedDashboard?.id === dashboard.id ? "ring-primary ring-2" : "hover:bg-muted/50"}`,
963
+ className: `cursor-pointer rounded-lg border border-border bg-card p-4 transition-all ${selectedDashboard?.id === dashboard.id ? "ring-2 ring-primary" : "hover:bg-muted/50"}`,
473
964
  role: "button",
474
965
  tabIndex: 0,
475
966
  onKeyDown: (e) => {
@@ -477,33 +968,33 @@ function AnalyticsDashboard() {
477
968
  selectDashboard(dashboard);
478
969
  },
479
970
  children: [
480
- /* @__PURE__ */ jsxDEV("div", {
971
+ /* @__PURE__ */ jsxDEV3("div", {
481
972
  className: "mb-2 flex items-center justify-between",
482
973
  children: [
483
- /* @__PURE__ */ jsxDEV("h3", {
974
+ /* @__PURE__ */ jsxDEV3("h3", {
484
975
  className: "font-medium",
485
976
  children: dashboard.name
486
977
  }, undefined, false, undefined, this),
487
- /* @__PURE__ */ jsxDEV("span", {
488
- className: `inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${STATUS_COLORS[dashboard.status] ?? ""}`,
978
+ /* @__PURE__ */ jsxDEV3("span", {
979
+ className: `inline-flex rounded-full px-2 py-0.5 font-medium text-xs ${STATUS_COLORS[dashboard.status] ?? ""}`,
489
980
  children: dashboard.status
490
981
  }, undefined, false, undefined, this)
491
982
  ]
492
983
  }, undefined, true, undefined, this),
493
- /* @__PURE__ */ jsxDEV("p", {
494
- className: "text-muted-foreground mb-3 text-sm",
984
+ /* @__PURE__ */ jsxDEV3("p", {
985
+ className: "mb-3 text-muted-foreground text-sm",
495
986
  children: dashboard.description
496
987
  }, undefined, false, undefined, this),
497
- /* @__PURE__ */ jsxDEV("div", {
498
- className: "text-muted-foreground flex items-center justify-between text-xs",
988
+ /* @__PURE__ */ jsxDEV3("div", {
989
+ className: "flex items-center justify-between text-muted-foreground text-xs",
499
990
  children: [
500
- /* @__PURE__ */ jsxDEV("span", {
991
+ /* @__PURE__ */ jsxDEV3("span", {
501
992
  children: [
502
993
  "/",
503
994
  dashboard.slug
504
995
  ]
505
996
  }, undefined, true, undefined, this),
506
- dashboard.isPublic && /* @__PURE__ */ jsxDEV("span", {
997
+ dashboard.isPublic && /* @__PURE__ */ jsxDEV3("span", {
507
998
  className: "text-green-600 dark:text-green-400",
508
999
  children: "\uD83C\uDF10 Public"
509
1000
  }, undefined, false, undefined, this)
@@ -511,149 +1002,20 @@ function AnalyticsDashboard() {
511
1002
  }, undefined, true, undefined, this)
512
1003
  ]
513
1004
  }, dashboard.id, true, undefined, this)),
514
- dashboards.length === 0 && /* @__PURE__ */ jsxDEV("div", {
515
- className: "text-muted-foreground col-span-full flex h-64 items-center justify-center",
1005
+ dashboards.length === 0 && /* @__PURE__ */ jsxDEV3("div", {
1006
+ className: "col-span-full flex h-64 items-center justify-center text-muted-foreground",
516
1007
  children: "No dashboards created yet"
517
1008
  }, undefined, false, undefined, this)
518
1009
  ]
519
1010
  }, undefined, true, undefined, this),
520
- selectedDashboard && widgets.length > 0 && /* @__PURE__ */ jsxDEV("div", {
521
- children: [
522
- /* @__PURE__ */ jsxDEV("h3", {
523
- className: "mb-4 text-lg font-semibold",
524
- children: [
525
- 'Widgets in "',
526
- selectedDashboard.name,
527
- '"'
528
- ]
529
- }, undefined, true, undefined, this),
530
- /* @__PURE__ */ jsxDEV("div", {
531
- className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3",
532
- children: widgets.map((widget) => /* @__PURE__ */ jsxDEV("div", {
533
- className: "border-border bg-card rounded-lg border p-4",
534
- children: [
535
- /* @__PURE__ */ jsxDEV("div", {
536
- className: "mb-2 flex items-center gap-2",
537
- children: [
538
- /* @__PURE__ */ jsxDEV("span", {
539
- className: "text-xl",
540
- children: WIDGET_ICONS[widget.type] ?? "\uD83D\uDCCA"
541
- }, undefined, false, undefined, this),
542
- /* @__PURE__ */ jsxDEV("span", {
543
- className: "font-medium",
544
- children: widget.name
545
- }, undefined, false, undefined, this)
546
- ]
547
- }, undefined, true, undefined, this),
548
- /* @__PURE__ */ jsxDEV("div", {
549
- className: "text-muted-foreground text-sm",
550
- children: widget.type.replace(/_/g, " ")
551
- }, undefined, false, undefined, this),
552
- /* @__PURE__ */ jsxDEV("div", {
553
- className: "text-muted-foreground mt-2 text-xs",
554
- children: [
555
- "Position: (",
556
- widget.gridX,
557
- ", ",
558
- widget.gridY,
559
- ") \u2022",
560
- " ",
561
- widget.gridWidth,
562
- "x",
563
- widget.gridHeight
564
- ]
565
- }, undefined, true, undefined, this)
566
- ]
567
- }, widget.id, true, undefined, this))
568
- }, undefined, false, undefined, this)
569
- ]
570
- }, undefined, true, undefined, this)
1011
+ selectedDashboard ? /* @__PURE__ */ jsxDEV3(AnalyticsWidgetBoard, {
1012
+ dashboardName: selectedDashboard.name,
1013
+ widgets: resolvedWidgets
1014
+ }, undefined, false, undefined, this) : null
571
1015
  ]
572
1016
  }, undefined, true, undefined, this),
573
- activeTab === "queries" && /* @__PURE__ */ jsxDEV("div", {
574
- className: "border-border rounded-lg border",
575
- children: /* @__PURE__ */ jsxDEV("table", {
576
- className: "w-full",
577
- children: [
578
- /* @__PURE__ */ jsxDEV("thead", {
579
- className: "border-border bg-muted/30 border-b",
580
- children: /* @__PURE__ */ jsxDEV("tr", {
581
- children: [
582
- /* @__PURE__ */ jsxDEV("th", {
583
- className: "px-4 py-3 text-left text-sm font-medium",
584
- children: "Query"
585
- }, undefined, false, undefined, this),
586
- /* @__PURE__ */ jsxDEV("th", {
587
- className: "px-4 py-3 text-left text-sm font-medium",
588
- children: "Type"
589
- }, undefined, false, undefined, this),
590
- /* @__PURE__ */ jsxDEV("th", {
591
- className: "px-4 py-3 text-left text-sm font-medium",
592
- children: "Cache TTL"
593
- }, undefined, false, undefined, this),
594
- /* @__PURE__ */ jsxDEV("th", {
595
- className: "px-4 py-3 text-left text-sm font-medium",
596
- children: "Shared"
597
- }, undefined, false, undefined, this)
598
- ]
599
- }, undefined, true, undefined, this)
600
- }, undefined, false, undefined, this),
601
- /* @__PURE__ */ jsxDEV("tbody", {
602
- className: "divide-border divide-y",
603
- children: [
604
- queries.map((query) => /* @__PURE__ */ jsxDEV("tr", {
605
- className: "hover:bg-muted/50",
606
- children: [
607
- /* @__PURE__ */ jsxDEV("td", {
608
- className: "px-4 py-3",
609
- children: [
610
- /* @__PURE__ */ jsxDEV("div", {
611
- className: "font-medium",
612
- children: query.name
613
- }, undefined, false, undefined, this),
614
- /* @__PURE__ */ jsxDEV("div", {
615
- className: "text-muted-foreground text-sm",
616
- children: query.description
617
- }, undefined, false, undefined, this)
618
- ]
619
- }, undefined, true, undefined, this),
620
- /* @__PURE__ */ jsxDEV("td", {
621
- className: "px-4 py-3",
622
- children: /* @__PURE__ */ jsxDEV("span", {
623
- className: `inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${QUERY_TYPE_COLORS[query.type] ?? ""}`,
624
- children: query.type
625
- }, undefined, false, undefined, this)
626
- }, undefined, false, undefined, this),
627
- /* @__PURE__ */ jsxDEV("td", {
628
- className: "text-muted-foreground px-4 py-3 text-sm",
629
- children: [
630
- query.cacheTtlSeconds,
631
- "s"
632
- ]
633
- }, undefined, true, undefined, this),
634
- /* @__PURE__ */ jsxDEV("td", {
635
- className: "px-4 py-3",
636
- children: query.isShared ? /* @__PURE__ */ jsxDEV("span", {
637
- className: "text-green-600 dark:text-green-400",
638
- children: "\u2713"
639
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV("span", {
640
- className: "text-muted-foreground",
641
- children: "\u2014"
642
- }, undefined, false, undefined, this)
643
- }, undefined, false, undefined, this)
644
- ]
645
- }, query.id, true, undefined, this)),
646
- queries.length === 0 && /* @__PURE__ */ jsxDEV("tr", {
647
- children: /* @__PURE__ */ jsxDEV("td", {
648
- colSpan: 4,
649
- className: "text-muted-foreground px-4 py-8 text-center",
650
- children: "No queries saved"
651
- }, undefined, false, undefined, this)
652
- }, undefined, false, undefined, this)
653
- ]
654
- }, undefined, true, undefined, this)
655
- ]
656
- }, undefined, true, undefined, this)
1017
+ activeTab === "queries" && /* @__PURE__ */ jsxDEV3(AnalyticsQueriesTable, {
1018
+ queries
657
1019
  }, undefined, false, undefined, this)
658
1020
  ]
659
1021
  }, undefined, true, undefined, this)
@@ -663,6 +1025,168 @@ function AnalyticsDashboard() {
663
1025
 
664
1026
  // src/ui/hooks/index.ts
665
1027
  "use client";
1028
+
1029
+ // src/ui/renderers/analytics.markdown.ts
1030
+ var mockDashboards = [
1031
+ {
1032
+ id: "dash-1",
1033
+ name: "Sales Overview",
1034
+ slug: "sales-overview",
1035
+ status: "PUBLISHED",
1036
+ widgetCount: 11,
1037
+ viewCount: 1250,
1038
+ lastViewedAt: "2026-03-18T12:00:00Z"
1039
+ },
1040
+ {
1041
+ id: "dash-2",
1042
+ name: "User Engagement",
1043
+ slug: "user-engagement",
1044
+ status: "PUBLISHED",
1045
+ widgetCount: 8,
1046
+ viewCount: 890,
1047
+ lastViewedAt: "2026-03-18T10:00:00Z"
1048
+ }
1049
+ ];
1050
+ var mockQueries = [
1051
+ {
1052
+ id: "q-1",
1053
+ name: "Monthly Revenue",
1054
+ type: "AGGREGATION",
1055
+ isShared: true,
1056
+ executionCount: 1500
1057
+ },
1058
+ {
1059
+ id: "q-2",
1060
+ name: "User Growth",
1061
+ type: "METRIC",
1062
+ isShared: true,
1063
+ executionCount: 890
1064
+ },
1065
+ {
1066
+ id: "q-3",
1067
+ name: "Product Sales",
1068
+ type: "SQL",
1069
+ isShared: false,
1070
+ executionCount: 340
1071
+ },
1072
+ {
1073
+ id: "q-4",
1074
+ name: "Conversion Funnel",
1075
+ type: "AGGREGATION",
1076
+ isShared: true,
1077
+ executionCount: 450
1078
+ }
1079
+ ];
1080
+ function dashboardWidgets(dashboardId) {
1081
+ return createExampleWidgets(dashboardId).map((widget2) => resolveAnalyticsWidget(widget2)).filter((widget2) => Boolean(widget2));
1082
+ }
1083
+ var analyticsDashboardMarkdownRenderer = {
1084
+ target: "markdown",
1085
+ render: async (desc) => {
1086
+ if (desc.source.type !== "component" || desc.source.componentKey !== "AnalyticsDashboard") {
1087
+ throw new Error("analyticsDashboardMarkdownRenderer: not AnalyticsDashboard");
1088
+ }
1089
+ const dashboard = mockDashboards[0];
1090
+ if (!dashboard) {
1091
+ return {
1092
+ mimeType: "text/markdown",
1093
+ body: `# No Dashboards
1094
+
1095
+ No dashboards available.`
1096
+ };
1097
+ }
1098
+ const widgets = dashboardWidgets(dashboard.id);
1099
+ const metricWidgets = widgets.filter((widget2) => widget2.bindings[0]?.spec.visualization.kind === "metric");
1100
+ const lines = [
1101
+ `# ${dashboard.name}`,
1102
+ "",
1103
+ "> Contract-backed analytics dashboard overview.",
1104
+ "",
1105
+ "## Key Metrics",
1106
+ ""
1107
+ ];
1108
+ for (const widget2 of metricWidgets) {
1109
+ const binding2 = widget2.bindings[0];
1110
+ if (!binding2)
1111
+ continue;
1112
+ lines.push(`- **${widget2.name}** via \`${binding2.spec.meta.key}\``);
1113
+ }
1114
+ lines.push("");
1115
+ lines.push("## Visual Blocks");
1116
+ lines.push("");
1117
+ for (const widget2 of widgets) {
1118
+ const kinds = widget2.bindings.map((binding2) => binding2.spec.visualization.kind).join(", ");
1119
+ lines.push(`- **${widget2.name}** (${widget2.layout}) \u2192 ${kinds}`);
1120
+ }
1121
+ lines.push("");
1122
+ lines.push("## Dashboard Stats");
1123
+ lines.push("");
1124
+ lines.push("| Metric | Value |");
1125
+ lines.push("|--------|-------|");
1126
+ lines.push(`| Total Dashboards | ${mockDashboards.length} |`);
1127
+ lines.push(`| Published | ${mockDashboards.filter((item) => item.status === "PUBLISHED").length} |`);
1128
+ lines.push(`| Shared Queries | ${mockQueries.filter((query) => query.isShared).length} |`);
1129
+ return {
1130
+ mimeType: "text/markdown",
1131
+ body: lines.join(`
1132
+ `)
1133
+ };
1134
+ }
1135
+ };
1136
+ var dashboardListMarkdownRenderer = {
1137
+ target: "markdown",
1138
+ render: async (desc) => {
1139
+ if (desc.source.type !== "component" || desc.source.componentKey !== "DashboardList") {
1140
+ throw new Error("dashboardListMarkdownRenderer: not DashboardList");
1141
+ }
1142
+ const lines = [
1143
+ "# Dashboards",
1144
+ "",
1145
+ "> Browse and manage analytics dashboards",
1146
+ "",
1147
+ "| Dashboard | Widgets | Views | Status | Last Viewed |",
1148
+ "|-----------|---------|-------|--------|-------------|"
1149
+ ];
1150
+ for (const dashboard of mockDashboards) {
1151
+ const lastViewed = dashboard.lastViewedAt ? new Date(dashboard.lastViewedAt).toLocaleDateString() : "Never";
1152
+ const statusIcon = dashboard.status === "PUBLISHED" ? "\uD83D\uDFE2" : "\u26AB";
1153
+ lines.push(`| [${dashboard.name}](/dashboards/${dashboard.slug}) | ${dashboard.widgetCount} | ${dashboard.viewCount.toLocaleString()} | ${statusIcon} ${dashboard.status} | ${lastViewed} |`);
1154
+ }
1155
+ return {
1156
+ mimeType: "text/markdown",
1157
+ body: lines.join(`
1158
+ `)
1159
+ };
1160
+ }
1161
+ };
1162
+ var queryBuilderMarkdownRenderer = {
1163
+ target: "markdown",
1164
+ render: async (desc) => {
1165
+ if (desc.source.type !== "component" || desc.source.componentKey !== "QueryBuilder") {
1166
+ throw new Error("queryBuilderMarkdownRenderer: not QueryBuilder");
1167
+ }
1168
+ const lines = [
1169
+ "# Query Builder",
1170
+ "",
1171
+ "> Create and manage reusable data queries.",
1172
+ "",
1173
+ "| Query | Type | Shared | Executions |",
1174
+ "|-------|------|--------|------------|"
1175
+ ];
1176
+ for (const query of mockQueries) {
1177
+ lines.push(`| ${query.name} | ${query.type} | ${query.isShared ? "\uD83C\uDF10" : "\uD83D\uDD12"} | ${query.executionCount.toLocaleString()} |`);
1178
+ }
1179
+ lines.push("");
1180
+ lines.push("## Visualization Contracts");
1181
+ lines.push("");
1182
+ lines.push("Widgets reference `VisualizationSpec` contracts instead of rendering ad-hoc widget types.");
1183
+ return {
1184
+ mimeType: "text/markdown",
1185
+ body: lines.join(`
1186
+ `)
1187
+ };
1188
+ }
1189
+ };
666
1190
  export {
667
1191
  useAnalyticsData,
668
1192
  queryBuilderMarkdownRenderer,